Shop OBEX P1 Docs P2 Docs Learn Events
Catalina 2.6 - a FREE C compiler for the Propeller - The Final Frontier! - Page 18 — Parallax Forums

Catalina 2.6 - a FREE C compiler for the Propeller - The Final Frontier!

11415161820

Comments

  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-02 00:47
    Multithreading? Please tell us more!

    This was the stuff I was playing with using MP/M. I had four C programs running all at once. However, the MP/M emulation itself was unstable and would crash every hour or so. What it did do is give me a real taste for the cool things you can do with multithreading. So this sounds very interesting.

    MP/M has a 50Hz interrupt. Your check every 100 instructions ought to look exactly the same to the user, ie it would appear like programs are running in parallel. It will be like the way cogs run in parallel, except presumably with 10 or even 100x the code space.

    Very cutting edge!

    Ok, back to the hard slog.

    So spinc is the magic program doing the clever separation of the pasm bit? Ah ha!

    Next step = getting the custom demo downloaded and working. Then I can start experimenting with changing bits. Dumb question here - does the custom_demo run in XMM?
  • RossHRossH Posts: 5,593
    edited 2010-12-02 01:53
    Dr_Acula wrote: »
    Multithreading? Please tell us more!
    There's a whole thread about it here. You must have missed it.
    Dr_Acula wrote: »
    So spinc is the magic program doing the clever separation of the pasm bit? Ah ha!

    Next step = getting the custom demo downloaded and working. Then I can start experimenting with changing bits. Dumb question here - does the custom_demo run in XMM?

    No - it's LMM only. It's intended to teach the basics of plugins (which are the same for LMM and XMM). But XMM adds a level of complexity to the targets and the load process that you don't want to tackle until you get LMM under your belt.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-02 02:27
    Thanks for the link - I'll check it out.

    As a casual observer, LMM sounds more complex than XMM, but you know the code. eg, with LMM, if hub is full of code, how do you find space to load in a cog? Do you always reserve the bottom 2k of hub ram for instance? XMM sounds simpler in that there is plenty of room in hub.

    But I can't even get the simplest of stuff to work here. I'm not even sure of the best approach - take a complex LMM plugin and strip bits out, or build up an XMM plugin from scratch.

    Since the ultimate project is going to use most of the hub for a video buffer, that means this is really an XMM project, and since the plugin examples are LMM, maybe I'll go back to building up an XMM plugin from scratch.

    I think I'll go back to what I was working on yesterday, and continue posting examples of PASM code and ask you which bits absolutely have to be there.

    See below - I'm sure there is something missing, but this is the idea

    1) pub start - compiler needs this but it does not do anything
    2) registry is at 0x7eFC and this is not likely to change, right?
    3) registry locations are located below this point, at known fixed locations, right?
    4) I think you said earlier that only the code in the cog knows which cog it is in. So the entry part must pass the cog number back to the registry. I am guessing this is a long, and its location is 0x7eFC - cognumber*n where n is a constant.


    Maybe lets break this down into parts - look at the entry part of the code first before passing any variables.

    Coupled with C code to take this compiled binary and load it into a cog, is this code enough to register this cog as running a dummy plugin?
    CON
    
    ' Registry definitions. The Catalina registry provides a place where all
    ' plugins can be found, and a request block for sending requests to each
    ' plugin. The registry location must be known to all plugins. To use the
    ' registry, each plugin finds its own cog number, and then registers by
    ' putting its plugin type in the top 8 bits of the long at
    '    long[REGISTRY][plugin_cog]
    ' The bottom 24 bits of that long point to the request block the plugin
    ' must use to receive requests and return results. Each request block
    ' is two longs - the first long is the request, which may be a 'short' 
    ' request (if the request and all its parameters can fit into a single
    ' long, or the address of a 'long' parameter block elsewhere in RAM. The
    ' plugin must know how to interpret these requests. When complete, the
    ' plusing zeroes the first long of the request block, and may either
    ' return the result in the second long of the request block (typical for
    ' 'short' requests, but also used for some 'long' requests), or in the
    ' 'long' parameter block.  
    ' 
    COGSTORE     = $7EFC           ' long used for interacting with cog used to store command line
    REGISTRY_END = COGSTORE        ' this location is fairly arbitrary.
    REGISTRY     = REGISTRY_END - 4 * 1 * 8
    REQUESTS     = REGISTRY     - 4 * 2 * 8
    '
    '
    OBJ
    ' no objects
    
    PUB start 
    '      dummy spin code so compiler works
    
    DAT
                  org       0
    entry
                  cogid     t1                      ' get ...
                  shl       t1,#2                   ' ... our ...
    '              add       t1,par                  ' ... registry block
    '   need to insert some code that takes $7EFC (the end of the registry)
    ' takes the cog number and (I think) $7EFC - (cognumber)*n where n is 8 or 16 or something else
    
                  rdlong    rqstptr,t1              ' register ...
                  and       rqstptr,low_24          ' ... this ...
                  wrlong    zero,rqstptr            ' ... plugin ...
                  mov       t2,#8                   ' ... as ...  #8 is a dummy plugin
                  shl       t2,#24                  ' ... the ...
                  or        t2,rqstptr              ' ... appropriate ...
                  wrlong    t2,t1                   ' ... type
    
    get_service
                  rdlong    rqst,rqstptr wz         ' any service requests?
                  if_z jmp get_service                   ' nothing so try again
                  mov       t1,rqst                 ' yes - get ...
                  shr       t1,#24                  ' ... service ...
                  mov       t1,rqstptr              ' return result ...
                  add       t1,#4                   ' ... in second long ... 
                  wrlong    rslt,t1                 ' ... of request block 
                  wrlong    zero,rqstptr            ' clear request block
                  jmp       #get_service            ' process next service request 
    
    
    '---------------------------------- Storage ------------------------------------
    '
    zero          long      0                       ' handy value (zero)
    low_24        long      $00FFFFFF               ' handy value (lower 24 bits)
    rqstptr       long      0                       ' request address
    rqst          long      0                       ' service request
    rslt          long      0                       ' service result
    t1            long      0                       ' temporary variable
    t2            long      0                       ' temporary variable
    t3            long      0                       ' temporary variable
    '
    param         long      0                       ' saved initialization data
    '
                  fit       $1f0 
    
  • RossHRossH Posts: 5,593
    edited 2010-12-02 04:02
    Hi Dr_Acula,

    I haven't read the code in detail, but it looks about right. However, you are going to run into all sorts of problems using so many "magic numbers" sprinkled throughout your code.

    If you don't want to use Catalina_Common.spin, at least make your own "cut down" version of it and put all your common definitions in there. Anything else and you will run into problems eventually. Just one example - what happens if I move the Registry (e.g. because I need more space for something?).

    On your point 4: the plugin knows what cog it is in - what I said previously was that the program only knows at the point of loading it what cog a plugin ends up in (whether it is loaded from SPIN or C). So that's the point at which the program has to register it (unless it registers itself).

    Finally, on XMM vs LMM - I suggest you get completely comfortable with LMM before tackling XMM. XMM introduces many additional complexities.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-02 04:46
    what happens if I move the Registry

    You won't do that? Will you?

    Thinking big picture, if I were starting this from scratch, I'd have put the registry at the top of ram too. Sooner or later one has to make a decision. Take a stand! Zog and Propbasic and all the others will eventually follow, right?!

    Ok, so the registry is at 0x7EFC. Your registry conventions have four data types and this appears more than flexible enough to do anything I can think of. It is very simple for instance to pass the location of a block of hub ram that might be, say, a video buffer.

    In fact, go one step further and fix the location of the registry and the requests. It is but a tiny bit of hub ram. There, it is done!

    Once you have done that, maybe there are no further magic numbers at all?

    Consider some C code. No plugins at all are defined at compile time. All we say is that there is some C code and it is going to run in external memory.

    This C code starts running and it knows it has 7 cogs free. So the first thing it does is load up a vga text driver. The C code knows it is going to put the binary in cog 1 and cog 2, so the vga driver does not even need to be self registering. The C code can copy the appropriate values to the registry. The C code knows that this VGA driver needs 3200 bytes of hub ram. So it passes the base value of that to the cog, via the registry.

    Next, we load up a keyboard driver into cog 3. We arbitrarily allocate 100 bytes of buffer space, and the C program knows it just loaded a certain type of vga driver that took 3200 bytes, so it puts the keyboard driver at location 3200 to 3300.

    Then we print a message and ask the user to hit a key.

    Now this is the clever bit. We now wipe the vga text driver from cogs 1 and 2, and instead load a vga graphics driver. The C code knows what sort of graphics driver it is, and it knows that this grapics driver has 19200 bytes for its video buffer. The C program also knows that this is going to overwrite the keyboard driver.

    So, in C, the first thing we do is move the keyboard buffer from 3200-3300 to 19200-19300. Then we load two cogs with the vga graphics driver. Register them from C.

    All the values needed to do this reside within the C program.

    Here is the heretical bit - none of the above references catalina_common. This is a big minus, but maybe not?

    Lets look at another way of doing things. If you are going to have an sd card, then text files are very easy. So - for every binary blob that gets loaded into cogs, create an associated text file that goes with that binary blob. This can describe, in simple language, what pins are associated with what hardware platform. This can use #ifdef syntax, but it can also use any language you like, because this text file is not being interpreted by BST or homespun or the proptool. Rather, it is being interpreted by the C program that is loading the cog binaries.

    Now, here is the cool bit. Say Zog wanted to load that vga driver. Or Propbasic. Or KyeDOS. All those programs need to do is look for the text file (maybe it is an .INI) and look through the list of platforms, then load the binary into cogs, and then tell it what pins do what.

    But maybe you want to use the catalina common file? Well, a simple C interpreter can even load that as a text file and parse it and get the values it needs that way. Just copy the common_values file to the sd card.

    So - you could have everything in one place. Or you could have binary blobs with their associated INI file.

    Make the values in the INI file really obvious what they are. Use a syntax that allows comments. Maybe make it even look like the CON section of a Spin program if you like. Then anyone with a text editor can change them as needed.

    Another point - nothing in the above is throwing away anything that has been built already. It is simply adding another layer on. You can still compile using the command line options. Or even do a hybrid - compile with the command line options such that you know that cog 7 is free, and load code in and out of cog 7 as required.

    So in summary, as long as the registry does not move, I don't think there are any other magic numbers.
  • RossHRossH Posts: 5,593
    edited 2010-12-02 05:26
    Hi Dr_Acula,

    Everything you describe is feasible. Similar things have been suggested before. However, they never seems to get much traction - maybe because the infrastructure required to make it work takes up so much space that you don't have much left to do anything useful with it.

    With fast XMM RAM you could certainly do it - but you are trading speed for flexibility.

    I'd suggest you start by writing a few LMM plugins, then tackle an XMM program that loads and uses them and see where you end up.

    Ross.

    P.S. You will still have magic numbers that should be put into one or more common definitions file. Try writiing a few plugins and you'll see what I mean. Just for starters, your plugins all need a unique number - you hardcoded "8" in your code rather than import it from Catalina_Common.spin - but how to other programs know that this is the number you have chosen for your plugin?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-03 04:00
    but how to other programs know that this is the number you have chosen for your plugin?

    Yes, I'm thinking of answers to that. The IDE already has a number of settings, eg XMM or not, download baud rate, com port, and these are all being stored in one INI setting.An IDE can also parse your common_input file and extract values from that. Indeed, I might look at adding that.

    I got a little sidetracked adding the Edit menu options to the IDE, and adding little things like F10 to compile so it is the same as the prop tool.

    Now on to experimenting with the commands. Page 28 in the manual has these. See the attached code - able to get the clock frequency for instance and print in on the screen.

    The catalina functions I need to experiment are things like
    int _coginit(int par, int addr, int cogid);
    int _cogstop(int cogid);
    unsigned _registry();
    void _register_plugin(int cog_id, int plugin_type);


    Can I ask, is there a C function to write/write a byte to a specific location in hub ram?
    1024 x 768 - 82K
  • RossHRossH Posts: 5,593
    edited 2010-12-03 04:18
    Dr_Acula wrote: »

    Can I ask, is there a C function to write/write a byte to a specific location in hub ram?

    Why would you need a function?
    void main() {
    
       register char *addr = (char *)0x5000; // Hub RAM Address 
       register char byte;
    
       byte = *addr; // read byte from Hub RAM
       *addr = byte; // write byte to Hub RAM
    }
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-03 04:55
    Sounds great. So - next thing is to move some binary bytes to a known location in hub ram (either from a data array or off the sd card), and then do a coginit to load that data into a cog.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-03 18:12
    A few quick questions:

    1) Do you have a delay routine in C? I know I can just use a For loop, but it would be neater to be able to sleep for n milliseconds based on the propellers clock?

    2) What is the best way to use constants in Catalina.
    int const a = 1;
    or
    #define MYVARIABLE	20  // can catalina use this?
    

    I read somewhere in the manual that if you define a constant in XMM the value ends up in hub. Is that right? I'd like to keep as much as possible in external ram.

    3) I am slightly confused by this line
    register char *addr = (char *)0x5000; // Hub RAM Address 
    

    as I think it is registering pointer *addr with a constant hex location 0x5000 and I'd like to instead use a variable location. I've got the skeleton of peek and poke, and I know it is inefficient to put these into functions, but I find it easier to understand code if it is in individual functions, even if sometimes that function slows things down. I can understand how to find the pointer of variable x, but how does one find the pointer to an address? Help fleshing out these two functions would be most appreciated: (I get to use the Copy menu item in the Catalina IDE for the first time!)
    char peek_hub(int address)						// get value in hub ram
    {
    	char peek_byte;
    
    									// convert integer 'address' into pointer addr
    	peek_byte = *addr;						// read value from pointer addr
    	return peek_byte;						// return it
    }
    
    void poke_hub(char value,int address)				// poke value to hub ram
    {
    									// convert integer 'address' to pointer addr
    	*addr = value;						// write to hub ram
    }
    
    
  • RossHRossH Posts: 5,593
    edited 2010-12-03 20:30
    Dr_Acula wrote: »
    A few quick questions:

    1) Do you have a delay routine in C? I know I can just use a For loop, but it would be neater to be able to sleep for n milliseconds based on the propellers clock?
    Just use the _waitcnt() function. To convert to milliseconds, do something like:
    #define msleep(ms) _waitcnt((ms)*(clockfreq()/1000))
    
    Dr_Acula wrote: »

    2) What is the best way to use constants in Catalina.

    Use #define - this is standard C. The good thing about #define is it takes no code for things that can be evaluated at compile time - so as well as simple things like:
    #define ONE_THOUSAND 1000
    
    You can do complex things, such as:
    #define FARENHEIGHT(CELSIUS) ((((CELSIUS) * 9)/5) + 32)
    
    #define FREEZING FARENHEIGHT(0)
    
    void main() {
       int a = FREEZING;
    }
    
    Dr_Acula wrote: »
    I read somewhere in the manual that if you define a constant in XMM the value ends up in hub. Is that right? I'd like to keep as much as possible in external ram.

    No - if you can point out to me where it says that I'll correct it. The rule for the LARGE XMM memory model is that everything is in XMM RAM except for the stack, which is in Hub RAM. All variables local to a function are allocated on the stack, and are therefore in Hub RAM. All "global" variables (more correctly, variables with "file" scope) are in XMM RAM.
    Dr_Acula wrote: »
    3) I am slightly confused by this line
    register char *addr = (char *)0x5000; // Hub RAM Address 
    
    as I think it is registering pointer *addr with a constant hex location 0x5000 and I'd like to instead use a variable location. I've got the skeleton of peek and poke, and I know it is inefficient to put these into functions, but I find it easier to understand code if it is in individual functions, even if sometimes that function slows things down. I can understand how to find the pointer of variable x, but how does one find the pointer to an address? Help fleshing out these two functions would be most appreciated: (I get to use the Copy menu item in the Catalina IDE for the first time!)
    The code I wrote is simply allocating a "pointer to char" variable called addr and initializing it with 0x5000. But addr is just a variable - you can assign another value to it if you want.

    By the way, your peek and poke code can be reduced to a couple of macros. Here is the code for both ways of implementing it. If you looked at the code generated, you'll understand why you don't want to use functions for doing things like this:
    // function implementation of peek:
    char peek(int address) {
        return *((char *)address);
    }
    
    // function implementation of poke:
    void poke(int address, char value) {
        *((char *)address) = value;
    }
    
     // macro implementation of peek:
    #define mpeek(addr) (*(addr))
    
    // macro implementation of poke:
    #define mpoke(addr, value) ((*addr) = value)
    
    void main() {
       register char c;
    
       // peek and poke using function calls:
       c = peek(0x5000);
       poke (0x5000, 0xff);
    
       // peek and poke using macros:
       c = mpeek((char *)0x5000);
       mpoke ((char *)0x5000, 0xFF);
    }
    
    Another advantage of the macro method is that it works for all types - provided you type cast your pointer to the correct type (as I do above by saying (char *)0x5000 instead of just 0x5000).

    Can I suggest you spend some time looking at a book on C before you tackle anything too complex? You'll save yourself an awful lot of grief and heartache if you do!

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-03 21:06
    Thanks for the quick replies.

    1) waitcnt looks like the answer, but can I just double check, do you need to read in the current counter and add it to the delay - eg the 'cnt' in this code. This code also has a check in case the number is very short.
    PUB pause1ms(period)
    '' Pause execution for period (in units of 1 ms).
      clkcycles := ((clkfreq / 1000 * period) - 4296) #> 381     ' Calculate 1 ms time unit
      waitcnt(clkcycles + cnt)                                   ' Wait for designated time
    
    addit - got it working in C
    void sleep(int milliseconds)                                         // sleep function
    {
         _waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
    }
    


    2) #define looks great
    3) I can't seem to find that XMM bit now, but in any case, great to hear the data is in external ram.
    4) The 0x5000 was confusing me as to whether it was a dummy value or an example value. Your additional code clarifies this so thanks++. I have quite a few tutorials on C, including some quite complicated ones on pointers, but even after an hour on the internet I could not find an example such as your code above. The closest I could get was BDS C which actually has peek and poke as functions, though with text that explains that you are better to use pointers. Some things in C are hard to search for too, eg the example of "(char *)0x5000", where the bit I need to clarify is the "space star right-bracket". It is hard to search google for "space star right-bracket - viz "Your search - " *)" - did not match any documents", and clearly this is different to the pointers with the star in front of the name or behind the name but with no space. I'm a bit of a newbie with C, but thanks to your help, learning very fast.

    Actually with the IDE, I reckon that the Propeller is becoming a very useful platform for learning C. The compile/download/run cycle with Payload is now so fast that you can change programs one line at a time and debug them as you go.

    Here is some test code checking a few things out. Next step is wiping the hub memory in XMM mode and poking in new data.
    void main () 
    {
         char c;     
         int i;     
         clearscreen();
         printf("Hello, World!\n");
         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=400;
         c=peek(i);                                                      // peek byte at this location
         printf("Peek value at %i = character %c Ascii value %d \n",i,c,c);                            
         while (1) ;                                                     // Prop reboots on exit from main() so stop this
    }
    

    Hopefully I can give something back in return for all this help, so attached is the latest version of the IDE. Compiled version (run Setup - hopefully no issues with the previous version), and there is the VB.NET source code as well. There is a small chance it might crash looking for a bitmap file in the picture editor part - when I get home I'll change that code.

    I've added an automatic indent menu item in Edit. Now this is something you can't do in Spin. If you use the C format with the { on a separate line (I need to think about the format where { is at the end of lines), then you can take a messy piece of code and tidy it up. The screenshots show before (black and white) and after (color). Strip out any tabs, remove all leading spaces, then put leading spaces back in with the right indents, and finally put all the comments in the same column.

    Instant neat code!

    Coding is quicker too - no need to hit tab at the end of lines for comments and you can even be lazy for small routines and not even indent any of the lines while you are writing the code.


    Catalina_Compiled_4th_December_2010.zip
    Catalina_Source_4th December 2010.zip
    C_unformatted.jpg

    c_formatted.jpg
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-06 21:51
    Hi Ross,

    Great news. I've got a tiny little program that is able to wipe the hub memory. And everything keeps running. Now, you really can't do that in Spin!
    void fill_memory(int start_address,int finish_address,char value)    // fill memory with these bytes
    {
           int i;
           for(i=start_address;i <= finish_address;i++)
           {
                  poke(i,value);   
           }
    }
    
    fill_memory(0,25000,65);                                        // fill memory NOTE must be running in XMM mode
    
    

    I am having some reliability problems with the serial driver code I'm using in KyeDOS with the xmodem file transfer to sd card crashing about once every 300 packets. I could go back to the standard Obex code, or maybe Kye's code.

    But that may not be necessary given your Catalyst solution worked out so much faster than what I was doing. Can I ask if there is a sequence of commands you can send to download and run an XMM program? Eg using catalyst and sending some sort of bootloader that then does the transfer and moves it to high memory? Even if you can't save it to the SD card, it would still be very useful for quick debugging. I have a vague feeling you have a way of downloading to external memory and any help would be most appreciated.
  • RossHRossH Posts: 5,593
    edited 2010-12-06 23:13
    Hi Dr_Acula,

    You need to be a bit careful when wiping RAM - you may end up overwriting the C stack. You could write a small LMM PASM function to return you the current stack pointer, and them make sure you never wipe higher in Hub RAM than that - something like:
    ' Catalina Code
    DAT ' code segment
    ' Catalina Export _SP
     long ' align long
    C__S_P_
       mov r0, SP
       jmp #RETN
    ' end
    
    You could then use this as follows:
    fill_memory(0,_SP(),65);
    
    As for loading XMM programs serially, you don't need Catalyst - payload can do that for you. Run the command build_all DRACBLADE in the utilities folder, and copy the resulting XMM.binary to your working directory.

    Then you just say something like:
    payload XMM my_program
    
    This first downloads the file XMM.binary, which is itself an XMM loader program. That program starts automatically, then payload downloads my_program.binary, and the XMM loader loads it into XMM RAM and then starts it.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-07 04:50
    Catalina just seems to get better and better!

    The payload download looks like the way to go. I'll add that as another download option in the IDE.

    The inline PASM code? Now this is a little gem that is going to take some thinking about. Yes, I think I can see what you are doing here - by the time you are compiling to XMM and have a dedicated cog running that, you can add pasm inside C? Is that what you are doing?

    If so, then Catalina joins a very select few group of programs where you can mix and match high level and assembly code. Of course, Spin can do this up to an extent, but you have to load cogs in and out.

    My head is racing with possibilities. Fast block moves for memory. Building video tiles on the fly within code (I'd copy some existing vb.net code for that). Hmm - a Spin interpreter in XMM PASM code??
  • RossHRossH Posts: 5,593
    edited 2010-12-07 14:26
    Dr_Acula wrote: »
    Catalina just seems to get better and better!

    The payload download looks like the way to go. I'll add that as another download option in the IDE.

    The inline PASM code? Now this is a little gem that is going to take some thinking about. Yes, I think I can see what you are doing here - by the time you are compiling to XMM and have a dedicated cog running that, you can add pasm inside C? Is that what you are doing?
    Yes - you can include 'inline' LMM PASM code simply by adding it as a function and calling it from C. The simplest and cleanest way is to include such functions in a library. My previous example had a couple of typos in it (now fixed - but at the time I hadn't actually compiled it) so I have attached a full example of how to implement a simple LMM PASM function that can be called from C to return the current stack pointer. This will work in both LMM and XMM programs.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-08 05:18
    ......................
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-08 05:20
    Catching up with some code. The XMM inline code is going to be very useful. One immediate benefit would be the ability to debug pasm code that will eventually be copied to a standalone cogject. Much easier to debug it as inline code first. Test something. Return a value. Test it again with a bit more code. etc

    Just catching up with the Payload options. Sometimes you might want a 'quick and simple' download, and sometimes you might want the binary file copied onto the sd card. Attached is a screenshot of compile options in the IDE.

    Kyedos is the program in the EEPROM, but payload reboots the prop and copies the file to ram without overwriting the EEPROM. So this is a perfect solution as you can do multiple downloads during the debugging process but not overwrite Kyedos.
    986 x 410 - 50K
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-10 21:03
    Next step is getting some sd card access working. I presume that 'under the hood', Catalina allows access to the sd card files?

    I've tried to find some code to do file access, as I think this is already included in the .h libraries. However, I think this example may be a different dialect of C to Catalina, as it is producing multiple errors:
    #include <stdio.h>
    #include <stdlib.h>
    
    void main ()
    {
           printf("Hello, World!\n");
           FILE *fp;
           if((fp = fopen("test", "rb")) == NULL) 
           {
                  printf("Cannot open file.\n");
           }
           if( fclose( fp )) 
           {
                  printf("File close error.\n");
           }
           while (1);                                                    // Prop reboots on exit from main()!
    }
    

    It is probably something obvious like a * not being in the right place, so one question is what is wrong with this code, but a more generic question is to ask, is there a link to an online manual or similar which describes the dialect of Catalina (? is it C89).

    I tried another example, and the keyword FILE seems to be one of the problems.
    void main (int argc, char *argv[])
    {
    
       FILE *file_p;
       char FileName[20] = "Testfile.txt";
    
       file_p = fopen(FileName, "r");
       if (file_p)
       {
          fclose(file_p);
       }
    

    Addit: I found the file test_sd.c, but this appears to be working at the sector level rather than at the file level. I'm looking for code that can open a text file, read in n bytes, and close the file. I'm hoping this can be done!
  • RossHRossH Posts: 5,593
    edited 2010-12-10 23:20
    Hi Dr_Acula,

    In C89, variable declarations cannot follow other statements. You just need to move the FILE *fp statement. Try:
    #include <stdio.h>
    #include <stdlib.h>
    
    void main ()
    {
           FILE *fp;
    
           printf("Hello, World!\n");
    
           if((fp = fopen("test", "rb")) == NULL) 
           {
                  printf("Cannot open file.\n");
           }
           if( fclose( fp )) 
           {
                  printf("File close error.\n");
           }
           while (1); // Prop reboots on exit from main()!
    }
    
    Compile this with the command:
    catalina -D DRACBLADE -x2 -lcx test.c
    
    Should run ok on your board. Note that you will have to load the resulting binary with Catalyst, or use Payload and load the XMM loader.

    The program test_sd.c just demontrates the SD card plugin. There is a another demo program called test_stdio_fs.c that you should look at.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-11 14:31
    Thanks++ for that. With SD card access it will be possible to move binary blobs into the program and then into hub and then do a cogstart. I need to think about whether it is better to be debugging the pasm part in the Proptool or whether to do it as another window in the IDE. Probably the proptool for the moment.

    Thanks again for the rapid support!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-12 02:34
    Now we are having fun!
    #include <stdio.h>
    
    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 main ()
    {
           FILE *fp;
           char x[10]="ABCDEFGHIJ";
           clearscreen();
           printf("Test files\n");
           if((fp = fopen("test.txt", "w")) == NULL) 
           {
                  printf("Cannot open file.\n");
           }
           fwrite(x, sizeof(x[0]), sizeof(x)/sizeof(x[0]), fp);            // write binary file from array
           if( fclose( fp )) 
           {
                  printf("File close error.\n");
           }
           printf("Created file test.txt\n");
           while (1);                                                    // Prop reboots on exit from main()!
    }
    

    My Propeller is set up with KyeDOS in EEPROM. The above program is in the vb.net IDE. All I do is hit F12 and it compiles, downloads and runs. It prints a message on the screen so all is ok.

    To test it worked, I hit the reset button which boots back into KyeDOS and then dump the file using the CAT command.
    **** KyeDOS SD card operating system by Kwabena W. Agyeman/J. Moxham ****
    Type Help for command listing
    Use the SPIN command to run the following binary Spin programs/emulations:
    NEW.BIN
    OTHELLO.BIN
    NEW1.BIN
    TEST_TER.BIN
    CP.BIN
    DBASIC.BIN
    LS.BIN
    MKDIR.BIN
    MV.BIN
    PCOM.BIN
    PINT.BIN
    RM.BIN
    RMDIR.BIN
    SST.BIN
    SCREEN.BIN
    CLS.BIN
    COGTEST.BIN
    TINY.BIN
    T.BIN
    CATLYST1.BIN
    CATLYST2.BIN
    CPM.BIN
    CPM1.BIN
    KYEDOS.BIN
    PHOTO.BIN
    
    SD>cat test.txt
    
    ABCDEFGHIJEnd of File
    
    
    SD>
    

    So what we have here is access to the vast library of C programs. I'm able to find examples of code on the internet to do the things I need to do, eg the above code came from http://www.cprogramming.com/tutorial/cfileio.html

    So if you can write binary files, you can read them. You can also xmodem the source code to the propeller. Now I'm starting to get all the building blocks together to be able to write C code and PASM code at the same time, and debug them together.

    Then we can start thinking about new libraries of Obex code that contains C and PASM.

    Great fun!

    BTW Ross, as soon as I started adding in the fopen and fclose code, it ran out of code space in LMM. Is this because this triggered stdio to add all of the functions, and this ran out of memory? If so, no problem, because we have XMM and for me it just means hitting F12 instead of F10 to compile, but is this a fundamental limitation of the propeller memory space in LMM?

    See attached - this is the PASM part of the program. What do you suggest for the compiler? Would this program compile as it is? Does the proptool have a command line compiler? Or would homespun be better, given that homespun is already part of the Catalina package?
    1024 x 768 - 57K
  • RossHRossH Posts: 5,593
    edited 2010-12-12 13:26
    Dr_Acula wrote: »
    BTW Ross, as soon as I started adding in the fopen and fclose code, it ran out of code space in LMM. Is this because this triggered stdio to add all of the functions, and this ran out of memory? If so, no problem, because we have XMM and for me it just means hitting F12 instead of F10 to compile, but is this a fundamental limitation of the propeller memory space in LMM?

    That's correct - you can use files but not use stdio if you instead use the catalina file and I/O functions - see catalina_fs.h and catalina_hmi.h
    Dr_Acula wrote: »
    See attached - this is the PASM part of the program. What do you suggest for the compiler? Would this program compile as it is? Does the proptool have a command line compiler? Or would homespun be better, given that homespun is already part of the Catalina package?

    Just use homespun.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-12 15:19
    Ok, homespun it is.

    Next step is to add a tiny bit of code, compile it and load it from within a catalina program. Then I can start thinking about gluing it together with your demo code. There are a few options, and the one that I'd like to do is where there is minimal overhead in the PASM part, so that the C part of the code does the registering.

    Down the track, one can think about combining PASM and C code together in one big file. The IDE could go through and preprocess all the DAT segments, compile them separately with homespun and xmodem them to the sd disk. Then compile the C program. But it should be possible to associate them together, eg if a PASM blob of code is given a name then the C program can know this name as well, and also which cog it is going to get loaded into, so it can then add the code that registers the PASM bit of code. And if the C program knows which cog is running that code, it knows which registry entry to use. Hence it should be possible to glue PASM and C code in the same way Spin and PASM and glued together. Except that it is not quite the same, because you can do things you can't do in Spin very easily, like have 50 PASM blobs of code being loaded in and out of cogs on the fly.

    I'd like to have a menu option in the IDE that adds a generic PASM blob of code into a C program. It might ask a few simple questions first, like which cog you want to load this into. It could then pre-write the minimum code in PASM, as well as the associated C functions to load and unload the binary - eg fopen, fclose, fread, moving the binary data from XMM to hub, and then loading it into a cog. The IDE can keep track of this so it only adds these functions once to the program.

    Ultimately, the aim is to automate and simplify the writing of PASM code within Catalina so it is easier to translate existing Obex code. Then we can start doing clever things like loading up a text VGA driver, then replacing it with a graphics color driver, then replacing it with a grayscale driver all within the same program.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-13 15:52
    1) Is it better to continue catalina discussions here or on the 2.8 thread?

    2) Re the comment on another thread about not combining Spin and C in one program, is it ok to combine PASM and C?
    /* Traditional first C program */
    
    #include <stdio.h>
    
    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 main ()
    {
           clearscreen();
           printf("Hello, World!\n");
           while (1);                                                    // Prop reboots on exit from main()!
    }
    
    #pasm
    #pasm filename = "cogtest1.bin"
    
    
    DAT
    
    start		org	0
    
    		fit 496
    
    #endpasm
    

    i) This will only run in an IDE that knows how to process this
    ii) The IDE can have an option to save as pure C and strip out the DAT parts
    iii) Even though this looks like it is one program, the IDE (or any other simple text processing program) would strip out the #pasm parts of code and compile them separately using Homespun, and transfer them to the sd card.
    iv) This is not the same as inline pasm code that is run from XMM. This code is being loaded and run on cogs and is running at full speed.
    v) There is some C code not shown - eg the code to open a file on the sd card, code to move the data to hub, and code to load the cog. For multiple reloads, eg switching between color and grayscale vga drivers, one might do a single load from sd card to an array, then move that array into hub as needed.

    Why do this?

    Well, I'm thinking that if you have the pasm part and the C part all on the same screen, it is going to be a lot easier to modify Obex code so it will run on catalina. You can have the Spin part in a comment section and work on translating that to C, and at the same time, have the original DAT part in a comment, and work on translating that to the DAT code that catalina understands (by adding the header part that tells it where the registry is).

    Binding them together is possible because the C program will have a line "fopen" and then the name of the file which appears in the DAT section.

    Thoughts would be most appreciated, both regarding the general concept, and also specifics like the use of # vs other syntax.

    Would there be any conflicts for instance with the syntax used for inline XMM pasm code?
  • RossHRossH Posts: 5,593
    edited 2010-12-13 17:00
    Dr_Acula wrote: »
    1) Is it better to continue catalina discussions here or on the 2.8 thread?
    You can continue it here - the 2.8 thread is currently about issues specific to the beta release of 2.8.
    Dr_Acula wrote: »

    2) Re the comment on another thread about not combining Spin and C in one program, is it ok to combine PASM and C?
    Well, it's entirely up to you - but if you are asking my opinion, then I'd say emphatically not. The main problem is that you are essentially defining yet another new language that is neither C nor PASM - and this new language can only be successfully compiled and loaded using your tool.

    By all means write a tool that automates the process of integrating C and PASM (and it would be great if it included both "vanilla" PASM functions loaded into cogs and "LMM" PASM functions executed by a kernel) - but at the end of the day, the tool should spit out only binary blobs, or pure PASM and C source files. It can then automate the process of building the final binary (and supporting blob files) from these artefacts, but should not do anything that you could not also do yourself using the PASM compiler and the C compiler manually.

    If I were doing what you are trying to do, I would keep the C and PASM files separate, but have the tool keep track of the fact that they were related by storing this information in a separate "project" file. This project file should also identify any plugins that should be stored in separate "blobs" because you want to load them dynamically. When the time comes to compile the whole program, the tool could use the information in the project file to generate a library containing any LMM PASM functions, a plugin containing any "vanilla" PASM functions, generate all the necessary binary blobs, and compile the main against a suitable target.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-13 21:01
    Ok, I'll keep them separate. They are currently on different tabs on the IDE so it is very quick moving between C and PASM. Your idea of a 'project file' is brilliant. It would bind everything together, but even if you lost the project file, you could still build it all manually.

    Compilation of the PASM program is now a one button exercise that runs Homespun with the -b and -d options.

    -b because it is being saved as a binary file (not as an eeprom file), and -d as it creates a nice printout of what the compiler did.

    Compile and download using xmodem to Kyedos of this program is one button press and takes only 4 seconds!

    I've named these binary blobs with a .cog extension, to distinguish from .bin extensions in KyeDOS that will run a complete program.

    I am working on the absolute minimal program. It seems one PUB with a coginit is needed, even if this ends up creating dummy code.
    The CON variables may not be needed. So - this is my test program and I think the CON values can be deleted - but on the other hand, many pasm programs are likely to reference the clock?
    CON
      _clkfreq = 80_000_000
      _clkmode = xtal1 + pll16x
    
    PUB Main
        coginit(1,@cogstart,0)  ' cog 1, cogstart, dummy value
    
    DAT
                      org 0
    cogstart 
                      mov testvariable,#65   ' test value A
                      jmp #cogstart
    
    
    testvariable      long    $7530           ' test memory location 30000
                      fit 496
    

    and homespun has produced a .lst file like this
    0000: 00 b4 c4 04 ' Frequency: 80000000 Hz
    0004: 6f          ' XTAL mode
    0005: 35          ' Checksum
    0006: 10 00       ' Base of program
    0008: 2c 00       ' Base of variables
    000a: 34 00       ' Base of stack
    000c: 24 00       ' Initial program counter
    000e: 38 00       ' Initial stack pointer
    
    '******************************************************************************
    '                                COGJECT.SPIN                                  
    '******************************************************************************
    
    '=================================== CONs =====================================
    _clkfreq = 80000000
    _clkmode = 1032
    '=============================== Object Header ================================
    0010: 1c 00 02 00 ' 28 bytes, 2-1 methods, 0 object pointers
    0014: 14 00 00 00 ' ptr #1 to $0024: PUB Main (locals size: 0)
    '================================ DAT Section =================================
    0018(0000):             '                   org 0
    0018(0000):             ' cogstart 
    0018(0000): 41 04 fc a0 '                   mov testvariable,#65   ' test value A
    001c(0001): 00 00 7c 5c '                   jmp #cogstart
    0020(0002): 30 75 00 00 ' testvariable      long    $7530           ' test memory location 30000
    '============================ Method #1: PUB Main =============================
    'PUB Main
    '------------------------------------------------------------------------------
        coginit(1,@cogstart,0)  ' cog 1, cogstart, dummy value
    '------------------------------------------------------------------------------
    0024: 36             PUSH#1	
    0025: c7 08          PUSH#.L	OBJ+8
    0027: 35             PUSH#0	
    0028: 2c             COGISUB	
    0029: 32             RETURN	
    002a: 00 00       
    002c: ff ff f9 ff ff ff f9 ff 
    

    now, if I look at the binary file in the PropTool, the top line is gray and says "initialization". So, where does the code start, is it at hex 0x10. Or does it start at 0x18

    So this .cog file gets loaded into an array in XMM and then copied over to .hub. Then I need to move it into the cog.

    If so, and we are loading this into a cog, does the C function
    int _coginit(int par, int addr, int cogid);
    
    strip out the first 16 bytes or do I need to do that?
  • RossHRossH Posts: 5,593
    edited 2010-12-13 21:31
    Dr_Acula wrote: »
    I am working on the absolute minimal program. It seems one PUB with a coginit is needed, even if this ends up creating dummy code.

    One PUB method is always needed - but if all you want is the binary blob there is no overhead, as this SPIN method will be stripped out later (see below).
    Dr_Acula wrote: »
    The CON variables may not be needed. So - this is my test program and I think the CON values can be deleted - but on the other hand, many pasm programs are likely to reference the clock?
    You might consider always including the Catalina_Common.spin object so the PASM code can access any platform specific values it needs.
    Dr_Acula wrote: »
    now, if I look at the binary file in the PropTool, the top line is gray and says "initialization". So, where does the code start, is it at hex 0x10. Or does it start at 0x18

    If so, and we are loading this into a cog, does the C function
    int _coginit(int par, int addr, int cogid);
    
    strip out the first 16 bytes or do I need to do that?

    Remember that what you are looking at in the listing is a representation of the binary output format, not the program. The first 0x10 bytes are the clock frequency and other data required to load and start the program. The SPIN program itself starts at 0x10, but the first part of this is also 'housekeeping' data - the actual PASM object code starts at byte 0x18 in the object format in this example - but the org statement int he PASM code means this code assumes it will be relocated to start at location 0x0 (which would be the case when it is loaded from location 0x18 into cog RAM using a coginit statement). You should not need to worry about any of this if you just want the blob - just leave it up to spinc to extract the blob of PASM code you need.

    Run spinc on the resulting binary to see what I mean:
    spinc my_prog.binary > myprog.h
    

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2010-12-13 23:07
    Fantastic.

    Ok, I'll put in the catalina_common object as part of the New button.

    Spinc - is that a little program to extract out the cog code? Catalina really is a treasure trove of goodies. Can't wait to get home and test this out!
  • RossHRossH Posts: 5,593
    edited 2010-12-13 23:54
    Hi Dr_Acula,

    The spinc utilty was written by jazzed. I just include it in Catalina because it's so useful - I used to extract the binary code by cutting and pasting!

    Ross.
Sign In or Register to comment.