Shop OBEX P1 Docs P2 Docs Learn Events
Run Basic programs using GCC — Parallax Forums

Run Basic programs using GCC

Dr_AculaDr_Acula Posts: 5,484
edited 2012-04-28 00:27 in Propeller 1
Some clever boffins have written a Basic to C translator http://www.bcxgurus.com/help/index.html

Let's start with this little Basic program.
Dim StringOne$ As String * 80            ' dim with 80 bytes
Dim StringTwo$ As String * 80            ' dim with 80 bytes
Line Input "Enter a string: ", StringOne$ 
Line Input "Enter another string: ", StringTwo$ 
Print StringOne$+StringTwo$                          ' print out the two strings combined

If you open the GCC terminal while it is downloading, it stops the download. But if you open it too late after the download, you miss the first bytes coming back. So let's add in a little bit of a delay, maybe 10 secs. We could write this in BASIC or in C. Or just to be clever and show this can be done, write a little bit of C code inside the BASIC program and surround it with the $CCODE directive.
$CCODE
  int i;
  for (i=0;i<40;i++)
    {
      waitcnt(CLKFREQ/10+CNT);                 // startup delay - start the terminal program while this is running
      printf("Start the terminal program %d\n", i);
    }
$CCODE

And there are a whole lot of #includes. There is no cost with adding all these on a PC, but for an embedded application only the bare minimum need to be included. After a bit of trial and error, the #include list looks like this
#include <stdio.h>
#include <propeller.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>

So this is the complete BASIC program (with a bit of C seasoning!)
' BCX help file http://www.bcxgurus.com/help/index.html
' After download using GCC IDE, THEN start the terminal program

#include <stdio.h>
#include <propeller.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>

$CCODE
  int i;
  for (i=0;i<40;i++)
    {
      waitcnt(CLKFREQ/10+CNT);                 // startup delay - start the terminal program while this is running
      printf("Start the terminal program %d\n", i);
    }
$CCODE
Dim StringOne$ As String * 80            ' dim with 80 bytes
Dim StringTwo$ As String * 80            ' dim with 80 bytes
Line Input "Enter a string: ", StringOne$ ' input a string. Use Line Input and needs some text
Line Input "Enter another string: ", StringTwo$ ' input a string. Use Line Input and needs some text
Print StringOne$+StringTwo$                          ' print out the string

You can leave out the $ after strings if you want. They are more "retro" Basic.

Then we run it through BCX. http://www.bcxgurus.com/help/index.html

There are a pile of Windows #includes at the beginning of the output file. Delete everything from the top of the file down to (and including) these lines
// *************************************************
// End of Object/Import Libraries To Search
// *************************************************
#endif

You can automate all that from an IDE with just one keypress.

Copy the program into the Simple IDE. Compiler is C++. Memory model is LMM. Download and run.

This is the output C code.
#include <stdio.h>
#include <propeller.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>

// *************************************************
//                Typedef
// *************************************************

typedef unsigned char UCHAR;
typedef unsigned long DWORD;
typedef unsigned long UINT;

// *************************************************
//                System Variables
// *************************************************


// *************************************************
//            User Global Variables
// *************************************************

static char    StringOne[80];
static char    StringTwo[80];



// *************************************************
//               Standard Prototypes
// *************************************************

char*   BCX_TmpStr(size_t);
char*   join (int, ... );


// *************************************************
//            User Global Initialized Arrays
// *************************************************


// *************************************************
//                  Main Program
// *************************************************

int main(int argc, char *argv[])
{
  int i;
  for (i=0;i<40;i++)
    {
      waitcnt(CLKFREQ/10+CNT);                 // startup delay - start the terminal program while this is running
      printf("Start the terminal program %d\n", i);
    }
printf("%s","Enter a string: ");
gets(StringOne);
printf("%s","Enter another string: ");
gets(StringTwo);
printf("%s%s\n",StringOne,StringTwo);
  return 0;   //  End of main program
}

// *************************************************
//                 Runtime Functions
// *************************************************

char *BCX_TmpStr (size_t Bites)
{
  static int   StrCnt;
  static char *StrFunc[2048];
  StrCnt=(StrCnt + 1) & 2047;
  if(StrFunc[StrCnt]) free (StrFunc[StrCnt]);
  return StrFunc[StrCnt]=(char*)calloc(Bites+128,sizeof(char));
}


char * join(int n, ...)
{
  register int i = n, tmplen = 0;
  register char *s_;
  register char *strtmp;
  va_list marker;
  va_start(marker, n); // Initialize variable arguments
  while(i-- > 0)
  {
    s_ = va_arg(marker, char *);
    if(s_) tmplen += strlen(s_);
  }
  strtmp = BCX_TmpStr(tmplen);
  va_end(marker); // Reset variable arguments
  i = n;
  va_start(marker, n); // Initialize variable arguments
  while(i-- > 0)
  {
    s_ = va_arg(marker, char *);
    if(s_) strcat(strtmp, s_);
  }
  va_end(marker); // Reset variable arguments
  return strtmp;
}

Comments

  • mindrobotsmindrobots Posts: 6,506
    edited 2012-04-27 05:38
    This is very cool but I think you either need a little more introduction to explain where you are headed or I need a lot more coffee this morning.

    I read through it a couple times before I caught on to what you were up to. Once I visited the BCX site, it all came clear.

    A great path for anyone with a large BASIC program collection!
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-04-27 05:45
    I think it means you can piggy back on all the GCC work and run Basic programs. So if the GCC team create an XMM package to run huge C programs, you now can run huge Basic programs too. I don't think there have been any versions of Basic up to now that could run a one megabyte program on the propeller.

    I had this partially working last year with Catalina but BCX translated into C++ and there were endless patches and fixes to turn the C++ into C89. But now with GCC it becomes much simpler. Just trim off the Windows includes at the beginning.

    I suspect there are going to be some more "essential" #include files, and so in practice it might work best in an XMM environment. It was fairly easy to work out which ones were needed, just try compiling it, look at the error, and search for that line on the internet to find which #include it went with.

    In the BCX examples and instructions are some GUI functions, buttons, text boxes and the like. I have an idea that this code can be used with the existing primitives on the touchscreen module that draw buttons, checkboxes, read and translate bitmaps off an sd card etc. I have an idea this could make GUI programming on the propeller a whole lot easier.
  • mindrobotsmindrobots Posts: 6,506
    edited 2012-04-27 06:43
    Excellent! Now, I'm with you (and to more cups coffee), this is a very neat trick!
  • jazzedjazzed Posts: 11,803
    edited 2012-04-27 07:07
    Dr_Acula wrote: »
    I think it means you can piggy back on all the GCC work and run Basic programs. So if the GCC team create an XMM package to run huge C programs, you now can run huge Basic programs too. I don't think there have been any versions of Basic up to now that could run a one megabyte program on the propeller.

    I had this partially working last year with Catalina but BCX translated into C++ and there were endless patches and fixes to turn the C++ into C89. But now with GCC it becomes much simpler. Just trim off the Windows includes at the beginning.

    I suspect there are going to be some more "essential" #include files, and so in practice it might work best in an XMM environment. It was fairly easy to work out which ones were needed, just try compiling it, look at the error, and search for that line on the internet to find which #include it went with.

    In the BCX examples and instructions are some GUI functions, buttons, text boxes and the like. I have an idea that this code can be used with the existing primitives on the touchscreen module that draw buttons, checkboxes, read and translate bitmaps off an sd card etc. I have an idea this could make GUI programming on the propeller a whole lot easier.

    Fascinating and excellent effort. Glad you found C++ useful.

    GCC offers 3 kinds of XMM memory models (all keep stack in HUB RAM):
    1. XMMC code only in external memory with data
    2. XMM-SINGLE code and data in external memory
    3. XMM-SPLIT code in one memory like flash and data in another memory like SRAM
    Thanks,
    --Steve
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-04-27 16:08
    GCC offers 3 kinds of XMM memory models (all keep stack in HUB RAM):

    XMMC code only in external memory with data
    XMM-SINGLE code and data in external memory
    XMM-SPLIT code in one memory like flash and data in another memory like SRAM

    That could be very useful.

    So if you put the code in external memory and used XMMC mode, and in the main you call a function and in that function you define a big array, say 10,000 bytes. That array ends up in hub, right?

    What happens when the function finishes? Is that memory freed up at the end of the function?
  • mindrobotsmindrobots Posts: 6,506
    edited 2012-04-27 16:19
    My guess is that if you create it, you need to keep track of it and free it up when done. C doesn't have any type of automatic garbage collection (especially on the Prop) like those fancy languages.

    What's the distinction between XMMC and XMMC-SINGLE? (the two definitions above almost read identically to me)

    XMMC data is in HUB? XMMC-SINGLE Data and Code is in HUB external?
  • denominatordenominator Posts: 242
    edited 2012-04-27 18:19
    Dr_Acula wrote: »
    So if you put the code in external memory and used XMMC mode, and in the main you call a function and in that function you define a big array, say 10,000 bytes. That array ends up in hub, right?

    What happens when the function finishes? Is that memory freed up at the end of the function?

    It all depends on how you define the array:
    void function foo()
    {
       char buffer[10000];
    
       // Use the buffer
    }
    

    In this case, the buffer was allocated on the stack and that memory is "freed" when you return from foo().
    void function foo()
    {
       static char buffer[10000];
    
       // Use the buffer
    }
    

    In this case, the buffer was allocated in data memory, and that memory was allocated by the linker; this allocation exists at the start of your program (before foo() is even called) and persists until you load a new program.
    void function foo()
    {
       char* buffer = malloc(10000);
    
       // Use the buffer
    }
    

    In this case, the buffer was allocated from the heap. It is not freed when foo() returns and is a memory "leak". The proper code would have been:
    void function foo()
    {
       char* buffer = malloc(10000);
    
       // Use the buffer
    
       free(buffer);
    }
    

    OR
    char* function foo()
    {
       char* buffer = malloc(10000);
    
       // Use the buffer
    
       return buffer;  /* Note: the caller should call free() with the returned pointer when done with the storage */
    }
    

    - Ted
  • denominatordenominator Posts: 242
    edited 2012-04-27 18:44
    mindrobots wrote: »
    What's the distinction between XMMC and XMMC-SINGLE? (the two definitions above almost read identically to me)

    XMMC data is in HUB? XMMC-SINGLE Data and Code is in HUB external?

    There is no XMMC-SINGLE. It is named XMM-SINGLE.

    The difference is:

    XMMC mode: max of 48K bytes data space (16K is used as the code cache), several GB of code space stored in external memory and cached in the 16K buffer at the top of hub ram.

    XMM-SINGLE mode: several GB of code+data space stored in external memory and cached in the 16K buffer at the top of hub ram.

    In the XMMC mode, the lower 48K of hub ram (the data space) is also reduced by the stack, the heap, and by any code/data that is marked as needing to be always hub-resident.

    In the XMM-SINGLE mode, the lower 48K of data is exclusively used by the stack, the heap, and by any code/data that is marked as needing to be always hub-resident. I believe that the library allocation routines could be re-written to use the external data space for the heap in all XMM (not XMMC) modes, but this is a TBD project.
  • jazzedjazzed Posts: 11,803
    edited 2012-04-27 19:15
    mindrobots wrote: »
    My guess is that if you create it, you need to keep track of it and free it up when done. C doesn't have any type of automatic garbage collection (especially on the Prop) like those fancy languages.

    What's the distinction between XMMC and XMMC-SINGLE? (the two definitions above almost read identically to me)

    XMMC data is in HUB? XMMC-SINGLE Data and Code is in HUB external?

    Please forgive my fat editing fingers. XMMC puts data and stack in HUB RAM.



    XMM Type
    STACK
    CODE
    DATA


    XMMC
    Hub RAM
    External Flash
    HUB RAM


    XMM-SINGLE
    Hub RAM
    External RAM
    External RAM


    XMM-SPLIT
    Hub RAM
    External Flash or RAM
    Other External RAM



    XMMC is invoked with the -mxmmc compiler flag.
    XMM-SINGLE is invoked with the -mxmm-single compiler flag. It uses a single RAM device for Code/Data.
    XMM-SPIT is invoked with the -mxmm or -mxmm-split compiler flag.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-04-27 22:20
    @denominator - thanks for that. Fantastic explanation. All those solutions will be useful. eg - you could store a "system" font as a static, and store a temporary font as a dynamic array that is deleted when the function ends.

    re
    XMMC Hub RAM External Flash HUB RAM

    If one added a new memory hardware solution, would it still be called XMMC or would you invent a new name for each hardware design?

    Also some questions regarding running pasm code but I think they are answered in this thread http://forums.parallax.com/showthread.php?138033-Converting-Spin-PASM-Object so off to read that...
  • denominatordenominator Posts: 242
    edited 2012-04-27 23:09
    Dr_Acula wrote: »
    If one added a new memory hardware solution, would it still be called XMMC or would you invent a new name for each hardware design?

    New names would not necessarily be required for a new hardware solution. If your new hardware solution somehow required you to lay out the "virtual" memory space differently, then yes, you would need to invent a new memory model and name and conequently make additions to the compiler and/or linker to support the new memory model. For example, if you want all code to be located in hub ram, but you want data to be located in external memory - there is no mode for that, and it would require a new name and additional implementation code in the compiler/linker.

    On the other hand, it will probably be more common that you want to interface different a different piece of hardware (say you have some spare isolinear optical chips from a surplus federation PADD) but use an existing memory model. In this case, you only need provide a cache driver for the hardware design and specify it in a new board config for loader.
    Dr_Acula wrote: »
    Also, is there a tutorial or a demo program that shows how to use cog code? In Spin, an example might be a very simple bit of Spin code that sets up a variable. Then starts a cog running, passes the variable to the cog, the cog adds one to the variable, ends and then you print the new answer.

    In the demo directory: Check out the toggle/cog_c_toggle demo for a simple example of how to start a cog running cog-mode C. Check out propboe/libsrc, particularly i2c_driver.c (the cog-mode C) and i2c_interface.c which runs in LMM/XMM* modes and interfaces with the cog via a mailbox.
    Dr_Acula wrote: »
    I think in GCC you can write the cog part in pasm or some sort of pasm equivalent, or you can write it in C?

    As shown above, you can write C that directly compiles to cog-mode PASM. This mode has a code space of 496 longs, and can address hub ram for data. Therefore, your C is executed directly - in this mode there is no "kernel" that provides a LMM-style virtual machine.

    You can also write any part of any GCC program in PASM; this includes all modes: XMM*, LMM, and cog-mode. You can do this two ways:

    1) Inline PASM mixed in with your C code

    The inline PASM is a bit tricky - you have to wrap it with information that relates the PASM variables to the compiler's variables (and thus allows the compiler to optimize around your PASM). See the gcc.pdf manual for more details, or if you're interested I can point you to a few examples.

    2) Include a .s file (that contains PASM) in your project

    The PASM supported by GCC is pretty close to "standard" PASM, but there are slight differences, and you can use the C preprocessor. If your PASM will be called by a XMM* or LMM program, you must obey certain rules when accessing memory. See http://propgcc.googlecode.com/hg/doc/Memory.html for details.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-04-27 23:41
    Thanks for that. I'm looking at toggle now.

    I just found something cool on the IDE - a big shiny blue arrow button for compiling that opens the terminal automatically. So the Basic code does not need the startup delay code any more. It is now smaller
    #include <stdio.h>
    #include <propeller.h>
    #include <string.h>
    #include <stdarg.h>
    #include <stdlib.h>
    
    Dim StringOne$ As String * 80            ' dim with 80 bytes
    Dim StringTwo$ As String * 80            ' dim with 80 bytes
    Line Input "Enter a string: ", StringOne$ ' input a string. Use Line Input and needs some text
    Line Input "Enter another string: ", StringTwo$ ' input a string. Use Line Input and needs some text
    Print StringOne$+StringTwo$                          ' print out the string
    

    Re the memory model, no nothing fancy there. Data in the hub, code somewhere external.

    Because the code can be large, the "cost" of all those pasm bits is not nearly as high as in Spin, where you can lose 14k out of 32k to pasm code.

    Re inline code and shared variables, I'm a big fan of Chip's original objects where everything gets passed to a cog in a parameter list, so there are no shared variables. This is a skeleton bit of pasm code that can compile to a standalone binary file with the spin tool.
    CON
      _clkfreq = 80_000_000
      _clkmode = xtal1 + pll16x
    
    PUB Main
        coginit(1,@entry,0)                     ' cog 1, entry, dummy value
    
    DAT
                      org 0
    entry                                          ' start of the cog code
                      mov arraypointer,par         ' arraypointer is the address of the array
                      rdlong array0,arraypointer   ' array0 = array[0]
                      mov array1,array0            ' array[1]=array[0] - long is 4 bytes so add 4
                      add array1,#4                ' array[1] += 1 change the value so see cog works
                      add arraypointer,#1          ' pointer += 1
                      wrlong array1,arraypointer   ' write array[1] back to hub
                      jmp #entry
    
    arraypointer      long    $00000000            ' pointer to array[0]
    array0            long    $00000000            ' first array location
    array1            long    $00000000            ' second array location
                      fit 496
    
  • denominatordenominator Posts: 242
    edited 2012-04-28 00:11
    Dr_Acula wrote: »
    Re inline code and shared variables, I'm a big fan of Chip's original objects where everything gets passed to a cog in a parameter list, so there are no shared variables. This is a skeleton bit of pasm code that can compile to a standalone binary file with the spin tool.

    That's great, because that style of code works really well in C when calling routines on a separate cog.

    Re inline PASM - one thing I forgot to say is that it is not for big runs of code. For example, you wouldn't declare an entire routine in inline PASM. Even jumps and loops in inline PASM are tricky. But don't sell inline PASM short - in certain circumstances it could provide you enough speed and flexibility so you can almost completely avoid PASM. In that mode of programming the vast majority of your code would be C and only certain critical pieces of code would be in PASM fully wrapped in C.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-04-28 00:27
    That sounds great.

    Re Basic and GCC, I have a rather scary demo program that tests out a whole lot of BCX basic functions. But it is large and so will need XMMC running. So I need to go back to that.

    I've got two threads running here so I might move this over to the XMM thread as that is the next problem to solve - getting XMM on some different hardware.

    I think BCX basic users won't ever need to worry about inline pasm as that will be hidden from the user. Things like opening files end up being one Basic line of code, like in C.

    But I might take your comments on inline pasm over to the XMM discussion because that is very interesting. If you can do inline C and pasm, you could do this in Basic too...
Sign In or Register to comment.