Shop OBEX P1 Docs P2 Docs Learn Events
Catalina 2.9 - Page 11 — Parallax Forums

Catalina 2.9

1911131415

Comments

  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-08 20:37
    Thanks jazzed, RossH and kuroneko,

    Kuroneko, I don't think that code quite works, but that is almost certainly due to my explanation not being clear.

    The test code first sets a value in hub location 100 to a certain number. The cog then is attempting to overwrite that number with a new number passed in par.

    In pseudo code
    put a number into par
    move the number from par into a temporary variable in the cog
    move the number from the temporary cog to a location in hub ram

    There are a few things that need to all work before this works.

    I am pretty sure that this code works, but I'm less sure after doing quite a number of tests. Maybe Ross can answer that:
           unsigned long par_cogject[] = { 70,71,72 };                    // data to pass to cog - ignore if not used
           _coginit((int)par_cogject>>2, (int)mycogject_array>>2, cognumber);  // array name built from spin file name
    

    I think this means that par will point to the location in hub that contains ascii 70 in a long.

    Next part - move par to a temporary variable. I thought this was done with MOV but I'm suddenly very unsure about that after reading the Propeller manual on page 380 where it says "MOV - Set a register to a value". I thought MOV was also used to move the contents of one register to another register. Maybe it is too but the text doesn't quite read like that.

    Finally, move the contents of the temporary register to hub. wrbyte does this, and it gets me every time but it is source to destination which is sort of the opposite of MOV

    So, this code works correctly and returns 80
    DAT
                      org 0
    cogstart          mov testvalue,#80 ' for testing
    		    wrbyte testvalue,testaddress
                      jmp #cogstart
    
    testaddress       long    100              ' a hub memory location, could also use a global constant
    testvalue         long    65                                         ' ascii A
                      fit     496
    

    but this code is supposed to return the value that was passed in the par array, and instead it returns 32 or 40
    DAT
                      org 0
    cogstart         
                      mov testvalue,par
    		    wrbyte testvalue,testaddress
                      jmp #cogstart
    
    testaddress       long    100              ' a hub memory location, could also use a global constant
    testvalue         long    65                                         ' ascii A
                      fit     496
    

    and kuroneko's code returns the original value that was in the hub ram before the cog was started, which is what I think it ought to do but this is not what is intended
    DAT
                      org 0
    cogstart         
                      mov testaddress,par
    		    wrbyte testvalue,testaddress
                      jmp #cogstart
    
    testaddress       long    100              ' a hub memory location, could also use a global constant
    testvalue         long    65                                         ' ascii A
                      fit     496
    

    So I am starting to wonder if the value is not getting into par in the first place? Is there a way of testing what catalina is putting in par?
  • kuronekokuroneko Posts: 3,623
    edited 2011-03-08 20:44
    Ah, so you want the 70 to end up in hub address #100? In this case use
    [COLOR="red"]rdbyte  temp, par
    wrbyte  temp, #100[/COLOR]
    waitpeq $, #0
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-08 20:45
    That's it!

    Thanks kuroneko, that works.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-08 21:12
    Eureka!

    Now we have some hybrid C and PASM code that passes not only the data to the cog, but also the location in hub ram. This means the cog becomes totally independent. I think this is the missing link needed to created self contained .cog files that a C program can load from an sd card, then reload.

    This is hybrid code that needs to be compiled by a compiler that knows how to strip out the pasm part, convert it to a C array and then paste it back in. (all command line programs that already exist).

    I'm rather excited about this as I managed to pass some array data from one function to another using C. Maybe I am starting to understand pointers now!
    /* PASM demo for use with Catalina IDE Precompiler and Compiler */
    
    #include <stdio.h>
    
    /* PASM Code for compilation using SpinC and inclusion as a data file
    PASM Start mycogject.spin
    CON
                                                                         ' add your values here
      _clkfreq = 80_000_000                                              ' 5Mhz Crystal
      _clkmode = xtal1 + pll16x                                          ' x 16
    
    PUB Main
        coginit(1,@cogstart,0)                                           ' cog 1, cogstart, dummy value
    
    DAT
                      org 0
    cogstart
                      mov    temp2, par         ' parameters array
                      rdlong testaddress,temp2  ' hub location
                      add    temp2,#4           ' increment counter by 4 bytes = 1 long
                      rdlong temp, temp2        ' get data byte
                      wrlong temp, testaddress  ' store to hub
                      add    temp2,#4
                      add    testaddress,#4
                      rdlong temp, temp2        ' get data byte
                      wrlong temp, testaddress
                      add    temp2,#4
                      add    testaddress,#4
                      rdlong temp, temp2        ' get data byte
                      wrlong temp, testaddress
    finish
                      jmp #finish
    
    testaddress       long    0                 ' hub memory location
    temp              long    0                 ' temp variable
    temp2             long    0                 ' temp variable
                      fit     496
    PASM End
    */ 
    
    void mycogject(int cognumber, unsigned long *parameters_array)                                        // this name copied from the .spin name in the pasm section - names must match eg void mycogject matches mycogject.spin. Also first code after this must be the .h array file. Put your code after the };
    {
           /** 
            * @file mycogject_array.h
            * Created with spin.binary PASM to C Array Converter.
            * Copyright (c) 2011, John Doe
            */
           unsigned long mycogject_array[] =
           {
               0xa0bc21f0, 0x08bc1c10, 0x80fc2004, 0x08bc1e10, 
               0x083c1e0e, 0x80fc2004, 0x80fc1c04, 0x08bc1e10, 
               0x083c1e0e, 0x80fc2004, 0x80fc1c04, 0x08bc1e10, 
               0x083c1e0e, 0x5c7c000d, 0x00000000, 0x00000000, 
               0x00000000
           };
           _coginit((int)parameters_array>>2, (int)mycogject_array>>2, cognumber);  // array name built from spin file name
    }
    
    void clearscreen()                                                   // white text on dark blue background
    {
           int i;
           for (i=0;i<40;i++)
           {
                   t_setpos(0,0,i);                                      // move cursor to next line
                   t_color(0,0x08FC);                                    // RRGGBBxx eg dark blue background 00001000 white text 11111100
           }
    }
    
    void sleep(int milliseconds)                                         // sleep function
    {
           _waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
    }
    
    char peek(int address)                                               // function implementation of peek
    {
           return *((char *)address);
    }
    
    void poke(int address, char value)                                   // function implementation of poke
    {
           *((char *)address) = value;
    }
    
    void main ()
    {
           char c;
           int i;
           unsigned long hubaddress=100;
           unsigned long parameters_array[20];                            // for passing parameters to the cog
           parameters_array[0]=hubaddress;					               // hub address
           parameters_array[1]=80;						                  // some data to pass
           parameters_array[2]=81;
           parameters_array[3]=82;
           clearscreen();
           printf("Clock speed %u \n",_clockfreq());                     // see page 28 of the propeller manual for other useful commands
           printf("Catalina running in cog number %i \n",_cogid());      // integer
           i=hubaddress;                                               // location to peek and poke
           poke(i,66);                                                   // poke a B, the cog should change this
           printf("load cogject into cog 7\n");
           mycogject(7,parameters_array);                                                 // run in cog 7
           c=peek(i);                                                    // peek byte at this location
           printf("Peek value at %i = character %c Ascii value %d \n",i,c,c);
           i=i+4;
           c=peek(i);
           printf("Peek value at %i = character %c Ascii value %d \n",i,c,c);
           i=i+4;
           c=peek(i);
           printf("Peek value at %i = character %c Ascii value %d \n",i,c,c);
           while (1); // Prop reboots on exit from main()
    }
    
  • RossHRossH Posts: 5,548
    edited 2011-03-09 01:35
    Dr_Acula wrote: »
    Eureka!

    Now we have some hybrid C and PASM code that passes not only the data to the cog, but also the location in hub ram. This means the cog becomes totally independent. I think this is the missing link needed to created self contained .cog files that a C program can load from an sd card, then reload.

    Congrats, Dr_A. Easier integration between C and PASM is indeed one of the "missing links"!

    I'll have a play with your tools when I get some time (i.e. once I get release 3.0 safely out the door).

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-09 02:34
    A quick question - can Catalina run on Linux and/or a Mac?

    If so, how does the directory structure work on those other computers (ie the equivalent of installing in c:\program files\catalina)
  • RossHRossH Posts: 5,548
    edited 2011-03-09 13:53
    Dr_Acula wrote: »
    A quick question - can Catalina run on Linux and/or a Mac?

    If so, how does the directory structure work on those other computers (ie the equivalent of installing in c:\program files\catalina)

    Hi Dr_A,

    All Catalina releases are tested and 100% functional on both Windows and Linux. I don't have a Mac, so I don't test on that platform but some people have reported successfully building Catalina for a Mac from source.

    On Linux everything is much the same except the default Catalina directory is /usr/local/lib/catalina

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-09 16:47
    Thanks Ross - that is very helpful to know when thinking about an IDE.

    I'm working on porting a spin object to C and I'm currently working on the standard fullduplexserial object. I know this can be done in other ways, but it is a demonstration to show loading and unloading cog code.

    This is the spin code
    VAR
    
      long  cog                     'cog flag/id
    
      long  rx_head                 '9 contiguous longs
      long  rx_tail
      long  tx_head
      long  tx_tail
      long  rx_pin
      long  tx_pin
      long  rxtx_mode
      long  bit_ticks
      long  buffer_ptr
                         
      byte  rx_buffer[16]           'transmit and receive buffers
      byte  tx_buffer[16]  
    ...
    PUB start(rxpin, txpin, mode, baudrate) : okay
    
    '' Start serial driver - starts a cog
    '' returns false if no cog available
    ''
    '' mode bit 0 = invert rx
    '' mode bit 1 = invert tx
    '' mode bit 2 = open-drain/source tx
    '' mode bit 3 = ignore tx echo on rx
    
      stop
      longfill(@rx_head, 0, 4)
      longmove(@rx_pin, @rxpin, 3)
      bit_ticks := clkfreq / baudrate
      buffer_ptr := @rx_buffer
      okay := cog := cognew(@entry, @rx_head) + 1
    

    and in C I have global variables (I think that is the right way to put the variable in hub?)
      unsigned long  rx_head;                 // 9 contiguous longs
      unsigned long  rx_tail;
      unsigned long  tx_head;
      unsigned long  tx_tail;
      unsigned long  rx_pin;
      unsigned long  tx_pin;
      unsigned long  rxtx_mode;
      unsigned long  bit_ticks;
      unsigned long  buffer_ptr;		
      char  rx_buffer[16];           // transmit and receive buffers
      char  tx_buffer[16];
    

    and for the start function
    unsigned long start(unsigned long rxpin,unsigned long txpin,unsigned long mode, unsigned long baudrate)
    {
    	unsigned long okay;
    	rx_head = 0;						// longfill(@rx_head, 0, 4)
    	tx_head = 0;
    	rx_tail = 0;
    	tx_tail = 0;
           rx_pin = rxpin;					//   longmove(@rx_pin, @rxpin, 3)
           tx_pin = txpin;
           rxtx_mode = mode;	
    	bit_ticks = _clockfreq() / baudrate;   		//   bit_ticks := clkfreq / baudrate
    	buffer_ptr = rx_buffer[0];				//   buffer_ptr := @rx_buffer
    	printf("Buffer is at %u \n",buffer_ptr);
    	return okay;
    }
    

    However, that last line is printing out zero for the buffer_ptr location. I am trying to translate this line:
     buffer_ptr := @rx_buffer
    

    Do I need to be using integers instead of unsigned longs to determine the location of an array?
  • kuronekokuroneko Posts: 3,623
    edited 2011-03-09 16:51
    The C equivalent to SPIN's @ is & so you want something like
    buffer_ptr = (unsigned long)&rx_buffer[0]
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-09 17:05
    Thanks! That works. I'm getting a figure now of 70944 which sounds plausible given that this is a program running in external memory.

    Next little thing is to get a number under 32768, ie a location in hub. I think that has something to do with where the variable is declared.
  • RossHRossH Posts: 5,548
    edited 2011-03-09 17:06
    Dr_Acula wrote: »
    ... and in C I have global variables (I think that is the right way to put the variable in hub?)

    No. I really must write a brief technical note about this.

    Global variables in Catalina (more correctly, any variables with file scope) will be in Hub RAM only in the LMM (-x0) and XMM SMALL (-x3) memory models. In the XMM LARGE (-x5) memory model, they will be in XMM RAM.

    To force a variable to be in Hub RAM in all memory models, it must be declared as a local. If you want to share it around, then declare it as a local in the 'main' function, and pass it's address to the other functions that need it. If you don't want to pass it as a parameter, you can instead store the local address in a global variable. For example (note I am at work and can't run this, but it should work!):
    #include <stdio.h>
    
    char *my_pointer;
    
    void my_function() {
       printf ("%s\n", my_pointer);
    }
    
    main() {
       char my_data[] = {"Hello, World!\n"};
    
       my_pointer = my_data;
    
       my_function();
    }
    
    Ross.

    P.S. Kuroneko is correct - but I think you can also just write
    buffer_ptr = (unsigned long) rx_buffer;
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-09 17:33
    you can instead store the local address in a global variable.

    Yes I think that is the answer. The aim is to try to replicate spin code without too many changes. So if a variable is declared in a VAR section in spin code, then it is global for that object and C should be too, otherwise too much code needs to be changed.

    I have tested this as you suggest:
    1) put unsigned long buffer_ptr; at the beginning of the program under stdio
    2) declare the circular buffers in Main and get the pointer to them
           char  rx_buffer[16];           // transmit and receive buffers
           char  tx_buffer[16];
           buffer_ptr = (unsigned long)&rx_buffer[0];		//   buffer_ptr := @rx_buffer
    

    And then in any function
    	printf("Buffer is at %u \n",buffer_ptr);
    

    and I get a value now of 27132.

    This is great - it means I can now pass that to the cog.
  • David BetzDavid Betz Posts: 14,519
    edited 2011-03-09 17:45
    RossH wrote: »
    No. I really must write a brief technical note about this.

    Global variables in Catalina (more correctly, any variables with file scope) will be in Hub RAM only in the LMM (-x0) and XMM SMALL (-x3) memory models. In the XMM LARGE (-x5) memory model, they will be in XMM RAM.

    To force a variable to be in Hub RAM in all memory models, it must be declared as a local. If you want to share it around, then declare it as a local in the 'main' function, and pass it's address to the other functions that need it. If you don't want to pass it as a parameter, you can instead store the local address in a global variable. For example (note I am at work and can't run this, but it should work!):

    The GNU linker allows a syntax like this:
    #define HUB __attribute__ ((section(".hub")))
    
    HUB char my_buffer[128];
    
    

    This places the variable my_buffer in the ".hub" section which my linker script then places in hub memory. This works in ZOG to declare global variables in hub memory. Maybe you could adopt a similar scheme with Catalina.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-09 18:08
    That sounds interesting David.

    Yes, it turned out this is not so easy. This object declares its variables in a VAR section and it consists of three parts that it passes to the cog:
      long  rx_head                 '9 contiguous longs
      long  rx_tail
      long  tx_head
      long  tx_tail
      long  rx_pin
      long  tx_pin
      long  rxtx_mode
      long  bit_ticks
      long  buffer_ptr
                         
      byte  rx_buffer[16]           'transmit and receive buffers
      byte  tx_buffer[16]  
    

    Part 1 - a list of 8 variables, in contiguous order
    Part 2 - a pointer to the beginning of the circular buffers
    Part 3 - the circular buffers.

    When the cog is started there is a reference to the start of part 1 viz okay := cog := cognew(@entry, @rx_head) + 1

    In spin the compiler assumes the variables are global, so things like setting these values are done in functions. eg bit_ticks := clkfreq / baudrate is in the Start PUB.

    But - in C, if these are declared in the Main routine, they are not global, so it gets more complicated as you have to pass a reference to the list.

    And I just found something else by printing out the pointers to these variables - the values are decreasing as you go down the list, 27140, 27136, 27132. However, in Spin, the values increase, and this is important because the cog expects these values to be in the right order.

    So the order of all variables and arrays is going to have to be reversed!

    I will need to think about this some more. It seems that a lot of spin code is treating contiguous variables as arrays, exploiting the fact that the compiler lists them in ascending order. If we can't use global variables, and the list is in the wrong order, I am wondering if it might not be easier just to explicitly create an array of variables that encompasses everything. Then you can pass that array to functions in C with just one parameter.

    For example, the fullduplexserial object is 9 longs and 32 longs for the buffer. Make that a 41 long array? Ideally, setting all the values should happen in a function rather than in the main, otherwise the main is going to fill up with a lot of code. The concept of 'objects' kind of goes out the window when the main() needs so much code added. Or maybe the idea would be to only declare hub variables in the main (as arrays), then do things like splitting them up into individual variables and swapping the order and setting values in individual functions?

    I guess that is allowed, since spin does tricky things like
    longfill(@rx_head, 0, 4)
    which is actually setting four variables to zero, even those variables are not in an array.

    So - instead of 9 longs and two arrays, we have one array -
    unsigned long serial[40]
    and instead of variables like rx_head, we reference that with
    serial[40]
    and rx_tail is serial[39]
    which takes into account the order reversal. Then it is just a matter of good commenting to describe how and why the spin code has been changed this way.

    This way we can pass then entire block to any functions so that all the variables in this code become array values
    PUB rxcheck : rxbyte
    
    '' Check if byte received (never waits)
    '' returns -1 if no byte received, $00..$FF if byte
    
      rxbyte--
      if rx_tail <> rx_head
        rxbyte := rx_buffer[rx_tail]
        rx_tail := (rx_tail + 1) & $F
    

    if (serial[39] != serial[40]) {
  • RossHRossH Posts: 5,548
    edited 2011-03-09 19:16
    David Betz wrote: »
    The GNU linker allows a syntax like this:
    #define HUB __attribute__ ((section(".hub")))
    
    HUB char my_buffer[128];
    
    
    This places the variable my_buffer in the ".hub" section which my linker script then places in hub memory. This works in ZOG to declare global variables in hub memory. Maybe you could adopt a similar scheme with Catalina.

    I may look at doing something like this for a subsequent release, although I'm always horrified by such non-standard constructs. Others have suggested using the "volatile" or "static" keywords for this type of thing, but while this makes the syntax standard, it is non-standard semantics. In a way that's even worse - at least with the GNU approach you know your code will be non-portable.

    However, while I agree it would simplify life for the programmer, in reality it is not a big problem since the main reason for needing hub variables is to interact with other languages - and this is always going to be non-trivial.

    Ross.
  • RossHRossH Posts: 5,548
    edited 2011-03-09 19:28
    Dr_Acula wrote: »
    ...
    But - in C, if these are declared in the Main routine, they are not global, so it gets more complicated as you have to pass a reference to the list.

    And I just found something else by printing out the pointers to these variables - the values are decreasing as you go down the list, 27140, 27136, 27132. However, in Spin, the values increase, and this is important because the cog expects these values to be in the right order.

    So the order of all variables and arrays is going to have to be reversed!

    You should never depend on a specific memory layout of unrelated variables in any language. In C, some variables may end up in registers and not be present in memory at all!

    if you want to force a specific memory layout in C you are supposed to declare a structure. You can then pass a pointer to that instead. So you would say something like:
    typedef struct {
      long  rx_head;
      long  rx_tail;
      long  tx_head;
      long  tx_tail;
      long  rx_pin;
      long  tx_pin;
      long  rxtx_mode;
      long  bit_ticks;
      long  buffer_ptr;
                         
      char  rx_buffer[16];
      char  tx_buffer[16];
    } struct_t;
    
    void my_function (struct_t *my_pointer) {
       /* do something to struct here */
    }
    
    void main() {
    
       struct_t my_struct;
    
       my_function(&my_struct);
    }
    
  • potatoheadpotatohead Posts: 10,261
    edited 2011-03-09 19:34
    I vote for known non portable. Different semantics for valid syntax are just hard for people unaware. On the other hand, seeing something stick out is hard too, but there it is, easy to see, known to need work.
  • Heater.Heater. Posts: 21,230
    edited 2011-03-10 00:35
    I do emphasize what Ross. Do not rely on data declarations in C ending up in memory in the same order you wrote them in the code.

    I just had a problem with Zog like this. I had two arrays defined consecutively, mirroring what I have in a Spin version of the same idea. Turns out that when I increased the optimization level the compiler decided to put these two arrays in memory in reverse order and everything failed.

    Use structures to enforce ordering.

    Also I'm with potatohead. Non portable is better. Having different semantics for the same syntax is horrible. Besides we normally use funky constructs like this when building device drivers, and platform specific stuff so portability is not such a big issue.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-10 02:26
    C really is full of clever tricks. Structures look perfect for this application.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-12 18:36
    A highly technical discussion here for the Catalina fans (and also thinking out loud).

    Ok, what I am doing is rewriting a Spin object for use in C. There is a 'tiny' mode for C that fits easily into the internal memory, but my programs tend to be larger and I ran out of space ages ago. So - given a propeller with external memory (and we have quite a smorgasbord now!), I think it could be very useful to have reloadable objects.

    In general terms, hub ram is more valuable than external ram. (Hub ram is faster and it can be accessed by cogs).

    So, looking at a typical object, we have two types of variables that are passed to cogs - setup values, and permanent values. For example, in a serial driver, a setup value might be the baud rate, and a permanent value might be the circular buffer. For a video object, a setup value might be the screen size, and a permanent value might be the video buffer.

    Permanent values might number from zero, right up to an array that fills most of the hub ram.
    Setup values tend to be less in number, maybe 10 or so.

    Setup values waste space in hub ram. This becomes more relevant when multiple copies are loaded or reloaded.

    Because most Spin code objects were not written for external memory there never has been a need to differentiate between these types of variables. Indeed, I'm not even sure Spin as a language would be able to explicitly place one variable in hub and the next in external memory. (One would have to use a custom PUB to do this, and these exist, both in Spin and PASM).

    But C is able to determine where variables are stored (hub variables in the Main, external variables defined at the beginning of the program).

    So this leads to a new generic Start for objects. Instead of passing one list of values to the PAR, we need to pass two. Setup values and permanent values.

    To make these neater, one could put one list after the other, but this is not necessary. Indeed, in order to save rewriting PASM code, it probably is easier to leave the order in whatever order the pasm code happens to be.

    Let's take a look at the SimpleSerial object, with new comments added as to the nature of a variable (we can determine if a variable is setup or permanent by doing a search for that variable name)
      long  rx_head                 ' permanent, and pointer to start of the variable block
      long  rx_tail                     ' permanent, used in rxcheck
      long  tx_head                 ' permanent, used in tx
      long  tx_tail                     ' permanent, used in tx
      long  rx_pin                    ' setup
      long  tx_pin                    ' setup
      long  rxtx_mode           ' permanent, used once in tx when checking for echo
      long  bit_ticks               ' setup
      long  buffer_ptr             ' setup (I think?)
                         
      byte  rx_buffer[16]           'permanent
      byte  tx_buffer[16]           ' permanent
    

    So this order stays the same as this is the pasm order. What we need to do in our C program is pass these variables in the correct way.

    Thinking about external memory management means looking at a C program in a slightly different way. For instance, where do you define the setup value "rx_pin"

    1) As a constant (probably not the best option when one might be loading multiple copies of an object)
    2) As a variable at the beginning of the program, underneath the #includes. This stores the value in external memory.
    3) In the Main program (possibly where one usually might store this, but not the best place in this instance as it wastes a hub memory location
    4) In a local function. The value will be disposed of when the function ends. On the other hand, it might be harder to find the value in amongst the code.
    5) As a number in the function call eg serialstart(31,30)
    6) As a constant read from a list where many constants are defined, eg at the beginning just under the #includes, serialporttx1 = 31, serialporttx2 = 29, serialporttx3=27

    Option 6 may be the closest to existing spin, but I'm still thinking about this.

    For 'permanent' values, one might have one list for an object, but one might also have multiple lists. I took a look at structures. These do look a good way of passing values in a group, though one minor disadvantage might be the dot notation, which in C refers to variables and in Spin refers to objects.

    So - we define the setup values as either constants or variables just under the #includes
    We define a group of hub memory locations in the Main program, possibly with multiple instances when using multiple cog objects

    Then we pass these to the start function for the object. The start function may or may not rearrange the order of variables.

    What I am thinking is whether you split the start process into two parts. One looks very similar to the spin start for that object eg
    PUB start(rxpin, txpin, mode, baudrate)
    and the other is a generic loader.

    So in Spin pseudocode, the PUB above becomes
    PUB start(rxpin, txpin, mode, baudrate,serial1permenentarray)

    where serial1permenentarray contains things like the buffer, and the buffer location.

    this Start then goes and rearranges the order splices these setup and permanent values together into a new array, as the order is going to be different. It then calls the generic loader with all the values in order for use by PAR. This array is disposed when the Start function ends.

    There may also be a need to pass 'setup' values as well. I need to check the syntax of a few objects and see how they are doing things.

    The serial1permanentarray is also passed to other methods eg
    PUB rxcheck : rxbyte
    
    '' Check if byte received (never waits)
    '' returns -1 if no byte received, $00..$FF if byte
    
      rxbyte--
      if rx_tail <> rx_head
        rxbyte := rx_buffer[rx_tail]
        rx_tail := (rx_tail + 1) & $F
    

    changes to
    PUB rxcheck(arraypointer)

    (or a structure) and from arraypointer this function can extract rx_tail and all the other variables it needs.

    All objects need to have something passed to them, which is going to make the code look a bit different to spin, as in spin, many objects are "void" eg
    PUB rxflush
    but they are not really void, as there are variables which are global to the object (ie those declared in the VAR section).

    So - many things need to change in the code to create reloadable objects. These changes will also need to be made in Spin once BigSpin is being used more. One needs to think a lot more about what code is using what memory.

    This is some C code I am using to test this out
    /* test passing unsigned longs to functions */
    
    #include <stdio.h>
    
    unsigned long setup_value = 30;				// setup values stored in external ram
    
    void cogloader(unsigned long pararray[])
       {
         printf("%d \n",pararray[0]);
         printf("%d \n",pararray[1]);
         printf("%d \n",pararray[2]);
       }
    
    void serial_start(unsigned long s, unsigned long hub_serial[])		// pass setup value(s) and permanent hub values here
       {
    	unsigned long cogpar[30]; 							// parameter array for the cog
    	cogpar[0] = hub_serial[4];							// rearrange order
    	cogpar[1] = s;								// splice in a setup value
    	cogpar[2] = hub_serial[3];
    	cogloader(cogpar);								// pass to generic cog loader
       }
      
    void main()
       {
        	unsigned long hub_serial[10];						// create the array in hub ram
        	hub_serial[3] = 7;							// put in an initial value
    	hub_serial[4] = 8;
    	serial_start(setup_value,hub_serial);							// pass array and other values
           while (1);                                                    // Prop reboots on exit from main()!
       }
    

    as an aside, the Catalina IDE makes it so much easier when you can test fragments like this with Shift F10 for the TinyC compiler, prior to using F10 for catalina. Instant compiling makes debugging *much* easier. In this respect Catalina is so far ahead of Spin. The downside is that it is not going to be easy to have 'objects' and you can't drop in blocks of code with copy/paste. Hopefully the changes are not too many though. Just a matter of working out which Spin code is permanent and which is temporary and where things should go.

    At the moment it looks like an spin object will be split into several parts
    1) the pasm code (which can be separately compiled to a .cog binary object, or converted to an array for inclusion in C)
    2) The methods, each of which will be a function in C
    3) The setup values, which are placed at the beginning of the C program so they are in hub
    4) Declarations of the functions if one wants the main at the top of the program
    5) The list of variables and arrays, to be copied to the start of the 'main'

    Hmm - not quite as neat as the concept of Spin objects. Then again, reloadable spin objects for external memory would also require a lot of custom copy/paste as well.
  • RossHRossH Posts: 5,548
    edited 2011-03-14 18:45
    This is for Jazzed & David Betz ...

    I have a pre-release of Catalina 3.0 almost ready to go. It completes the SPI FLASH functionality for the C3, plus a few other things.

    Here is a quick summary:
    • The new caching SPI driver has been integrated. Cache sizes of 1k,2k,4k or 8k are supported.
    • Two new memory modes have been added specically for executing from SPI FLASH (which requires the new caching SPI driver):
    -x3 puts all read-only segments in SPI FLASH and all read/write segments in SPI RAM (the stack is still in Hub).
    -x4 puts the code segment in SPI FLASH and all data segments in Hub (the SPI RAM is not used).
    These memory models are fully supported by the Catalyst program loader and the Payload serial loader. There are also new related command-line options (-P and -R) that can be used to relocate the read/write and read/only segments.
    • There is a new flash_boot utility program that can be programmed into EEPROM to boot a program already loaded into FLASH
    • There are fixes for bugs in setjmp/longjmp on some platforms.
    • All the HMI plugins are now ANSI compliant (There are "legacy" options that can be used for programs that depend on the current HMI plugin behaviour, but it is recommended to modify all programs to expect ANSI behaviour for things like backspace, newline and carriage return processing).
    I've only tested the Windows versions of most things so far, so I need to know if either of you are using Linux.

    Ross.
  • jazzedjazzed Posts: 11,803
    edited 2011-03-14 18:51
    Excellent news Ross. I'm using Windows 7 mostly these days. I can test on Linux if you need it.
    Thanks.
  • David BetzDavid Betz Posts: 14,519
    edited 2011-03-14 18:59
    RossH wrote: »
    This is for Jazzed & David Betz ...

    I have a pre-release of Catalina 3.0 almost ready to go. It completes the SPI FLASH functionality for the C3, plus a few other things.

    That sounds great! I'll look forward to trying it out.
  • RossHRossH Posts: 5,548
    edited 2011-03-14 20:06
    David, Jazzed ...

    I'll package up something for release - probably tomorrow or the day after. If I can make it small enough to be a patch release that can be applied to 2.9 then I'll email it to you - otherwise I'll post it on sourceforge (with suitable disclaimers! - there's still a heap of testing on other platforms left to do).

    The Linux testing is not onerous, it is just tedious to do it all over again. It's mainly the various scripts and makefiles that have to be tested. But you're welcome to do some if you want!

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-16 03:49
    Hi Ross,

    I'm testing this little code fragment
    void main ()
    {
    	unsigned long serial_parameters[40];				// reserve hub space for buffer, head tail pointers
    	printf("serial_parameters array is at %u \n",(unsigned long)&serial_parameters[0]);
    	printf("end of serial_parameters array is at %u \n",(unsigned long)&serial_parameters[40]);
    	serial_start(31,30,0,1200,serial_parameters);						// start serial cog pins 31,30, mode 0, 1200 baud
    	while (1); // Prop reboots on exit from main()
    }
    

    and now I am very confused as the order seems to now be swapped around. The start of the array is at 27068 and the finish at 27228. This is now the same order as Spin, which will actually make things easier, but I am sure I had code earlier that was in reverse. It probably does not matter, but my only concern is that something different in the code has the effect of swapping the order. Do you have any knowledge of the internals of how catalina assigns array and variable space?
  • RossHRossH Posts: 5,548
    edited 2011-03-16 04:02
    Dr_Acula wrote: »
    Hi Ross,

    I'm testing this little code fragment
    void main ()
    {
        unsigned long serial_parameters[40];                // reserve hub space for buffer, head tail pointers
        printf("serial_parameters array is at %u \n",(unsigned long)&serial_parameters[0]);
        printf("end of serial_parameters array is at %u \n",(unsigned long)&serial_parameters[40]);
        serial_start(31,30,0,1200,serial_parameters);                        // start serial cog pins 31,30, mode 0, 1200 baud
        while (1); // Prop reboots on exit from main()
    }
    
    and now I am very confused as the order seems to now be swapped around. The start of the array is at 27068 and the finish at 27228. This is now the same order as Spin, which will actually make things easier, but I am sure I had code earlier that was in reverse. It probably does not matter, but my only concern is that something different in the code has the effect of swapping the order. Do you have any knowledge of the internals of how catalina assigns array and variable space?

    Hi Dr_A,

    Arrays are always allocated this way. Your previous case was a bunch of consecutive long variables, but they weren't part of any array or structure. In that case, Catalina (or any C compiler) is free to lay them out in memory any way it likes. Catalina will tend to reverse them because it allocates them consecutively in the local stack frame, and the stack grows downwards in memory.

    Ross.
  • Heater.Heater. Posts: 21,230
    edited 2011-03-16 04:54
    Dr_A,

    Yep, see my post #319 here. You can't rely on a C compiler placing memory for different declarations in the same order as you write them. If you want to do that you have to wrap all the declarations up into a structure.

    By the way you should be aware that
    &serial_parameters[0]
    

    By can be written simply as:
    serial_parameters
    

    The name of an array is a pointer to the first element of the array. Makes the source look a bit neater.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-16 05:15
    Thanks guys, that makes sense. Though I might stick with the & pointing to the array element 0, just to remind me this is an array, not a variable. I hope you don't mind one of my other personal C quirky styles as well, where I put { on a line by itself.

    I didn't quite understand structures - particularly how they are decoded in the receiving function. But I think it is going to be simpler to pass one array and then to make sure it is well commented as to which array element corresponds with which spin variable.

    Anyway - a eureka moment. We have a C cog object being loaded and it transmits data out of the serial port.

    This is a big milestone. The IDE is taking the pasm part (which exists as a comment block in C), compiling it, pasting it back into C as an array, and the C part is interacting with this. The pasm part is self contained, so it could just as easily have been loaded off an SD card.

    The spin code is the standard serial object (I'll post this as there is a question at the bottom of this)
    ''***************************************
    ''*  Full-Duplex Serial Driver v1.1     *
    ''*  Author: Chip Gracey                *
    ''*  Copyright (c) 2006 Parallax, Inc.  *
    ''*  See end of file for terms of use.  *
    ''***************************************
    
    
    VAR
    
      long  cog                     'cog flag/id
    
      long  rx_head                 '9 contiguous longs
      long  rx_tail
      long  tx_head
      long  tx_tail
      long  rx_pin
      long  tx_pin
      long  rxtx_mode
      long  bit_ticks
      long  buffer_ptr
                         
      byte  rx_buffer[16]           'transmit and receive buffers
      byte  tx_buffer[16]  
    
    
    PUB start(rxpin, txpin, mode, baudrate) : okay
    
    '' Start serial driver - starts a cog
    '' returns false if no cog available
    ''
    '' mode bit 0 = invert rx
    '' mode bit 1 = invert tx
    '' mode bit 2 = open-drain/source tx
    '' mode bit 3 = ignore tx echo on rx
    
      stop
      longfill(@rx_head, 0, 4)
      longmove(@rx_pin, @rxpin, 3)
      bit_ticks := clkfreq / baudrate
      buffer_ptr := @rx_buffer
      okay := cog := cognew(@entry, @rx_head) + 1
    
    
    PUB stop
    
    '' Stop serial driver - frees a cog
    
      if cog
        cogstop(cog~ - 1)
      longfill(@rx_head, 0, 9)
    
    
    PUB rxflush
    
    '' Flush receive buffer
    
      repeat while rxcheck => 0
      
        
    PUB rxcheck : rxbyte
    
    '' Check if byte received (never waits)
    '' returns -1 if no byte received, $00..$FF if byte
    
      rxbyte--
      if rx_tail <> rx_head
        rxbyte := rx_buffer[rx_tail]
        rx_tail := (rx_tail + 1) & $F
    
    
    PUB rxtime(ms) : rxbyte | t
    
    '' Wait ms milliseconds for a byte to be received
    '' returns -1 if no byte received, $00..$FF if byte
    
      t := cnt
      repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / (clkfreq / 1000) > ms
      
    
    PUB rx : rxbyte
    
    '' Receive byte (may wait for byte)
    '' returns $00..$FF
    
      repeat while (rxbyte := rxcheck) < 0
    
    
    PUB tx(txbyte)
    
    '' Send byte (may wait for room in buffer)
    
      repeat until (tx_tail <> (tx_head + 1) & $F)
      tx_buffer[tx_head] := txbyte
      tx_head := (tx_head + 1) & $F
    
      if rxtx_mode & %1000
        rx
    
    
    PUB str(stringptr)
    
    '' Send string                    
    
      repeat strsize(stringptr)
        tx(byte[stringptr++])
        
    
    PUB dec(value) | i
    
    '' Print a decimal number
    
      if value < 0
        -value
        tx("-")
    
      i := 1_000_000_000
    
      repeat 10
        if value => i
          tx(value / i + "0")
          value //= i
          result~~
        elseif result or i == 1
          tx("0")
        i /= 10
    
    
    PUB hex(value, digits)
    
    '' Print a hexadecimal number
    
      value <<= (8 - digits) << 2
      repeat digits
        tx(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
    
    
    PUB bin(value, digits)
    
    '' Print a binary number
    
      value <<= 32 - digits
      repeat digits
        tx((value <-= 1) & 1 + "0")
    
    
    DAT
    
    '***********************************
    '* Assembly language serial driver *
    '***********************************
    
                            org
    '
    '
    ' Entry
    '
    entry                   mov     t1,par                'get structure address
                            add     t1,#4 << 2            'skip past heads and tails
    
                            rdlong  t2,t1                 'get rx_pin
                            mov     rxmask,#1
                            shl     rxmask,t2
    
                            add     t1,#4                 'get tx_pin
                            rdlong  t2,t1
                            mov     txmask,#1
                            shl     txmask,t2
    
                            add     t1,#4                 'get rxtx_mode
                            rdlong  rxtxmode,t1
    
                            add     t1,#4                 'get bit_ticks
                            rdlong  bitticks,t1
    
                            add     t1,#4                 'get buffer_ptr
                            rdlong  rxbuff,t1
                            mov     txbuff,rxbuff
                            add     txbuff,#16
    
                            test    rxtxmode,#%100  wz    'init tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_ne_c       or      outa,txmask
            if_z            or      dira,txmask
    
                            mov     txcode,#transmit      'initialize ping-pong multitasking
    '
    '
    ' Receive
    '
    receive                 jmpret  rxcode,txcode         'run a chunk of transmit code, then return
    
                            test    rxtxmode,#%001  wz    'wait for start bit on rx pin
                            test    rxmask,ina      wc
            if_z_eq_c       jmp     #receive
    
                            mov     rxbits,#9             'ready to receive byte
                            mov     rxcnt,bitticks
                            shr     rxcnt,#1
                            add     rxcnt,cnt                          
    
    :bit                    add     rxcnt,bitticks        'ready next bit period
    
    :wait                   jmpret  rxcode,txcode         'run a chuck of transmit code, then return
    
                            mov     t1,rxcnt              'check if bit receive period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            test    rxmask,ina      wc    'receive bit on rx pin
                            rcr     rxdata,#1
                            djnz    rxbits,#:bit
    
                            shr     rxdata,#32-9          'justify and trim received byte
                            and     rxdata,#$FF
                            test    rxtxmode,#%001  wz    'if rx inverted, invert byte
            if_nz           xor     rxdata,#$FF
    
                            rdlong  t2,par                'save received byte and inc head
                            add     t2,rxbuff
                            wrbyte  rxdata,t2
                            sub     t2,rxbuff
                            add     t2,#1
                            and     t2,#$0F
                            wrlong  t2,par
    
                            jmp     #receive              'byte done, receive next byte
    '
    '
    ' Transmit
    '
    transmit                jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,par                'check for head <> tail
                            add     t1,#2 << 2
                            rdlong  t2,t1
                            add     t1,#1 << 2
                            rdlong  t3,t1
                            cmp     t2,t3           wz
            if_z            jmp     #transmit
    
                            add     t3,txbuff             'get byte and inc tail
                            rdbyte  txdata,t3
                            sub     t3,txbuff
                            add     t3,#1
                            and     t3,#$0F
                            wrlong  t3,t1
    
                            or      txdata,#$100          'ready byte to transmit
                            shl     txdata,#2
                            or      txdata,#1
                            mov     txbits,#11
                            mov     txcnt,cnt
    
    :bit                    test    rxtxmode,#%100  wz    'output bit on tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_and_c      xor     txdata,#1
                            shr     txdata,#1       wc
            if_z            muxc    outa,txmask        
            if_nz           muxnc   dira,txmask
                            add     txcnt,bitticks        'ready next cnt
    
    :wait                   jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,txcnt              'check if bit transmit period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            djnz    txbits,#:bit          'another bit to transmit?
    
                            jmp     #transmit             'byte done, transmit next byte
    '
    '
    ' Uninitialized data
    '
    t1                      res     1
    t2                      res     1
    t3                      res     1
    
    rxtxmode                res     1
    bitticks                res     1
    
    rxmask                  res     1
    rxbuff                  res     1
    rxdata                  res     1
    rxbits                  res     1
    rxcnt                   res     1
    rxcode                  res     1
    
    txmask                  res     1
    txbuff                  res     1
    txdata                  res     1
    txbits                  res     1
    txcnt                   res     1
    txcode                  res     1
    
    {{
    
    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
    &#9474;                                                   TERMS OF USE: MIT License                                                  &#9474;                                                            
    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
    &#9474;Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    &#9474; 
    &#9474;files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    &#9474;
    &#9474;modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software&#9474;
    &#9474;is furnished to do so, subject to the following conditions:                                                                   &#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.&#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          &#9474;
    &#9474;WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         &#9474;
    &#9474;COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   &#9474;
    &#9474;ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         &#9474;
    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
    }}
    

    and this is the complete C program
    /* PASM demo for use with Catalina IDE Precompiler and Compiler */
    
    #include <stdio.h>
    
    
    /* PASM Code for compilation using SpinC and inclusion as a data file
    PASM Start mycogject.spin
    CON
                                                                         ' add your values here
      _clkfreq = 80_000_000                                              ' 5Mhz Crystal
      _clkmode = xtal1 + pll16x                                          ' x 16
                                                                         ' Start of C hub constants
                                                                         ' End of C constants
    PUB Main
        coginit(1,@entry,0)                                           ' cog 1, cogstart, dummy value
    
    DAT
    
    '***********************************
    '* Assembly language serial driver *
    '***********************************
    
                            org
    '
    '
    ' Entry
    '
    entry                   mov     t1,par                'get structure address
                            add     t1,#4 << 2            'skip past heads and tails
    
                            rdlong  t2,t1                 'get rx_pin
                            mov     rxmask,#1
                            shl     rxmask,t2
    
                            add     t1,#4                 'get tx_pin
                            rdlong  t2,t1
                            mov     txmask,#1
                            shl     txmask,t2
    
                            add     t1,#4                 'get rxtx_mode
                            rdlong  rxtxmode,t1
    
                            add     t1,#4                 'get bit_ticks
                            rdlong  bitticks,t1
    
                            add     t1,#4                 'get buffer_ptr
                            rdlong  rxbuff,t1
                            mov     txbuff,rxbuff
                            add     txbuff,#16
    
                            test    rxtxmode,#%100  wz    'init tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_ne_c       or      outa,txmask
            if_z            or      dira,txmask
    
                            mov     txcode,#transmit      'initialize ping-pong multitasking
    '
    '
    ' Receive
    '
    receive                 jmpret  rxcode,txcode         'run a chunk of transmit code, then return
    
                            test    rxtxmode,#%001  wz    'wait for start bit on rx pin
                            test    rxmask,ina      wc
            if_z_eq_c       jmp     #receive
    
                            mov     rxbits,#9             'ready to receive byte
                            mov     rxcnt,bitticks
                            shr     rxcnt,#1
                            add     rxcnt,cnt                          
    
    :bit                    add     rxcnt,bitticks        'ready next bit period
    
    :wait                   jmpret  rxcode,txcode         'run a chuck of transmit code, then return
    
                            mov     t1,rxcnt              'check if bit receive period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            test    rxmask,ina      wc    'receive bit on rx pin
                            rcr     rxdata,#1
                            djnz    rxbits,#:bit
    
                            shr     rxdata,#32-9          'justify and trim received byte
                            and     rxdata,#$FF
                            test    rxtxmode,#%001  wz    'if rx inverted, invert byte
            if_nz           xor     rxdata,#$FF
    
                            rdlong  t2,par                'save received byte and inc head
                            add     t2,rxbuff
                            wrbyte  rxdata,t2
                            sub     t2,rxbuff
                            add     t2,#1
                            and     t2,#$0F
                            wrlong  t2,par
    
                            jmp     #receive              'byte done, receive next byte
    '
    '
    ' Transmit
    '
    transmit                jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,par                'check for head <> tail
                            add     t1,#2 << 2
                            rdlong  t2,t1
                            add     t1,#1 << 2
                            rdlong  t3,t1
                            cmp     t2,t3           wz
            if_z            jmp     #transmit
    
                            add     t3,txbuff             'get byte and inc tail
                            rdbyte  txdata,t3
                            sub     t3,txbuff
                            add     t3,#1
                            and     t3,#$0F
                            wrlong  t3,t1
    
                            or      txdata,#$100          'ready byte to transmit
                            shl     txdata,#2
                            or      txdata,#1
                            mov     txbits,#11
                            mov     txcnt,cnt
    
    :bit                    test    rxtxmode,#%100  wz    'output bit on tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_and_c      xor     txdata,#1
                            shr     txdata,#1       wc
            if_z            muxc    outa,txmask        
            if_nz           muxnc   dira,txmask
                            add     txcnt,bitticks        'ready next cnt
    
    :wait                   jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,txcnt              'check if bit transmit period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            djnz    txbits,#:bit          'another bit to transmit?
    
                            jmp     #transmit             'byte done, transmit next byte
    '
    '
    ' Uninitialized data
    '
    t1                      res     1
    t2                      res     1
    t3                      res     1
    
    rxtxmode                res     1
    bitticks                res     1
    
    rxmask                  res     1
    rxbuff                  res     1
    rxdata                  res     1
    rxbits                  res     1
    rxcnt                   res     1
    rxcode                  res     1
    
    txmask                  res     1
    txbuff                  res     1
    txdata                  res     1
    txbits                  res     1
    txcnt                   res     1
    txcode                  res     1
    PASM End
    */ 
    
    void mycogject(int cognumber, unsigned long *parameters_array)                                        // this name copied from the .spin name in the pasm section - names must match eg void mycogject matches mycogject.spin. Also first code after this must be the .h array file. Put your code after the };
    {
           /** 
            * @file mycogject_array.h
            * Created with spin.binary PASM to C Array Converter.
            * Copyright (c) 2011, John Doe
            */
           unsigned long mycogject_array[] =
           {
               0xa0bca9f0, 0x80fca810, 0x08bcaa54, 0xa0fcb201, 
               0x2cbcb255, 0x80fca804, 0x08bcaa54, 0xa0fcbe01, 
               0x2cbcbe55, 0x80fca804, 0x08bcae54, 0x80fca804, 
               0x08bcb054, 0x80fca804, 0x08bcb454, 0xa0bcc05a, 
               0x80fcc010, 0x627cae04, 0x617cae02, 0x689be85f, 
               0x68abec5f, 0xa0fcc833, 0x5cbcbc64, 0x627cae01, 
               0x613cb3f2, 0x5c640016, 0xa0fcb809, 0xa0bcba58, 
               0x28fcba01, 0x80bcbbf1, 0x80bcba58, 0x5cbcbc64, 
               0xa0bca85d, 0x84bca9f1, 0xc17ca800, 0x5c4c001f, 
               0x613cb3f2, 0x30fcb601, 0xe4fcb81e, 0x28fcb617, 
               0x60fcb6ff, 0x627cae01, 0x6cd4b6ff, 0x08bcabf0, 
               0x80bcaa5a, 0x003cb655, 0x84bcaa5a, 0x80fcaa01, 
               0x60fcaa0f, 0x083cabf0, 0x5c7c0016, 0x5cbcc85e, 
               0xa0bca9f0, 0x80fca808, 0x08bcaa54, 0x80fca804, 
               0x08bcac54, 0x863caa56, 0x5c680033, 0x80bcac60, 
               0x00bcc256, 0x84bcac60, 0x80fcac01, 0x60fcac0f, 
               0x083cac54, 0x68fcc300, 0x2cfcc202, 0x68fcc201, 
               0xa0fcc40b, 0xa0bcc7f1, 0x627cae04, 0x617cae02, 
               0x6ce0c201, 0x29fcc201, 0x70abe85f, 0x7497ec5f, 
               0x80bcc658, 0x5cbcc85e, 0xa0bca863, 0x84bca9f1, 
               0xc17ca800, 0x5c4c004d, 0xe4fcc446, 0x5c7c0033
           };
    
           _coginit((int)parameters_array>>2, (int)mycogject_array>>2, cognumber);  // array name built from spin file name
    }
    
    void clearscreen()                                                   // white text on dark blue background
    {
           int i;
           for (i=0;i<40;i++)
           {
                   t_setpos(0,0,i);                                      // move cursor to next line
                   t_color(0,0x08FC);                                    // RRGGBBxx eg dark blue background 00001000 white text 11111100
           }
    }
    
    void sleep(int milliseconds)                                         // sleep function
    {
           _waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
    }
    
    char peek(int address)                                               // function implementation of peek
    {
           return *((char *)address);
    }
    
    void poke(int address, char value)                                   // function implementation of poke
    {
           *((char *)address) = value;
    }
    
    unsigned long serial_start(unsigned long rxpin,unsigned long txpin,unsigned long mode, unsigned long baudrate, unsigned long par[])
    {
    /*
    PUB start(rxpin, txpin, mode, baudrate) : okay
    
    '' Start serial driver - starts a cog
    '' returns false if no cog available
    ''
    '' mode bit 0 = invert rx
    '' mode bit 1 = invert tx
    '' mode bit 2 = open-drain/source tx
    '' mode bit 3 = ignore tx echo on rx
    
      stop
      longfill(@rx_head, 0, 4)
      longmove(@rx_pin, @rxpin, 3)
      bit_ticks := clkfreq / baudrate
      buffer_ptr := @rx_buffer
      okay := cog := cognew(@entry, @rx_head) + 1
    */
    
    	unsigned long okay;
    	unsigned long bit_ticks;
    	unsigned long buffer_ptr;
    	par[0] = 0;						// rx_head   longfill(@rx_head, 0, 4)
    	par[1] = 0;						// rx_tail
    	par[2] = 0;						// tx_head
    	par[3] = 0;						// tx_tail
    	par[4] = rxpin;					//   longmove(@rx_pin, @rxpin, 3)
    	par[5] = txpin;					// note - if rewrite the pasm code could save a couple of hub longs here
    	par[6] = mode;					// as rxpin and txpin are not used anywhere else
    	bit_ticks = _clockfreq() / baudrate;   		//   bit_ticks := clkfreq / baudrate
    	par[7] = bit_ticks;
    	buffer_ptr = (unsigned long)&par[9];		//   buffer_ptr := @rx_buffer  points to start of circular buffer
    	par[8] = buffer_ptr;					// pointer to the start of the circular buffers
    								// rx buffer is 9 to 12 and tx buffer is 13 to 16 (16 bytes =4 longs)
    	mycogject(7,par);					// pass the packaged up array
    	// okay returns the cog number or -1 if a fail page 119 manual. Ignored here
    	printf("par array is at %u \n",(unsigned long)&par[0]);
    	printf("par array entry 1 is at %u \n",(unsigned long)&par[1]);
    	printf("par array entry 7 is at %u \n",(unsigned long)&par[7]);
    	printf("rx_head is at %u \n",(unsigned long)&par[9]);
    	printf("buffer_ptr is %u \n",par[8]);
    	return okay;
    }
    
    void serial_tx(char tx,unsigned long par[])
    {
    /*
    PUB tx(txbyte)
    '' Send byte (may wait for room in buffer)
      repeat until (tx_tail <> (tx_head + 1) & $F)
      tx_buffer[tx_head] := txbyte
      tx_head := (tx_head + 1) & $F
      if rxtx_mode & %1000
        rx
    */
    	unsigned long tx_head;
    	int address;
    	while ( par[3] == (par[2] + 1 ) & 0xF)	// par[2] is tx_head, par[3] is tx_tail
    	{
    	}
    	poke(address,tx);				// poke the tx byte value to hub ram
    	tx_head = par[2];				// get the head value
    	address = par[8] + 16 + tx_head;		// location of rx buffer plus 16 to get tx buffer plus the head value
    	poke(address,tx);				// poke the tx byte value to hub ram
    	tx_head = tx_head + 1;			// add one
    	tx_head = tx_head & 0xF; 			// logical and with 15
    	par[2] = tx_head;				// store it back again
    							// need to add the echo mode?
    }
    
    unsigned long serial_rxcheck(unsigned long par[])
    {
    /*
    PUB rxcheck : rxbyte
    '' Check if byte received (never waits)
    '' returns -1 if no byte received, $00..$FF if byte
      rxbyte--
      if rx_tail <> rx_head
        rxbyte := rx_buffer[rx_tail]
        rx_tail := (rx_tail + 1) & $F
    */
    	unsigned long rxbyte;			// actually is a long, so can return -1 FFFFFFFF if nothing and 0-FF if a byte
    	int address;					// hub address
    	rxbyte = rxbyte - 1;				// return ffffffff if nothing
    	if (par[1] != par[0])
    	{
    		address = par[8] + par[1];		// par[8] is the rx buffer, par[1] is rx_tail
    		rxbyte = peek(address);		// get the return byte from the buffer
    		par[1] = (par[1] +1) & 0xF;		// add one to tail
    	}
    	return rxbyte;
    }
    
    unsigned long serial_rx(unsigned long par[])
    {
    /*
    PUB rx : rxbyte
    '' Receive byte (may wait for byte)
    '' returns $00..$FF
      repeat while (rxbyte := rxcheck) < 0	
    */
    	unsigned long rxbyte;			// actually is a long, not a byte
    //	while (rxbyte = serial_rxcheck(par)) < 0
    //	{
    //	}
    	return rxbyte;				// return the value
    }
    
    
    
    void main ()
    {
    	int i;
    	unsigned long serial_parameters[16];				// reserve hub space for buffer, head tail pointers
           clearscreen();
           printf("Clock speed %u \n",_clockfreq());                     // see page 28 of the propeller manual for other useful commands
           printf("Catalina running in cog number %i \n",_cogid());      // integer
    	serial_start(31,30,0,1200,serial_parameters);			// start serial cog pins 31,30, mode 0, 1200 baud
           printf("Started serial driver\n");
    	for(i=0; i<40; i++)
    	{
    		serial_tx(65+i,serial_parameters); 					// test sending a byte
    		sleep(1000);
    	}
    	while (1); // Prop reboots on exit from main()
    }
    

    I'm now working on the receive part. I haven't tested rxcheck, but I think it is ok.

    The line that has me stumped at the moment is the line from "rx" in spin
      repeat while (rxbyte := rxcheck) < 0      
    

    This actually contains several things. It is a repeat loop. It is calling the function "rxcheck". It is checking if the return value is -1. And if the return value is not -1, it is also returning the value of the returned value.

    I know how to do some of that in C, but not how to do all of it in one line.

    You can see my attempt in the code. There is a slight complication in that all the "common" spin variables like "rx_head" are array variables in C as they can't be common variables, so I need to pass them from the main function to the rx function, and then from the rx function to the rxcheck function. I'm using "par" and I may or may not need to use different array names here??

    I'm not sure if this can be done in one line like in Spin? If not, maybe it needs splitting up into several lines (which I did with some of the other code too, as it made debugging easier).

    This is all very exciting!
  • kuronekokuroneko Posts: 3,623
    edited 2011-03-16 05:25
    If it has to be a single line then
    while ((rxbyte = serial_rxcheck(par)) < 0) {}
    
    will do just fine (if you don't mind an empty body).

    Where does this single line requirement come from? Auto-translation?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-03-16 05:52
    Thanks kuroneko. That works!

    The single line is because this is what Spin uses. Makes it easier to translate if it is a "line to line" translation.

    Two minor issues:

    1) in "serial_rxcheck, if I don't explicitly set rxbyte to zero at the beginning of the function, it decrements each time. I thought C set a variable to zero when the variable was defined? Maybe it is good programming practice to set variables to zero anyway?

    2) in the line kuroneko posted,
    	while ((rxbyte = serial_rxcheck(par)) == -1) {} // 0xffffffff and -1 works, but " < 0" gives a compiler error
    

    I get a compiler error when using <0, but 0xffffffff and -1 works fine. It doesn't really matter but it might make it easier translating spin.

    Anyway, the program below takes input from a terminal program and adds one and returns the character. Type "a" and it returns "b".

    I've very excited about this. It is the first working reloadable "cogject".

    /* PASM demo for use with Catalina IDE Precompiler and Compiler */
    
    #include <stdio.h>
    
    
    /* PASM Code for compilation using SpinC and inclusion as a data file
    PASM Start mycogject.spin
    CON
                                                                         ' add your values here
      _clkfreq = 80_000_000                                              ' 5Mhz Crystal
      _clkmode = xtal1 + pll16x                                          ' x 16
                                                                         ' Start of C hub constants
                                                                         ' End of C constants
    PUB Main
        coginit(1,@entry,0)                                           ' cog 1, cogstart, dummy value
    
    DAT
    
    '***********************************
    '* Assembly language serial driver *
    '***********************************
    
                            org
    '
    '
    ' Entry
    '
    entry                   mov     t1,par                'get structure address
                            add     t1,#4 << 2            'skip past heads and tails
    
                            rdlong  t2,t1                 'get rx_pin
                            mov     rxmask,#1
                            shl     rxmask,t2
    
                            add     t1,#4                 'get tx_pin
                            rdlong  t2,t1
                            mov     txmask,#1
                            shl     txmask,t2
    
                            add     t1,#4                 'get rxtx_mode
                            rdlong  rxtxmode,t1
    
                            add     t1,#4                 'get bit_ticks
                            rdlong  bitticks,t1
    
                            add     t1,#4                 'get buffer_ptr
                            rdlong  rxbuff,t1
                            mov     txbuff,rxbuff
                            add     txbuff,#16
    
                            test    rxtxmode,#%100  wz    'init tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_ne_c       or      outa,txmask
            if_z            or      dira,txmask
    
                            mov     txcode,#transmit      'initialize ping-pong multitasking
    '
    '
    ' Receive
    '
    receive                 jmpret  rxcode,txcode         'run a chunk of transmit code, then return
    
                            test    rxtxmode,#%001  wz    'wait for start bit on rx pin
                            test    rxmask,ina      wc
            if_z_eq_c       jmp     #receive
    
                            mov     rxbits,#9             'ready to receive byte
                            mov     rxcnt,bitticks
                            shr     rxcnt,#1
                            add     rxcnt,cnt                          
    
    :bit                    add     rxcnt,bitticks        'ready next bit period
    
    :wait                   jmpret  rxcode,txcode         'run a chuck of transmit code, then return
    
                            mov     t1,rxcnt              'check if bit receive period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            test    rxmask,ina      wc    'receive bit on rx pin
                            rcr     rxdata,#1
                            djnz    rxbits,#:bit
    
                            shr     rxdata,#32-9          'justify and trim received byte
                            and     rxdata,#$FF
                            test    rxtxmode,#%001  wz    'if rx inverted, invert byte
            if_nz           xor     rxdata,#$FF
    
                            rdlong  t2,par                'save received byte and inc head
                            add     t2,rxbuff
                            wrbyte  rxdata,t2
                            sub     t2,rxbuff
                            add     t2,#1
                            and     t2,#$0F
                            wrlong  t2,par
    
                            jmp     #receive              'byte done, receive next byte
    '
    '
    ' Transmit
    '
    transmit                jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,par                'check for head <> tail
                            add     t1,#2 << 2
                            rdlong  t2,t1
                            add     t1,#1 << 2
                            rdlong  t3,t1
                            cmp     t2,t3           wz
            if_z            jmp     #transmit
    
                            add     t3,txbuff             'get byte and inc tail
                            rdbyte  txdata,t3
                            sub     t3,txbuff
                            add     t3,#1
                            and     t3,#$0F
                            wrlong  t3,t1
    
                            or      txdata,#$100          'ready byte to transmit
                            shl     txdata,#2
                            or      txdata,#1
                            mov     txbits,#11
                            mov     txcnt,cnt
    
    :bit                    test    rxtxmode,#%100  wz    'output bit on tx pin according to mode
                            test    rxtxmode,#%010  wc
            if_z_and_c      xor     txdata,#1
                            shr     txdata,#1       wc
            if_z            muxc    outa,txmask        
            if_nz           muxnc   dira,txmask
                            add     txcnt,bitticks        'ready next cnt
    
    :wait                   jmpret  txcode,rxcode         'run a chunk of receive code, then return
    
                            mov     t1,txcnt              'check if bit transmit period done
                            sub     t1,cnt
                            cmps    t1,#0           wc
            if_nc           jmp     #:wait
    
                            djnz    txbits,#:bit          'another bit to transmit?
    
                            jmp     #transmit             'byte done, transmit next byte
    '
    '
    ' Uninitialized data
    '
    t1                      res     1
    t2                      res     1
    t3                      res     1
    
    rxtxmode                res     1
    bitticks                res     1
    
    rxmask                  res     1
    rxbuff                  res     1
    rxdata                  res     1
    rxbits                  res     1
    rxcnt                   res     1
    rxcode                  res     1
    
    txmask                  res     1
    txbuff                  res     1
    txdata                  res     1
    txbits                  res     1
    txcnt                   res     1
    txcode                  res     1
    PASM End
    */ 
    
    void mycogject(int cognumber, unsigned long *parameters_array)                                        // this name copied from the .spin name in the pasm section - names must match eg void mycogject matches mycogject.spin. Also first code after this must be the .h array file. Put your code after the };
    {
           /** 
            * @file mycogject_array.h
            * Created with spin.binary PASM to C Array Converter.
            * Copyright (c) 2011, John Doe
            */
           unsigned long mycogject_array[] =
           {
               0xa0bca9f0, 0x80fca810, 0x08bcaa54, 0xa0fcb201, 
               0x2cbcb255, 0x80fca804, 0x08bcaa54, 0xa0fcbe01, 
               0x2cbcbe55, 0x80fca804, 0x08bcae54, 0x80fca804, 
               0x08bcb054, 0x80fca804, 0x08bcb454, 0xa0bcc05a, 
               0x80fcc010, 0x627cae04, 0x617cae02, 0x689be85f, 
               0x68abec5f, 0xa0fcc833, 0x5cbcbc64, 0x627cae01, 
               0x613cb3f2, 0x5c640016, 0xa0fcb809, 0xa0bcba58, 
               0x28fcba01, 0x80bcbbf1, 0x80bcba58, 0x5cbcbc64, 
               0xa0bca85d, 0x84bca9f1, 0xc17ca800, 0x5c4c001f, 
               0x613cb3f2, 0x30fcb601, 0xe4fcb81e, 0x28fcb617, 
               0x60fcb6ff, 0x627cae01, 0x6cd4b6ff, 0x08bcabf0, 
               0x80bcaa5a, 0x003cb655, 0x84bcaa5a, 0x80fcaa01, 
               0x60fcaa0f, 0x083cabf0, 0x5c7c0016, 0x5cbcc85e, 
               0xa0bca9f0, 0x80fca808, 0x08bcaa54, 0x80fca804, 
               0x08bcac54, 0x863caa56, 0x5c680033, 0x80bcac60, 
               0x00bcc256, 0x84bcac60, 0x80fcac01, 0x60fcac0f, 
               0x083cac54, 0x68fcc300, 0x2cfcc202, 0x68fcc201, 
               0xa0fcc40b, 0xa0bcc7f1, 0x627cae04, 0x617cae02, 
               0x6ce0c201, 0x29fcc201, 0x70abe85f, 0x7497ec5f, 
               0x80bcc658, 0x5cbcc85e, 0xa0bca863, 0x84bca9f1, 
               0xc17ca800, 0x5c4c004d, 0xe4fcc446, 0x5c7c0033
           };
    
           _coginit((int)parameters_array>>2, (int)mycogject_array>>2, cognumber);  // array name built from spin file name
    }
    
    void clearscreen()                                                   // white text on dark blue background
    {
           int i;
           for (i=0;i<40;i++)
           {
                   t_setpos(0,0,i);                                      // move cursor to next line
                   t_color(0,0x08FC);                                    // RRGGBBxx eg dark blue background 00001000 white text 11111100
           }
    }
    
    void sleep(int milliseconds)                                         // sleep function
    {
           _waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
    }
    
    char peek(int address)                                               // function implementation of peek
    {
           return *((char *)address);
    }
    
    void poke(int address, char value)                                   // function implementation of poke
    {
           *((char *)address) = value;
    }
    
    unsigned long serial_start(unsigned long rxpin,unsigned long txpin,unsigned long mode, unsigned long baudrate, unsigned long par[])
    {
    /*
    PUB start(rxpin, txpin, mode, baudrate) : okay
    
    '' Start serial driver - starts a cog
    '' returns false if no cog available
    ''
    '' mode bit 0 = invert rx
    '' mode bit 1 = invert tx
    '' mode bit 2 = open-drain/source tx
    '' mode bit 3 = ignore tx echo on rx
    
      stop
      longfill(@rx_head, 0, 4)
      longmove(@rx_pin, @rxpin, 3)
      bit_ticks := clkfreq / baudrate
      buffer_ptr := @rx_buffer
      okay := cog := cognew(@entry, @rx_head) + 1
    */
    
    	unsigned long okay;
    	unsigned long bit_ticks;
    	unsigned long buffer_ptr;
    	par[0] = 0;						// rx_head   longfill(@rx_head, 0, 4)
    	par[1] = 0;						// rx_tail
    	par[2] = 0;						// tx_head
    	par[3] = 0;						// tx_tail
    	par[4] = rxpin;					//   longmove(@rx_pin, @rxpin, 3)
    	par[5] = txpin;					// note - if rewrite the pasm code could save a couple of hub longs here
    	par[6] = mode;					// as rxpin and txpin are not used anywhere else
    	bit_ticks = _clockfreq() / baudrate;   		//   bit_ticks := clkfreq / baudrate
    	par[7] = bit_ticks;
    	buffer_ptr = (unsigned long)&par[9];		//   buffer_ptr := @rx_buffer  points to start of circular buffer
    	par[8] = buffer_ptr;					// pointer to the start of the circular buffers
    								// rx buffer is 9 to 12 and tx buffer is 13 to 16 (16 bytes =4 longs)
    	mycogject(7,par);					// pass the packaged up array
    	// okay returns the cog number or -1 if a fail page 119 manual. Ignored here
    	printf("par array is at %u \n",(unsigned long)&par[0]);
    	printf("par array entry 1 is at %u \n",(unsigned long)&par[1]);
    	printf("par array entry 7 is at %u \n",(unsigned long)&par[7]);
    	printf("rx_head is at %u \n",(unsigned long)&par[9]);
    	printf("buffer_ptr is %u \n",par[8]);
    	return okay;
    }
    
    void serial_tx(char tx,unsigned long par[])
    {
    /*
    PUB tx(txbyte)
    '' Send byte (may wait for room in buffer)
      repeat until (tx_tail <> (tx_head + 1) & $F)
      tx_buffer[tx_head] := txbyte
      tx_head := (tx_head + 1) & $F
      if rxtx_mode & %1000
        rx
    */
    	unsigned long tx_head;
    	int address;
    	while ( par[3] == (par[2] + 1 ) & 0xF)	// par[2] is tx_head, par[3] is tx_tail
    	{
    	}
    	poke(address,tx);				// poke the tx byte value to hub ram
    	tx_head = par[2];				// get the head value
    	address = par[8] + 16 + tx_head;		// location of rx buffer plus 16 to get tx buffer plus the head value
    	poke(address,tx);				// poke the tx byte value to hub ram
    	tx_head = tx_head + 1;			// add one
    	tx_head = tx_head & 0xF; 			// logical and with 15
    	par[2] = tx_head;				// store it back again
    							// need to add the echo mode?
    }
    
    unsigned long serial_rxcheck(unsigned long par[])
    {
    /*
    PUB rxcheck : rxbyte
    '' Check if byte received (never waits)
    '' returns -1 if no byte received, $00..$FF if byte
      rxbyte--
      if rx_tail <> rx_head
        rxbyte := rx_buffer[rx_tail]
        rx_tail := (rx_tail + 1) & $F
    */
    	unsigned long rxbyte;			// actually is a long, so can return -1 FFFFFFFF if nothing and 0-FF if a byte
    	int address;					// hub address
    	rxbyte = 0;					// set explicitly to zero
    	rxbyte = rxbyte - 1;				// return ffffffff if nothing
    //	printf("rx check value %u \n",rxbyte);
    //	printf("rx head %u \n",par[0]);
    //	printf("rx tail %u \n",par[1]);
    	if (par[1] != par[0])
    	{
    		address = par[8] + par[1];		// par[8] is the rx buffer, par[1] is rx_tail
    		rxbyte = peek(address);		// get the return byte from the buffer
    		par[1] = (par[1] +1) & 0xF;		// add one to tail
    	}
    	return rxbyte;
    }
    
    unsigned long serial_rx(unsigned long par[])
    {
    /*
    PUB rx : rxbyte
    '' Receive byte (may wait for byte)
    '' returns $00..$FF
      repeat while (rxbyte := rxcheck) < 0	
    */
    	unsigned long rxbyte;			// actually is a long, not a byte
    	while ((rxbyte = serial_rxcheck(par)) == -1) {} // 0xffffffff and -1 works, but " < 0" gives a compiler error
    	return rxbyte;				// return the value
    }
    
    
    
    void main ()
    {
    	int i;
    	unsigned long received_byte;
    	unsigned long serial_parameters[16];				// reserve hub space for buffer, head tail pointers
           clearscreen();
           printf("Clock speed %u \n",_clockfreq());                     // see page 28 of the propeller manual for other useful commands
           printf("Catalina running in cog number %i \n",_cogid());      // integer
    	serial_start(31,30,0,1200,serial_parameters);			// start serial cog pins 31,30, mode 0, 1200 baud
           printf("Started serial driver\n");
    	for(i=0; i<10; i++)
    	{
    		serial_tx(65+i,serial_parameters); 					// test sending a byte
    		sleep(500);
    		printf("send byte %u \n",65+i);
    	}
    	printf("type some characters, will return that character plus 1 \n");
    	for (i=0;i<19;i++)							// test 19 times, so tests buffer restarting
    	{
    		received_byte = serial_rx(serial_parameters);		// get a byte
    		received_byte = received_byte + 1;				// add one and send it back
    		serial_tx(received_byte,serial_parameters);
    		printf("sent back byte %u \n",received_byte);
    	}
    	printf("program finished \n");
    	while (1); // Prop reboots on exit from main()
    }
    
    
Sign In or Register to comment.