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

Catalina 2.9

1235715

Comments

  • RossHRossH Posts: 5,547
    edited 2011-02-14 19:49
    Dr_Acula wrote: »
    Hi Ross,

    Can you explain the library options a little more? There is -lc, which I think is some essential libraries. -lm is maths. I added that to a program, but it doesn't change the compile size. Is there a memory cost with adding these libraries?

    In general terms, which libraries go with which #include files?

    Hi Dr_A,

    There's not an exact relationship between the individual libraries and header files. Catalina separates the C library into two parts (libc and libm) primarily for reasons of implementation convenience (which I'll go into below). Together, these libraries contain all the ANSI-standard library functions, plus all the catalina-specific library functions.

    So while it is true to say that maths.h defines the functions in libm, and that all the other header files (stdio.h, string.h etc, plus catalina_cog.h, catalina_hmi.h etc) define the functions in libc, there are also some functions in libc that require the functions from libm. This means you may have to include libm even when you don't yourself use any of the functions from maths.h.

    However, it is important to note that there is no cost in linking a program with a library if the program does not use any of the functions the library contains - even if you have included the headers! So you can (if you want to) always include all the headers, and always link all your programs with both -lc and -lm. The main reasons for not doing so is that it means you cannot define your own version of any function that exists anywhere in any of the libraries (e.g. you couldn't define your own printf() function) and also processing all the libraries each time slows down the compilation.

    So why split the library into two? Well, it's mainly for convenience on my part when I'm maintaining the libraries - there are 4 variants of libc (i.e. libc, libci, libcx & libcix) and 3 variants of libm (i.e. libm, libma & libmb). The purpose of each of these variants is given in the Catalina Reference Manual (pp 68). By separating them this way I have to manage only 7 different libraries (4 + 3). If I didn't separate them, I would have to manage 12 different libraries (4 * 3).

    To put it another way, consider that I may soon add a new maths library variant (which I will probably call libmc) that implements all the floating point functions using a single cog (by using lonesocks neat single-cog F32 implementation instead of the two cogs that run Float32_A and Float32_B). Because I have kept the maths library separate, I will only have to add one new library. If I had combined libm with libc, I would now have to add (and manage) four new libraries - and also the names would become very unwieldy (e.g. I would have to call them something like libcmc, libcimc, libcxmc & libcixmc - not to mention libcma, libcmb etc etc).

    Ross.
  • RossHRossH Posts: 5,547
    edited 2011-02-14 20:12
    Hi Dr_A,

    Just saw the second half of your post - yes, itoa() is not ANSI standard - but did you include the version I posted earlier in this thread?

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-14 20:34
    Thanks - that explanation is very useful. So - if there is no cost with these -lc and -lm commands (apart from compilation speed), would it make sense to use lcx all the time? (I see you have lcix as well - is this something new since v 2.6?) (addit - just read lcix on the first post, I think I'll go with lcx for the moment)

    Re itoa, which post was that?
  • RossHRossH Posts: 5,547
    edited 2011-02-14 20:39
    Dr_Acula wrote: »
    Thanks - that explanation is very useful. So - if there is no cost with these -lc and -lm commands (apart from compilation speed), would it make sense to use lcx all the time? (I see you have lcix as well - is this something new since v 2.6?)
    No, you really don't want to do that - the versions of some functions in libcx are much larger than the versions in libc, because they include code for aribitrary stream handling (necessary for file access), whereas the libc versions only support stdin, stdout and stderr.

    EDIT: Yes, libcix is new - it is an integer only library but with file system support.
    Dr_Acula wrote: »
    Re itoa, which post was that?

    Post #113

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-14 20:44
    Yes, found that the hard way just now when it tried to fit a 90k program into a 32k propeller!

    Thanks for post 113 - I missed that one. I will add that to the list that the preprocessor does.

    Downloading 2.9 now - so we are on the same page!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-14 21:02
    Ok, ported over to 2.9

    Everything is working except for one thing with Payload. On the old version there was a program "XMM.BINARY" that was c:\program files\catalina\utilities. I was doing all the coding from the demo folder, so one thing I was doing every time was copy xmm.binary to c:\program files\demos\xmm.binary

    Then I would run Payload XMM myfile

    What is the new way of running payload?
  • RossHRossH Posts: 5,547
    edited 2011-02-14 21:14
    Hi Dr_A,

    I've not changed anything - you just need to rebuild the utilities - go to the utilities folder and execute the command:
    build_all DRACBLADE
    
    This will build the xmm.binary file, which you can then copy to wherever you like.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-14 21:28
    Ok, fixed it. This one might come up again so I might think about a way of doing that automatically. Maybe somewhere in the $COMMAND section of the basic program you have a definition of the hardware. Hmm - it would make sense to only define that once, then some of the other things change further down - ideally automatically. It would be nice to avoid too many #ifdefs. I'll have to think about that.

    Maybe a $HARDWARE section. This then changes the command line, and also maybe reruns the build_all.

    Meanwhile...

    When the BIN$() function is run in basic it creates this line:
       itoa(number,strtmp,2);
    

    Where there are three variables that are passed - the value, the string location and the radix (base).

    Your example I think passed only one variable. And there are examples on the internet that have two variables, not three.

    I tried looking for some code but couldn't find any with three variables. But I do have this working in TinyC. So does that mean that TinyC has the code for the flavour of this function? It must do!

    Where would one go looking for such code? I tried a few .h files but they only seem to have skeleton code. stdlib.h has the definition char* itoa (int, char*, int);
  • RossHRossH Posts: 5,547
    edited 2011-02-14 21:44
    Dr_Acula wrote: »
    Where would one go looking for such code? I tried a few .h files but they only seem to have skeleton code. stdlib.h has the definition char* itoa (int, char*, int);

    Google "itoa source code" (plus one edit by me!):
    /*
     * COPYRIGHT:   See COPYING in the top level directory
     * PROJECT:     ReactOS system libraries
     * FILE:        lib/crtdll/stdlib/itoa.c
     * PURPOSE:     converts a integer to ascii
     * PROGRAMER:   
     * UPDATE HISTORY:
     *              1995: Created
     *              1998: Added ltoa Boudewijn Dekker
     */
    /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
    #include <errno.h>
    #include <stdlib.h>
    
    char *
    itoa(int value, char *string, int radix)
    {
      char tmp[33];
      char *tp = tmp;
      int i;
      unsigned v;
      int sign;
      char *sp;
    
      if (radix > 36 || radix <= 1)
      {
        __set_errno(EDOM);
        return 0;
      }
    
      sign = (radix == 10 && value < 0);
      if (sign)
        v = -value;
      else
        v = (unsigned)value;
      while (v || tp == tmp)
      {
        i = v % radix;
        v = v / radix;
        if (i < 10)
          *tp++ = i+'0';
        else
          *tp++ = i + 'a' - 10;
      }
    
      if (string == 0)
        string = (char *)malloc((tp-tmp)+sign+1);
      sp = string;
    
      if (sign)
        *sp++ = '-';
      while (tp > tmp)
        *sp++ = *--tp;
      *sp = 0;
      return string;
    }
    
    P.S. You can probably now see why itoa is not an ANSI function - every man and his dog had his own version!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-15 14:47
    Woot - this works! I now have ten million in binary on the propeller screen.

    I see there was some other code by the same author that did things like convert floating point numbers to strings. Do you think this is likely to come up as being needed?

    Some C questions:

    1)

    Does it matter where the * goes? I have these two lines in the function declares but I think they do the same thing in terms of declaring the array.
    char *strupr(char *);
    char*  BCX_TmpStr(size_t);
    

    And things like 'char*' vs 'char *'

    2)
    There seem to be several variables for a 32 bit long. I've already ended up with DWORD and UINT.

    I'm pondering whether to also add "LONG". The downside is that it is a vague declaration. But the upside is that translating from spin might be easier. Your sage advice would be most appreciated.

    3)

    The resolution of double numbers
    static double  number1;
    
    number1=1.2345000000000000000;
    printf("% .15G\n",(double)number1);
    

    on TinyC this prints 1.2345
    but on the propeller it prints 1.234500050554474

    and other mathematical functions such as Sin seem to disagree from about the 8th decimal place. Should one use Float instead? Using float at prints 1.2345

    Hmm using Float, TinyC and Catalina and Windows Calculator give different numbers for Sin. TinyC and Catalina are closer to each other than Windows Calc. Maybe it does not matter beyond 8 decimal places?

    4)

    I just noticed this thread http://forums.parallax.com/showthread.php?129593-NEW-32MB-SDRAM-Module-for-Propeller-Platform

    Can Catalina run on this?

    5) Declaring sevaral varialbe. This code works:
    void TestDim (void)
    {
      static   int      a;
      static   int      b;
      memset(&a,0,sizeof(a));
      memset(&b,0,sizeof(b));
    }
    

    but this code (ie the output from BCX) gives an error
    void TestDim (void)
    {
      static   int      a;
      memset(&a,0,sizeof(a));
      static   int      b;
      memset(&b,0,sizeof(b));
    }
    

    Addit: I think I can fix this with a bubble sort - if static below memset then swap.... yes, a bubble sort on this works.
  • RossHRossH Posts: 5,547
    edited 2011-02-15 17:33
    Dr_Acula wrote: »
    Woot - this works! I now have ten million in binary on the propeller screen.

    I see there was some other code by the same author that did things like convert floating point numbers to strings. Do you think this is likely to come up as being needed?
    Hard to say - but at least you now know where to get the code from if it is needed.

    Once you start programming in C, the internet is one gigantic OBEX!
    Dr_Acula wrote: »

    Some C questions:

    1)

    Does it matter where the * goes? I have these two lines in the function declares but I think they do the same thing in terms of declaring the array.
    char *strupr(char *);
    char*  BCX_TmpStr(size_t);
    
    And things like 'char*' vs 'char *'
    No - there is no differene between 'char*' and 'char *'
    Dr_Acula wrote: »


    2)
    There seem to be several variables for a 32 bit long. I've already ended up with DWORD and UINT.

    I'm pondering whether to also add "LONG". The downside is that it is a vague declaration. But the upside is that translating from spin might be easier. Your sage advice would be most appreciated.
    Yes, in C many declarations are a bit vague. You're generally better advised to add your own - but until Parallax decide to come out with a 64 bit Propeller, you're probably safe in simply assuming that both ints and longs are 32 bits.
    Dr_Acula wrote: »


    3)

    The resolution of double numbers
    static double  number1;
    
    number1=1.2345000000000000000;
    printf("% .15G\n",(double)number1);
    
    on TinyC this prints 1.2345
    but on the propeller it prints 1.234500050554474

    and other mathematical functions such as Sin seem to disagree from about the 8th decimal place. Should one use Float instead? Using float at prints 1.2345

    Hmm using Float, TinyC and Catalina and Windows Calculator give different numbers for Sin. TinyC and Catalina are closer to each other than Windows Calc. Maybe it does not matter beyond 8 decimal places?


    All floating point arithmetic is approximate. In Catalina, float and double are the same - an IEE 754 binary32 number. Other complers may use a 64 bit float as a double. And I wouldn't trust the Windows Calculator to even add 2+2 correctly!

    IEEE 754 binary32 is accurate to around 7 digits.
    Dr_Acula wrote: »
    Not yet - but soon!
    Dr_Acula wrote: »

    5) Declaring sevaral varialbe. This code works:
    void TestDim (void)
    {
      static   int      a;
      static   int      b;
      memset(&a,0,sizeof(a));
      memset(&b,0,sizeof(b));
    }
    
    but this code (ie the output from BCX) gives an error
    void TestDim (void)
    {
      static   int      a;
      memset(&a,0,sizeof(a));
      static   int      b;
      memset(&b,0,sizeof(b));
    }
    
    Addit: I think I can fix this with a bubble sort - if static below memset then swap.... yes, a bubble sort on this works.

    Yes - same problem as we've discussed previously - i.e. in C98 you cannot mix declarations and statements. The declarations must come first. You need to split the declarations from the initialization and put them first.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-15 18:24
    And I wouldn't trust the Windows Calculator to even add 2+2 correctly!

    te he :)

    So Catalina will soon be running with 32Mb of memory?? That will be amazing.

    And... it means we will have Basic with 32Mb as well.

    The XMM and LMM code is now working with all the instructions I can throw at it. Maybe the odd one is going to cause problems, but I have generic fixes for replacing code with new code, for sorting the order of code and for changing syntax.

    My project today was to get the tiny version working. I noticed there was a new #include "#include <catalina_cog.h>" in your led test code, so I dropped that in. The syntax for flashing a led is now very similar to Bean's basic and to the Picaxe and Basic Stamp. And we can always change it if needed if people would prefer PINS instead of _outa().

    With the code below, inline C code works fine, so there is no need to change your _outa() code to something different.

    The sleep routine is a little more complex and was copied from an existing Spin routine.

    This Basic program compiles to 3k. So this is a great way to get started on the propeller with flashing leds, then to move up to displays/keyboards, and then to external memory.

    And all thanks to the clever code in Catalina!
    ' BCX help file http://www.bcxgurus.com/help/index.html
    ' Minimialist version for the Propeller for flashing leds etc
    #include <catalina_cog.h>
    $COMMAND
    catalina -lci -D DEMO -D NO_HMI -D NO_ARGS  -D ALTERNATE -D NO_FLOAT
    ' library ci, demo board, no vga, no mouse, no keyboard, no floating point
    $COMMAND
    ' ************ main **********
    Do
      High(0)              ' pin 0 high (pin 1 on physical chip)
      Sleep(1000)          ' sleep this number of milliseconds
      Low(0)               ' pin 0 low
      Sleep(1000)          ' sleep
    Loop
    ' ********** end main ********
    
    Sub High(Pin as Integer)
      Dim Mask As uint
      Mask = Mask Or (1 << Pin) ' shift and logical OR
      _dira(Mask, Mask)
      _outa(Mask,0xFFFFFFFF)' set just this pin high
    End Sub
    
    Sub Low(Pin as Integer)
      Dim Mask As uint
      Mask = Mask Or (1 << Pin) ' shift and logical OR
      _dira(Mask, Mask)
      _outa(Mask,0x00000000)' set just this pin low
    End Sub
    
    Sub Sleep(ByVal milliseconds As int)
      Dim clkcycles As uint ' uint = long
      clkcycles = (_clockfreq() / 1000) * milliseconds ' calculate the number of cycles
      clkcycles -= 4296 ' subtract 4296
      If clkcycles < 381 Then clkcycles = 381 ' minimum
      _waitcnt(clkcycles + _cnt())
    End Sub
    

    and the resulting C code:
    #include <catalina_cog.h>
    
    // *************************************************
    //                Typedef
    // *************************************************
    
    typedef unsigned char UCHAR;
    typedef unsigned long DWORD;
    typedef unsigned long UINT;
    
    
    
    // *************************************************
    //               Standard Macros
    // *************************************************
    
    #define BOR |
    
    
    // *************************************************
    //               User Prototypes
    // *************************************************
    
    void    High (int);
    void    Low (int);
    void    Sleep (int);
    
    
    // *************************************************
    //            User Global Initialized Arrays
    // *************************************************
    
    
    // *************************************************
    //                  Main Program
    // *************************************************
    
    int main(int argc, char *argv[])
    {
    for(;;)
      {
        High(0);
        Sleep(1000);
        Low(0);
        Sleep(1000);
      }
      return 0;   //  End of main program
    }
    
    // *************************************************
    //                 Runtime Functions
    // *************************************************
    
    
    // ************************************
    //       User Subs and Functions
    // ************************************
    
    
    void High (int Pin)
    {
      static   UINT     Mask;
      memset(&Mask,0,sizeof(Mask));
      Mask=Mask BOR (1<<Pin);
      _dira(Mask,Mask);
      _outa(Mask,0xFFFFFFFF);
    }
    
    
    void Low (int Pin)
    {
      static   UINT     Mask;
      memset(&Mask,0,sizeof(Mask));
      Mask=Mask BOR (1<<Pin);
      _dira(Mask,Mask);
      _outa(Mask,0x00000000);
    }
    
    
    void Sleep (int milliseconds)
    {
      static   UINT     clkcycles;
      memset(&clkcycles,0,sizeof(clkcycles));
      clkcycles=(_clockfreq()/1000)*milliseconds;
      clkcycles-=4296;
      if(clkcycles<381)
        {
          clkcycles=381;
        }
      _waitcnt(clkcycles+_cnt());
    }
    
  • RossHRossH Posts: 5,547
    edited 2011-02-15 18:45
    Hi Dr_A,

    Greate work! You've probably done more to promote the use of Catalina (and prove that C can do useful things on the Propeller) than I have ever managed! :smile:

    One thing - your 3k is presumably the total file size? Not sure exactly what options you use, but when I compile it I get a code size of 888 bytes (i.e. 222 instructions).

    Ross.

    EDIT: Just noticed your compile options are specified at the top of the file. Using these (but adding in -O3 for the Catalina Optimizer!) I get:
    code = 888 bytes
    cnst = 12 bytes
    init = 4 bytes
    data = 16 bytes
    file = 3152 bytes
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-15 19:01
    Yes, I get that tiny size too. Not sure which one is the "real" file size and no doubt there are others who can debate that as a <fun> exercise for the reader. (not me though!).

    Pretty amazing. I think this could open up the use of C to Basic programmers, because you can drop in C functions into the code.

    Re the LMM model, I've coded the example like this:
    #include <stdio.h>
    
    $COMMAND
    catalina -lc -lm -D DEMO -D HIRES_VGA
    ' library c, library maths, demo board, hires vga, mouse, keyboard
    $COMMAND
    

    I figure that those that want to flash leds won't need stdio. And those that want a keyboard (Line Input is the same as scanf) and a display are going to want stdio. That comes to a program about 25k so not a huge amount of room for code space.

    I guess people flashing leds have lots of room for code and maybe those wanting keyboards/displays probably are more likely to want external memory.

    With the new 32Mb module it looks like Gadget Gangster are doing that as an add-on board, which makes a lot of sense.

    Do you have a C3? I presume this will port across to the C3 but always worth double checking on real hardware. I'm thinking of the changes to the command line that one might have for the C3, GG, Dracblade, ClusoBlades etc. I coded the miniature and LMM examples with "DEMO" as that is probably the commonest board out there.

    Just did a crude speed test, this is running basic about 300x faster than a Picaxe, and about half the speed of an Android running Basic4Android (with considerably less power consumption than the Android).

    BTW - the latest dracblade boards have a mouse now - pins 24 and 25. I think the earlier versions of the dracblade code have "no mouse"

    Maybe time for a beta release now?
  • RossHRossH Posts: 5,547
    edited 2011-02-15 19:25
    Dr_Acula wrote: »
    I figure that those that want to flash leds won't need stdio. And those that want a keyboard (Line Input is the same as scanf) and a display are going to want stdio. That comes to a program about 25k so not a huge amount of room for code space.

    I guess people flashing leds have lots of room for code and maybe those wanting keyboards/displays probably are more likely to want external memory.
    Don't forget there is a "half way house" between just flashing LEDs and having full stdio support - i.e. all the HMI functions defined in catalina_hmi.h. All these functions are built into the HMI cog and take no extra code space.

    For a real-world example of their use, see the game othello.c in the demo directory - this is the full game, with screen output and keyboard input, and it has a code size of under 6k!
    Dr_Acula wrote: »
    With the new 32Mb module it looks like Gadget Gangster are doing that as an add-on board, which makes a lot of sense.

    Do you have a C3? I presume this will port across to the C3 but always worth double checking on real hardware. I'm thinking of the changes to the command line that one might have for the C3, GG, Dracblade, ClusoBlades etc. I coded the miniature and LMM examples with "DEMO" as that is probably the commonest board out there.
    Yes I have a C3 - everything on that is supported except for the Flash RAM - still working on that one
    Dr_Acula wrote: »
    Just did a crude speed test, this is running basic about 300x faster than a Picaxe, and about half the speed of an Android running Basic4Android (with considerably less power consumption than the Android).
    Don't forget that with Catalina on the Propeller you could have C (and now Basic!) running on all 8 cogs - so theoretically you can have up to 4 times thes speed of the Android!
    Dr_Acula wrote: »
    BTW - the latest dracblade boards have a mouse now - pins 24 and 25. I think the earlier versions of the dracblade code have "no mouse"
    Ok - I'll update the Catalina targets.
    Dr_Acula wrote: »
    Maybe time for a beta release now?

    Go for it! The best way to find bugs or problems is have someone else try and get your code working!

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-15 23:43
    That all sounds great.

    Humanoido has posted a request for an eprom version of the program. I guess the programs I've been writing are "F10", ie download to ram. He is looking for a program that boots from eeprom, ie an "F11" program.

    I'm sure this is just a command line option (something to do with -e).

    So I'm thinking two examples:

    1) - the minimal led flashing program. Send that to eeprom.
    2) The LMM program you mentioned above with HMI in cogs. Send that to eeprom.

    What would be the command line parameters for these? (assuming a demo board)

    Addit - I think that is a payload command -e

    Addit: - solved the eeprom download.

    Reading through the manual re the HMI. I need to think about things like "if the HMI plugin is present, then search through the C program and replace printf with t_string"
  • RossHRossH Posts: 5,547
    edited 2011-02-16 03:51
    Dr_Acula wrote: »
    That all sounds great.

    Humanoido has posted a request for an eprom version of the program. I guess the programs I've been writing are "F10", ie download to ram. He is looking for a program that boots from eeprom, ie an "F11" program.

    I'm sure this is just a command line option (something to do with -e).

    So I'm thinking two examples:

    1) - the minimal led flashing program. Send that to eeprom.
    2) The LMM program you mentioned above with HMI in cogs. Send that to eeprom.

    What would be the command line parameters for these? (assuming a demo board)

    Addit - I think that is a payload command -e

    Addit: - solved the eeprom download.

    Reading through the manual re the HMI. I need to think about things like "if the HMI plugin is present, then search through the C program and replace printf with t_string"

    To summarize the EEPROM issue:

    For LMM programs, nothing special is required to compile a program for programming into EEPROM (the -e compiler flag just generates an .eeprom file instead of a .binary file - but in fact you can program either into an EEPROM). To actually load the programinto EEPROM, use the -e flag to payload (which, as I just said, works for both .binary and .eeprom files).

    But for XMM programs the situation is more complex - since the Propeller doesn't include a native loader capable of loading XMM programs from EEPROM, your program has to do so - so you must compile it using the -D EEPROM option. But to load such a program into EEPROM is currently beyond the capabilities of payload (even with the -e option) - instead, you must use a program like the Hydra Asset Manager.

    Confusing, isn't it?

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-16 21:40
    I've got programs up to 32k loading straight from eeprom now.

    Another topic...

    A little while back I was doing some work putting inline pasm code in catalina. The code works, but the issue of memory management in hub ram comes up.

    For a <32k program, I presume the model is similar to Spin. You have a block of code and somewhere in there you allocate an array. You might call it "myarray[100]". When you compile this, it allocates 100 longs of hub ram. You can fill up some of these with some data, set a single variable to point to the start of the array and send that variable to the cogstart. The cog can then read those values into its code, processes them, and can send data back and forth to the main program via that array.

    This is all very logical.

    But - what happens when you run out of code space and start compiling with an external memory model? Presumably you want your code to port across, but it is not so simple. myarray[100] is now sitting in external memory so the cog can't get to it.

    I have been pondering this for some time, thinking about a syntax that can be used for both LMM and XMM programs. (This might be applicable to Zog and Big Spin too).

    Assuming you have an external memory model where the entire program starts at 32k (ie you don't start at zero, though I guess you could do that).

    I wonder if you could rename "myarray[100]" as "hub_myarray[100]

    A LMM program would just see this as another name and ignore it.

    But an XMM program could run a precompiler and collect up all the arrays that start with "hub" and put them all one after the other in hub ram.

    This still isn't a solution though for porting code from LMM to XMM without too many changes, because you now need a function to write to this array, and to read from this array. You can't just say "a = myarray[20]", you have to say a=read(pointer(myarray[],20)) or something similar with * etc. That can be done of course as we have the "peek" and "poke" code we discussed a few weeks back. So the precompiler needs to keep track of where it put all those hub arrays.

    Maybe this is just the price of using external memory? I shall ponder this some more...
  • RossHRossH Posts: 5,547
    edited 2011-02-16 22:07
    Dr_Acula wrote: »

    Another topic...

    A little while back I was doing some work putting inline pasm code in catalina. The code works, but the issue of memory management in hub ram comes up.

    For a <32k program, I presume the model is similar to Spin. You have a block of code and somewhere in there you allocate an array. You might call it "myarray[100]". When you compile this, it allocates 100 longs of hub ram. You can fill up some of these with some data, set a single variable to point to the start of the array and send that variable to the cogstart. The cog can then read those values into its code, processes them, and can send data back and forth to the main program via that array.

    This is all very logical.

    But - what happens when you run out of code space and start compiling with an external memory model? Presumably you want your code to port across, but it is not so simple. myarray[100] is now sitting in external memory so the cog can't get to it.

    I have been pondering this for some time, thinking about a syntax that can be used for both LMM and XMM programs. (This might be applicable to Zog and Big Spin too).

    Assuming you have an external memory model where the entire program starts at 32k (ie you don't start at zero, though I guess you could do that).

    I wonder if you could rename "myarray[100]" as "hub_myarray[100]

    A LMM program would just see this as another name and ignore it.

    But an XMM program could run a precompiler and collect up all the arrays that start with "hub" and put them all one after the other in hub ram.

    This still isn't a solution though for porting code from LMM to XMM without too many changes, because you now need a function to write to this array, and to read from this array. You can't just say "a = myarray[20]", you have to say a=read(pointer(myarray[],20)) or something similar with * etc. That can be done of course as we have the "peek" and "poke" code we discussed a few weeks back. So the precompiler needs to keep track of where it put all those hub arrays.

    Maybe this is just the price of using external memory? I shall ponder this some more...

    This is a compex topic. For a start, Catalina has one LMM model (and one LMM kernel in Catalina_LMM.spin) but two XMM models - SMALL and LARGE (and two corresponding XMM kernels in Catalina_XMM.spin - differentiated by #ifdef LARGE ... #else ... #endif).

    In the SMALL model, all data is in Hub RAM, and all code is in XMM RAM. So in fact this model is not too disimilar to the LMM model - i.e. you know the data will always be in Hub RAM. But the LARGE model is different - in this model global data is in XMM RAM and local data is in Hub RAM. However, the kernel differentiates between the two by address - i.e. any data address less than $8000 is hub RAM any data address equal to or larger than $8000 is XMM RAM.

    So in fact in the XMM LARGE model it would be easier to do what you want - just place your "reserved" data in low Hub RAM (since the stack grows down from the top of Hub RAM). But in the LMM and XMM SMALL models I would have to specifically allow a "reserved" data area - or perhaps allow you to explicitly declare (somehow!) that a C construct should be placed at a specific address.

    I'll give this one some more thought.

    Ross.
  • aaren253aaren253 Banned Posts: 4
    edited 2011-02-16 23:14
    We are cruising Carnival on Oct 9 - Catalina Island and Ensenada, Mex. - I know we are limited because of my kids.
    Do you know if we can leave our kids in Carnival's baby sitting program inside the ship while me and my husband go off for a while?
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-17 01:25
    So in fact in the XMM LARGE model it would be easier to do what you want - just place your "reserved" data in low Hub RAM (since the stack grows down from the top of Hub RAM).

    Now that is a very interesting piece of information.

    Ok, rather than code anything new, let's look at how this works. I think I can see your logic, in that a local function needs fast access to its own variables. And when a function is finished, you can recycle its variables.

    So say we have a function that starts a cog, and it has some data you want to pass to the cog. You call the function, and at the beginning you declare an array, and then you fill it up with some data and then you start the cog.

    But the problem will be when the function ends, because that array gets discarded. Fine if you want to just pass 10 variables to the cog, but not so fine if that array was 20k of hub ram to be used as a graphics buffer.

    Is there a way of keeping that function alive? Maybe a new thread or something?

    Or do you use the small model?

    I wonder how many typical 'variables' one might have. Maybe not very many i,j,k type variables. Hmm, but you might also want to dimension an integer array with 50,000 elements.

    Thinking about that more, if you dimensioned an array of 10,000 elements, you might want more explicit control about where that ended up, regardless of whether you were in big or small mode.

    Is there some C89 way of saying "this array should go in hub and this one in external memory"?

    I guess this might be a bigger ask, but one thing I did code is some pasm routine handling where I explicitly managed the hub ram. Once you get 5 or 10 arrays, and you specifically say how big they are and where they go, you then have to have variables to keep track of that so if you change one at the beginning it automatically moves all the other ones. Better in a way to let the compiler work that out. All you do is say "hub" or "xmm". In a perfect world you could then just look at the pointer to that array so you can move data in and out. Also, when running code, the pointer would tell you where it is as it would be either >=0x8000 or < 0x8000.

    In Basic, one does have the luxury of being able to run a preprocessor and hence tweak the language:
    Dim myarray[10] As Integer <HUB>
    Dim yourarray[10] As Char <XMM>
    

    Easy to search for strings "<HUB>" and "<XMM>" but how would you pass this information to C?
  • RossHRossH Posts: 5,547
    edited 2011-02-17 01:40
    aaren253 wrote: »
    We are cruising Carnival on Oct 9 - Catalina Island and Ensenada, Mex. - I know we are limited because of my kids.
    Do you know if we can leave our kids in Carnival's baby sitting program inside the ship while me and my husband go off for a while?
    Ummm ... I have no idea.

    Are you sure you're posting in the right forum? The Catalina in these threads is a C compiler, and has nothing to do with the Catalina aircraft or Catalina Island.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-17 01:41
    Ross, I think that is Spam - check the byline at the bottom of the post. Also check the poster, 5 posts in as many minutes and none of them make sense. Maybe one for the moderators?
  • RossHRossH Posts: 5,547
    edited 2011-02-17 01:54
    Dr_Acula wrote: »
    Now that is a very interesting piece of information.

    Ok, rather than code anything new, let's look at how this works. I think I can see your logic, in that a local function needs fast access to its own variables. And when a function is finished, you can recycle its variables.
    Exactly - thats why local variables are normally on the stack.
    Dr_Acula wrote: »
    So say we have a function that starts a cog, and it has some data you want to pass to the cog. You call the function, and at the beginning you declare an array, and then you fill it up with some data and then you start the cog.

    But the problem will be when the function ends, because that array gets discarded. Fine if you want to just pass 10 variables to the cog, but not so fine if that array was 20k of hub ram to be used as a graphics buffer.
    True - in this case you would normally use malloc() to allocate the buffer. In the LMM and SMALL XMM memory model this would be Hub RAM. But in the LARGE XMM model this would be XMM RAM.
    Dr_Acula wrote: »
    Is there a way of keeping that function alive? Maybe a new thread or something?
    No - once you exit the function, that's it - but another way to do it (which I use in both the graphics and multithreading support) is to declare the variable as a local variable in the main() function (which never exits - or if it does you no longer care!) then take the address of it and pass it as a pointer to wherever it is needed - this way the variable will always be in Hub RAM (on the stack) in every memory model, and the memory used will be constant for the life of the program.
    Dr_Acula wrote: »
    Or do you use the small model?

    I wonder how many typical 'variables' one might have. Maybe not very many i,j,k type variables. Hmm, but you might also want to dimension an integer array with 50,000 elements.
    You wouldn't normally declare that as a local - at least not on the Propeller!
    Dr_Acula wrote: »

    Thinking about that more, if you dimensioned an array of 10,000 elements, you might want more explicit control about where that ended up, regardless of whether you were in big or small mode.

    Is there some C89 way of saying "this array should go in hub and this one in external memory"?
    No - someone suggested using the C keyword volatile for this - but I disagree, as volatile actually means something completely different.
    Dr_Acula wrote: »
    I guess this might be a bigger ask, but one thing I did code is some pasm routine handling where I explicitly managed the hub ram. Once you get 5 or 10 arrays, and you specifically say how big they are and where they go, you then have to have variables to keep track of that so if you change one at the beginning it automatically moves all the other ones. Better in a way to let the compiler work that out. All you do is say "hub" or "xmm". In a perfect world you could then just look at the pointer to that array so you can move data in and out. Also, when running code, the pointer would tell you where it is as it would be either >=0x8000 or < 0x8000.

    In Basic, one does have the luxury of being able to run a preprocessor and hence tweak the language:
    Dim myarray[10] As Integer <HUB>
    Dim yourarray[10] As Char <XMM>
    
    Easy to search for strings "<HUB>" and "<XMM>" but how would you pass this information to C?

    I'll give this one some thought.

    Ross.
  • RossHRossH Posts: 5,547
    edited 2011-02-17 01:55
    Dr_Acula wrote: »
    Ross, I think that is Spam - check the byline at the bottom of the post. Also check the poster, 5 posts in as many minutes and none of them make sense. Maybe one for the moderators?
    Yes - you're right.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-17 03:17
    No - once you exit the function, that's it - but another way to do it (which I use in both the graphics and multithreading support) is to declare the variable as a local variable in the main() function (which never exits - or if it does you no longer care!) then take the address of it and pass it as a pointer to wherever it is needed - this way the variable will always be in Hub RAM (on the stack) in every memory model, and the memory used will be constant for the life of the program.

    That sounds like it could work.

    As an example, think of a display buffer - 80x40 =3200 bytes, and an associated cog.

    In Basic, I use Dim DisplayBuffer[3200] As Integer <HUB>

    the precompiler makes sure this ends up just after the main declaration in C.

    There is a PTR() command in Basic which does the same thing as the C pointer.

    For C programmers, do you just have to remember where to put the declaration? If so, in a New in the C IDE, I might just put a comment to that effect.
    Call Testsub()
    
    Do
    Loop Until 0=1                          ' infinite loop
    ' *********** end main *******
    
    Sub Testsub()
       Dim Testarray[10] as Integer
       DIM LOCAL a AS INTEGER PTR
       a=Testarray
       print a
    End Sub
    

    void    Testsub (void);
    
    int main(int argc, char *argv[])
    {
    Testsub();
    for(;;)
      {
        if(0==1)
          {
            break;
          }
      }
      return 0;   //  End of main program
    }
    
    
    void Testsub (void)
    {
      static   int      Testarray[10];
      static   int*     a;
      memset(&Testarray,0,sizeof(Testarray));
      memset(&a,0,sizeof(int *));
      a=Testarray;
      printf("% d\n",(int)a);
    }
    

    This prints out a value of 69520. I'm not 100% sure what this means, first question - does this suggest I am using the SMALL or LARGE XMM mode (and if so and it is the wrong one, how do I change?)
  • RossHRossH Posts: 5,547
    edited 2011-02-17 05:21
    Dr_Acula wrote: »
    That sounds like it could work.

    As an example, think of a display buffer - 80x40 =3200 bytes, and an associated cog.

    In Basic, I use Dim DisplayBuffer[3200] As Integer <HUB>

    the precompiler makes sure this ends up just after the main declaration in C.

    There is a PTR() command in Basic which does the same thing as the C pointer.

    For C programmers, do you just have to remember where to put the declaration? If so, in a New in the C IDE, I might just put a comment to that effect.
    Call Testsub()
    
    Do
    Loop Until 0=1                          ' infinite loop
    ' *********** end main *******
    
    Sub Testsub()
       Dim Testarray[10] as Integer
       DIM LOCAL a AS INTEGER PTR
       a=Testarray
       print a
    End Sub
    
    void    Testsub (void);
    
    int main(int argc, char *argv[])
    {
    Testsub();
    for(;;)
      {
        if(0==1)
          {
            break;
          }
      }
      return 0;   //  End of main program
    }
    
    
    void Testsub (void)
    {
      static   int      Testarray[10];
      static   int*     a;
      memset(&Testarray,0,sizeof(Testarray));
      memset(&a,0,sizeof(int *));
      a=Testarray;
      printf("% d\n",(int)a);
    }
    
    This prints out a value of 69520. I'm not 100% sure what this means, first question - does this suggest I am using the SMALL or LARGE XMM mode (and if so and it is the wrong one, how do I change?)

    Dr_A

    By using the word "static" you have stopped the variable being allocated local (i.e. on the stack) - instead, it is being allocated from global RAM. This means that it will be allocated in XMM RAM when you use the XMM LARGE memory model - this is probably why you are seeing an address > 32k

    Remove the word "static" from the C code and I get the following (on a C3 - your results may vary):

    When compiled with -x0 (or no -x option) - which means LMM - I get 29744

    When compiled with -x2 - which means XMM SMALL - I get 29488

    When compiled with -x5 - which means XMM LARGE - I get 29488

    The difference between LMM and XMM is because the XMM target has to allocate an additional 256 byte buffer to handle the loading the XMM RAM - otherwise the array would be in the same place in each case.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-17 16:32
    By using the word "static" you have stopped the variable being allocated local (i.e. on the stack) - instead, it is being allocated from global RAM. This means that it will be allocated in XMM RAM when you use the XMM LARGE memory model - this is probably why you are seeing an address > 32k

    BCX is doing that. I guess one could consider it a bug. But wait! We could turn that into a feature. ie, if you find <XMM> then remove the work static. That might be a neat way to determine whether it is hub or external.
  • RossHRossH Posts: 5,547
    edited 2011-02-17 17:17
    Dr_Acula wrote: »
    BCX is doing that.

    Dr_A,

    That doesn't sound right - local variables should not always end up declared as static. I looked at http://www.bcxgurus.com/help/index.html and it seems to support both STATIC and LOCAL declaration (as well as DIM, GLOBAL and SHARED).

    In this case, I think you should be declaring your variable TestArray as LOCAL if you always want it in Hub RAM. The documentation also appears to imply that DIM is the same as LOCAL and DIM LOCAL - but this seems to be incorrect.

    Try out the various options and see what you get.

    Ross.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2011-02-18 20:53
    There seems to be a bug with BCX - the instructions say that if you declare a variable with STATIC it uses static, and if you declare with DIM or LOCAL, it declares with the standard declaration "int a;"

    But the actual program puts static in regardless of what you use. I am awaiting confirmation I have joined the BCX forum so I can ask about this. There are some threads about command line switches that can change this, but I can't find any formal documentation of those command line switches.

    Meanwhile, I have been thinking through the C code and I think I am at a eureka moment.
    No - once you exit the function, that's it - but another way to do it (which I use in both the graphics and multithreading support) is to declare the variable as a local variable in the main() function (which never exits - or if it does you no longer care!) then take the address of it and pass it as a pointer to wherever it is needed - this way the variable will always be in Hub RAM (on the stack) in every memory model, and the memory used will be constant for the life of the program.

    Remember a few weeks back you wrote a hub ram "peek" and "poke" for me?

    Well, I don't think you need them now. This is better.

    All you do is declare an array/variable in the main. This makes it definitely in hub. eg a screen buffer.

    If you want to modify that, you can pass a pointer to that array to any function you like.

    Now, what does this lead to?

    Well, I'm thinking about the concept of cog code that can be run on any language. Basic. C. Forth. Zog. Big Spin.

    It makes sense for such code to not take up any hub memory. Do that with 8 cogs, and you suddenly save 16k of hub space just like that.

    Use the little program you have shown me that takes some pasm and converts it to a C array. Put that C array back into your C program. The cog program is now effectively sitting in external ram (if we are running a XMM program that is)

    How do we talk to this? Well, keep it very simple - you can only pass one long to the cog. Generally, that long would be a pointer to an array. Where is the array? Well, it is in hub ram, defined as an array just after "main".

    I think your quote above is the missing link!

    So this is the C code we end up with
    void mycogject(int cognumber, unsigned long parameters)                                        // 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[] =                               // dummy data or compiled above code or #include file.h
           {
                  0x003c0602, 0x5c7c0000, 0x00001388, 0x00000041
           };
           unsigned long par_cogject[] = { 1, 2, 3 };                    // 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
    }
    

    and we pass the location of that hub array, through to this function, and then through to "long par_cogject". So instead of it containing dummy 1,2,3 as above, it now points to the hub array.

    Then filling the array with data to pass to the cog is done in a similar way to Spin.

    So in the function above void mycogject(int cognumber, unsigned long parameters)

    parameters instead becomes a pointer to that hub array.

    what does that change this line to?
    _coginit((int)par_cogject>>2, (int)mycogject_array>>2, cognumber);

    par_cogject becomes *parameters or something?

    And I guess something we might want to work out now - would you pass data in "bytes" or "longs"? I'd be inclined towards "longs" because this is the propeller standard. So the array we define in "main" is an unsigned long array, not a char array.
Sign In or Register to comment.