My first Propeller C program (if I can do it. . . )

Hey all,
I shouldn't have been so late to this party but I have to say I'm really excited about Propeller GCC. This afternoon I had a 15 minute break and I installed the 40 MB Propeller GCC compiler and sample programs.
Next I found the lmm_toggle directory and proceeded to modify toggle.c to look like this:
Compiled the code and downloaded to RAM in a Propeller BOE. I was really excited to see the results! At least now I can run the demos that all of your are producing and do something beyond being the sideline cheerleader. Didn't want to wear a dress anyway.
I need to learn enough about C to port some of my simple projects from Spin. I need to find a C tutorial for business types (hah ha) - does such a book exist? Can somebody point me in the right direction so I can get a better feeling for the syntax? I have a whole week off over Thanksgiving and I think I should be able to come back here with some real questions and a demo of my own.
Nice work to the Propeller GCC developers!
Ken Gracey
I shouldn't have been so late to this party but I have to say I'm really excited about Propeller GCC. This afternoon I had a 15 minute break and I installed the 40 MB Propeller GCC compiler and sample programs.
Next I found the lmm_toggle directory and proceeded to modify toggle.c to look like this:
*/
#include "propeller.h"
int main(int argc, char* argv[])
{
int mask = 0x3fffffff;
int freq = CLKFREQ>>1;
DIRA = mask;
for(;;) {
OUTA ^= 1<<0;
OUTA ^= 1<<1;
OUTA ^= 1<<2;
OUTA ^= 1<<3;
OUTA ^= 1<<4;
OUTA ^= 1<<5;
OUTA ^= 1<<6;
waitcnt(freq+CNT);
}
}
Compiled the code and downloaded to RAM in a Propeller BOE. I was really excited to see the results! At least now I can run the demos that all of your are producing and do something beyond being the sideline cheerleader. Didn't want to wear a dress anyway.
I need to learn enough about C to port some of my simple projects from Spin. I need to find a C tutorial for business types (hah ha) - does such a book exist? Can somebody point me in the right direction so I can get a better feeling for the syntax? I have a whole week off over Thanksgiving and I think I should be able to come back here with some real questions and a demo of my own.
Nice work to the Propeller GCC developers!
Ken Gracey
Comments
I thought you were following the "business types" guide to C - set direction, hire consultants, concentrate on YOUR job until they are done! (kidding!)
Hats off to all the developers!
We still need to find a front-end Eclipse guru. Got a few tips from Captain Quirk but more options would be beneficial.
And hats off to the volunteers, too. If any of them want anything we can provide (hardware, robots, sensors, etc.) please contact me. Your contributions are truly valuable.
Ken Gracey
In another thread a lot of people recomended "The C Programming Language(ANSI)" by Brian W. Kernighan and Dennis M. Ritchie. I picked up a copy at BN I just started to get into it. It assumes you have a little programming knowledge, but I understand it so far (I Think). As I go through it I am going to compile and run it on my computer and then the Propeller with PropGcc. Propgcc has compiled and ran the first two examples with no problems.
Yeah, Great Job Propeller GCC Team.
Ron
What I'm not sure about is how the Propeller's capabilities will shine through in C. For example, how can any standard C code run on a Propeller when syntax must be supplemented for cog launching, driving a VGA, etc. I guess everybody accepts that C must vary from what we've seen on a PC to be useful in embedded systems. Probably a very elementary concern. David Betz is porting the VGA from Spin to C/PASM blob so maybe that'll answer my questions.
Forgot to mention the book is in its 2nd Edition. I also found this C Reference Card (ANSI) usefull makes a good book mark to.:cool:
Ron
Good points.
I've experimented with two solutions. One involves writing an IDE that can detect pasm code and separate it out and compile it and then read it back in as a C array and copy it into a C program as a hex array.
The second is to treat such compiled programs as something that can be loaded off an sd card.
In both cases the C and the pasm have to be separated out and the only interaction between the two must be via the array passed by par. This means that not every piece of code in the Obex can be translated easily.
Some work well though. The mouse for instance (this is Catalina code):
At the beginning of the program, declare an array to store this. This works best for XMM so this array ends up in external memory rather than hub memory. This saves hub memory. You could also load directly from an sd card into the cog, but I have written this so there is a copy in external memory as reloads are then quicker. In terms of speed, you could put the cog data anywhere - fastest = in hub, medium =external ram, slowest = sd card. It depends if you will be loading and reloading cogs through the program or just loading them once at the beginning.
unsigned long cogject_mouse[512]; // external memory for mouse driver
In the main, declare the common array between hub and spin. In Catalina, declaring an array in "main" means it is local and so ends up in hub rather than in external memory
unsigned long mouse_parameters[19]; // group of 7 plus 12 longs in par_x in the spin version
Read the cog data from an sd card into the xmm array we declared earlier. The file "mouse.cog" is actually "mouse.bin" which is the pasm part of the standard Obex mouse driver that has been compiled without any Spin.
readcog("mouse.cog",cogject_mouse);
which calls this code to read from the sd card
void readcog(char *filename,unsigned long external_cog[]) // read in a .cog file into external memory array { int i; FILE *FP1; i = 0; if((FP1=fopen(filename,"rb"))==0) // open the file { fprintf(stderr,"Can't open file %s\n",filename); exit(1); } fseek(FP1,0,0); for(i=0;i<24;i++) { getc(FP1); // read in the first 24 bytes and discard } i = 0; while(!EoF(FP1) & (i<505)) // run until end of file or 511-6 { external_cog[i] = getc(FP1) | (getc(FP1)<<8) | (getc(FP1)<<16) | (getc(FP1)<<24); // get the long i+=1; } if(FP1) { fclose(FP1); // close the file FP1=NULL; } //printf("external array cog first long = 0x%x \n",external_cog[0]); // hex value }
Then fill up the array we pass to the cog with some parameters
mouse_engine_start(7, cogject_mouse, mouse_parameters, 24, 25); // start the mouse driver (takes 2 secs to reply with the mouse type)
which calls this routine
void mouse_engine_start(int cognumber, unsigned long cogarray[], unsigned long mouse_parameters[], int dpin, int cpin) { mouse_parameters[0] = (unsigned long) &mouse_parameters[0]; // the first long of the array points to the location of the array itself mouse_parameters[5] = (unsigned long) dpin; // data pin mouse_parameters[6] = (unsigned long) cpin; _cogstop(cognumber); // stop this cog external_memory_cog_load(cognumber,cogarray,mouse_parameters); // load from external ram to this cog, pass location of the array }
and which calls this routine to move data from an array in external memory into a cog:
void external_memory_cog_load(int cognumber, unsigned long cogdata[], unsigned long parameters_array[]) // load a cog from external memory { unsigned long hubcog[511]; // create a local array, this is in hub ram, not external ram int i; for(i=0;i<512;i++) { hubcog[i]=cogdata[i]; // move from external memory to a local array in hub } _coginit((int)parameters_array>>2, (int)hubcog>>2, cognumber); // load the cog }
That starts up the cog, and the mouse location and the buttons appear in the mouse_parameters array.
This is a fragment of code to read the mouse pointer and check it is not off the screen and to see if a button has been pressed
{ if ((oldx != mouse_parameters[0]) | (oldy != mouse_parameters[1])) { mx = mouse_parameters[0]; my = mouse_parameters[1]; if (mx>0x80000000) mx = 0; // x < 0 if (mx > 307) mx = 307; // x > 307 if (my>0x80000000) my = 0; // y < 0 if (my>219) my=219; // y > 219 gray_mousepointer(screen,mousebuffer,mx, 219-my); // move the pointer //gray_debug_hex(screen,mouse_parameters[1]); // print the value oldx = mouse_parameters[0]; // store the previous value oldy = mouse_parameters[1]; } new_button = mouse_parameters[3]; // in case state changes in the next few lines if ((old_button == 0) & (new_button == 1)) // mouse has changed from zero to 1 { mouse_button(screen, mouse_parameters); // mouse down } old_button = new_button; // store current state of buttons }
You can do the same thing with a VGA driver, eg this is a C translation of Kye's VGA driver. Separate out the pasm and the Spin. Convert the Spin to C. Compile the pasm part to a binary file, rename it as .cog and put it on an sd card
void pix_clearscreen(unsigned long screen[]) { int i; for (i=0;i<4800;i++) { screen[i] = 0x00000000; // fill with black, use longs rather than bytes so 4 pixels per loop } } void pix_pixel(unsigned long screen[], int x, int y, char color) { poke((y*160+x+(unsigned long)&screen[0]),color); } void pix_readscreen(char *filename, unsigned long screen[]) // read a full screen 19200 byte file into the screen { int i; FILE *FP1; i = 0; FP1=fopen(filename,"rb"); // open the file fseek(FP1,0,0); for(i=0;i<=4800;i++) { screen[i] = getc(FP1) | (getc(FP1)<<8) | (getc(FP1)<<16) | (getc(FP1)<<24); // get the long } fclose(FP1); // close the file FP1=NULL; } char pix_color(char red,char green,char blue) // pass red,green,blue 0-3, returns a combined value { return ((red & 0x3) << 6 ) | ((green & 0x3) << 4) | ((blue & 0x3) << 2); } void pix_screengray(unsigned long screen[]) // whole screen gray { int i; for(i=0;i<4800;i++) { screen[i] = 0xa8a8a8a8; // same as pix_color(2,2,2) } } void pixenginestart(int cognumber, unsigned long cogarray[], unsigned long screen[], unsigned long pingroup, char color_parameters[]) { int i; unsigned long frequencystate; screen[2] = (0xff << (8*pingroup)); screen[3] = (0x300000ff | (pingroup << 9)); frequencystate = ((25175000 + 1600) / 4); screen[4] = 1; for(i=0;i<32;i++) { frequencystate = frequencystate << 1; screen[4] = (screen[4] << 1) | (screen[4] >> 31); if (frequencystate >= _clockfreq()) { frequencystate -= _clockfreq(); screen[4] += 1; } } color_parameters[0] = 1; // displayindicator color_parameters[1] = 0; // syncindicator screen[0] = (unsigned long) &color_parameters[0]; // pointer to displayindicator screen[1] = (unsigned long) &color_parameters[1]; // pointer to syncindicator _cogstop(cognumber); // stop just before starting the next one external_memory_cog_load(cognumber,cogarray,screen); // load from external ram, pass some values in screen[] //pix_clearscreen(screen); // clear the screen to black pix_screengray(screen); // screen gray } void pix_colorbar(unsigned long screen[]) { int x; int c; x = 0; for (c=0;c<64;c++) { pix_pixel(screen,x+10,6,(c<<2)); // print all the colors in a bar x++; } } void pix_line(unsigned long screen[], int startx, int starty, int endx, int endy, char color) { int row; // also does an unfilled box int col; for(col=startx;col <= endx ;col++) { pix_pixel(screen,col,starty,color); // horizontal lines pix_pixel(screen,col,endy,color); } for(row=starty;row <= endy;row++) { pix_pixel(screen,startx,row,color); pix_pixel(screen,endx,row,color); } } void pix_box_fill(unsigned long screen[], int startx, int starty, int endx, int endy, char color) { int row; int col; for(row =starty;row <= endy; row++) { for(col=startx;col <=endx; col++) { pix_pixel(screen,col,row,color); // horizontal line } } } void copyscreen(unsigned long source[],unsigned long destination[]) { memcpy(destination,source,19200); // a strings.h function } void color_demo(unsigned long screen[],char color_parameters[],int cognumber) // kye graphics demo { pixenginestart(cognumber,cogject_color,screen,2,color_parameters); // start the driver pix_pixel(screen,3,3,0x44); pix_pixel(screen,4,4,0xFF); pix_pixel(screen,5,5,pix_color(1,2,3)); // rgb each 0,1,2 or 3 pix_line(screen,0,0,159,119,pix_color(3,0,0)); // red border for screen pix_colorbar(screen); // a color bar pix_line(screen,10,10,30,30,pix_color(3,3,0)); // a yellow box pix_box_fill(screen,10,50,40,60,pix_color(3,0,3)); // magenta filled rectangle //pix_readscreen("wallaby.vga",screen); // display whole screen image //copyscreen(screen,screen_external); // copy to external ram //pix_readscreen("Giraffe.vga",screen); // display whole screen image //copyscreen(screen_external,screen); // copy back to hub //pix_readscreen("prop160.vga",screen); // display whole screen image //pix_box_fill(screen, 0, 0, 159,119,pix_color(2,2,2)); // whole screen gray }
and for reference, this was Kye's original Spin/Pasm VGA driver. Compare the function 'pixenginestart' in C and Spin
{{ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // VGA64 6 Bits Per Pixel Engine // // Author: Kwabena W. Agyeman // Updated: 11/17/2010 // Designed For: P8X32A // Version: 1.0 // // Copyright (c) 2010 Kwabena W. Agyeman // See end of file for terms of use. // // Update History: // // v1.0 - Original release - 11/17/2009. // // For each included copy of this object only one spin interpreter should access it at a time. // // Nyamekye, /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Video Circuit: // // 0 1 2 3 Pin Group // // 240OHM // Pin 0, 8, 16, 24 ----R-------- Vertical Sync // // 240OHM // Pin 1, 9, 17, 25 ----R-------- Horizontal Sync // // 470OHM // Pin 2, 10, 18, 26 ----R-------- Blue Video // | // 240OHM | // Pin 3, 11, 19, 27 ----R----- // // 470OHM // Pin 4, 12, 20, 28 ----R-------- Green Video // | // 240OHM | // Pin 5, 13, 21, 29 ----R----- // // 470OHM // Pin 6, 14, 22, 30 ----R-------- Red Video // | // 240OHM | // Pin 7, 15, 23, 31 ----R----- // // 5V // | // --- 5V // // --- Vertical Sync Ground // | // GND // // --- Hoirzontal Sync Ground // | // GND // // --- Blue Return // | // GND // // --- Green Return // | // GND // // --- Red Return // | // GND /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }} CON #$FC, Light_Grey, #$A8, Grey, #$54, Dark_Grey #$C0, Light_Red, #$80, Red, #$40, Dark_Red #$30, Light_Green, #$20, Green, #$10, Dark_Green #$0C, Light_Blue, #$08, Blue, #$04, Dark_Blue #$F0, Light_Orange, #$A0, Orange, #$50, Dark_Orange #$CC, Light_Purple, #$88, Purple, #$44, Dark_Purple #$3C, Light_Teal, #$28, Teal, #$14, Dark_Teal #$FF, White, #$00, Black PUB plotBox(color, xPixelStart, yPixelStart, xPixelEnd, yPixelEnd) '' 8 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Plots a one color box of pixels on screen. '' // '' // Color - The color of the box of pixels to display on screen. A color byte (%RR_GG_BB_xx). '' // XPixelStart - The X cartesian pixel start coordinate. X between 0 and 159. Y between 0 and 119. '' // YPixelStart - The Y cartesian pixel start coordinate. Note that this axis is inverted like on all other graphics drivers. '' // XPixelEnd - The X cartesian pixel end coordinate. X between 0 and 159. Y between 0 and 119. '' // YPixelEnd - The Y cartesian pixel end coordinate. Note that this axis is inverted like on all other graphics drivers. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// xPixelEnd := ((xPixelEnd <# 159) #> 0) yPixelEnd := (((yPixelEnd <# 119) #> 0) * 160) xPixelStart := ((xPixelStart <# xPixelEnd) #> 0) yPixelStart := (((yPixelStart * 160) <# yPixelEnd) #> 0) yPixelEnd += xPixelStart yPixelStart += xPixelStart xPixelEnd -= --xPixelStart repeat result from yPixelStart to yPixelEnd step 160 bytefill(@displayBuffer + result, (color | $3), xPixelEnd) PUB plotPixel(color, xPixel, yPixel) '' 6 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Plots a one color pixel on screen. '' // '' // Color - The color of the pixel to display on screen. A color byte (%RR_GG_BB_xx). '' // XPixel - The X cartesian pixel coordinate. X between 0 and 159. Y between 0 and 119. '' // YPixel - The Y cartesian pixel coordinate. Note that this axis is inverted like on all other graphics drivers. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// displayBuffer.byte[((xPixel <# 159) #> 0) + (160 * ((yPixel <# 119) #> 0))] := (color | $3) PUB displayClear '' 3 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Clears the screen to black. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// longfill(@displayBuffer, 0, constant((160 * 120) / 4)) PUB displayPointer '' 3 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Returns a pointer to the display buffer. '' // '' // The display buffer is an array of 160 by 120 bytes. Each byte represents a pixel on the screen. '' // '' // Each pixel is a color byte (%RR_GG_BB_xx). Where RR, GG, and BB are the two bit values of red, green, blue respectively. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// return @displayBuffer PUB displayState(state) '' 4 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Enables or disables the PIX Driver's video output - turning the monitor off or putting it into standby mode. '' // '' // State - True for active and false for inactive. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// displayIndicator := state PUB displayRate(rate) '' 4 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Returns true or false depending on the time elasped according to a specified rate. '' // '' // Rate - A display rate to return at. 0=0.234375Hz, 1=0.46875Hz, 2=0.9375Hz, 3=1.875Hz, 4=3.75Hz, 5=7.5Hz, 6=15Hz, 7=30Hz. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// result or= (($80 >> ((rate <# 7) #> 0)) & syncIndicator) PUB displayWait(frames) '' 4 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Waits for the display vertical refresh. '' // '' // The best time to draw on screen for flicker free operation is right after this function returns. '' // '' // Frames - Number of vertical refresh frames to wait for. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// repeat (frames #> 0) result := syncIndicator repeat until(result <> syncIndicator) PUB displayColor(redAmount, greenAmount, blueAmount) '' 6 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Builds a color byte (%RR_GG_BB_xx) from red, green, and blue componets. '' // '' // RedAmount - The amount of red to add to the color byte. Between 0 and 3. '' // GreenAmount - The amount of green to add to the color byte. Between 0 and 3. '' // BlueAmount - The amount of blue to add to the color byte. Between 0 and 3. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// return ((((redAmount <# 3) #> 0) << 6) | (((greenAmount <# 3) #> 0) << 4) | (((blueAmount <# 3) #> 0) << 2) | $3) PUB PIXEngineStart(pinGroup) '' 7 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Starts up the PIX driver running on a cog. '' // '' // Returns true on success and false on failure. '' // '' // PinGroup - Pin group to use to drive the video circuit. Between 0 and 3. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// PIXEngineStop if(chipver == 1) pinGroup := ((pinGroup <# 3) #> 0) directionState := ($FF << (8 * pinGroup)) videoState := ($30_00_00_FF | (pinGroup << 9)) pinGroup := constant((25_175_000 + 1_600) / 4) frequencyState := 1 repeat 32 pinGroup <<= 1 frequencyState <-= 1 if(pinGroup => clkfreq) pinGroup -= clkfreq frequencyState += 1 displayIndicatorAddress := @displayIndicator syncIndicatorAddress := @syncIndicator cogNumber := cognew(@initialization, @displayBuffer) result or= ++cogNumber PUB PIXEngineStop '' 3 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Shuts down the PIX driver running on a cog. '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(cogNumber) cogstop(-1 + cogNumber~) DAT ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' PIX Driver ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// org 0 ' //////////////////////Initialization///////////////////////////////////////////////////////////////////////////////////////// initialization mov vcfg, videoState ' Setup video hardware. mov frqa, frequencyState ' movi ctra, #%0_00001_101 ' ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' Active Video ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// loop mov displayCounter, par ' Set/Reset tiles fill counter. mov tilesCounter, #120 ' tilesDisplay mov tileCounter, #4 ' Set/Reset tile fill counter. tileDisplay mov vscl, visibleScale ' Set/Reset the video scale. mov counter, #40 ' ' //////////////////////Visible Video////////////////////////////////////////////////////////////////////////////////////////// videoLoop rdlong buffer, displayCounter ' Download new pixels. add displayCounter, #4 ' or buffer, HVSyncColors ' Update display scanline. waitvid buffer, #%%3210 ' djnz counter, #videoLoop ' Repeat. ' //////////////////////Invisible Video//////////////////////////////////////////////////////////////////////////////////////// mov vscl, invisibleScale ' Set/Reset the video scale. waitvid HSyncColors, syncPixels ' Horizontal Sync. ' //////////////////////Repeat///////////////////////////////////////////////////////////////////////////////////////////////// sub displayCounter, #160 ' Repeat. djnz tileCounter, #tileDisplay ' add displayCounter, #160 ' Repeat. djnz tilesCounter, #tilesDisplay ' ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' Inactive Video ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// add refreshCounter, #1 ' Update sync indicator. wrbyte refreshCounter, syncIndicatorAddress ' ' //////////////////////Front Porch//////////////////////////////////////////////////////////////////////////////////////////// mov counter, #11 ' Set loop counter. frontPorch mov vscl, blankPixels ' Invisible lines. waitvid HSyncColors, #0 ' mov vscl, invisibleScale ' Horizontal Sync. waitvid HSyncColors, syncPixels ' djnz counter, #frontPorch ' Repeat # times. ' //////////////////////Vertical Sync////////////////////////////////////////////////////////////////////////////////////////// mov counter, #(2 + 2) ' Set loop counter. verticalSync mov vscl, blankPixels ' Invisible lines. waitvid VSyncColors, #0 ' mov vscl, invisibleScale ' Vertical Sync. waitvid VSyncColors, syncPixels ' djnz counter, #verticalSync ' Repeat # times. ' //////////////////////Back Porch///////////////////////////////////////////////////////////////////////////////////////////// mov counter, #31 ' Set loop counter. backPorch mov vscl, blankPixels ' Invisible lines. waitvid HSyncColors, #0 ' mov vscl, invisibleScale ' Horizontal Sync. waitvid HSyncColors, syncPixels ' djnz counter, #backPorch ' Repeat # times. ' //////////////////////Update Display Settings//////////////////////////////////////////////////////////////////////////////// rdbyte buffer, displayIndicatorAddress wz ' Update display settings. muxnz dira, directionState ' ' //////////////////////Loop/////////////////////////////////////////////////////////////////////////////////////////////////// jmp #loop ' Loop. ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' Data ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// invisibleScale long (16 << 12) + 160 ' Scaling for inactive video. visibleScale long (4 << 12) + 16 ' Scaling for active video. blankPixels long 640 ' Blank scanline pixel length. syncPixels long $00_00_3F_FC ' F-porch, h-sync, and b-porch. HSyncColors long $01_03_01_03 ' Horizontal sync color mask. VSyncColors long $00_02_00_02 ' Vertical sync color mask. HVSyncColors long $03_03_03_03 ' Horizontal and vertical sync colors. ' //////////////////////Configuration Settings///////////////////////////////////////////////////////////////////////////////// directionState long 0 videoState long 0 frequencyState long 0 ' //////////////////////Addresses////////////////////////////////////////////////////////////////////////////////////////////// displayIndicatorAddress long 0 syncIndicatorAddress long 0 ' //////////////////////Run Time Variables///////////////////////////////////////////////////////////////////////////////////// counter res 1 buffer res 1 tileCounter res 1 tilesCounter res 1 refreshCounter res 1 displayCounter res 1 ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// fit 496 DAT ' //////////////////////Variable Arrary//////////////////////////////////////////////////////////////////////////////////////// displayBuffer long 0[(160 * 120) / 4] ' Display buffer. displayIndicator byte 1 ' Video output control. syncIndicator byte 0 ' Video update control. cogNumber byte 0 ' Cog ID. ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {{ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TERMS OF USE: MIT License /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the // Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }}
I beg to differ - Catalina allows you to run exactly the same C code you run on your PC on your Propeller!
Catalina supports launching C (or Spin or PASM for that matter) in multiple cogs using syntax that will be very familiar to anyone who has ever used Posix threads, plus it has full VGA, TV, serial device, file system support using standard C syntax, plus it gives you access to all the unique facilities of the Propeller.
Yes, I expect GCC may catch up eventually ... but why wait?
Ross.
P.S. Sorry, Ken - but you asked for this one
Just a little clarification here. Technically as far as syntax goes C is C is C...That is to say that Catalina or ICC or propgcc or whatever C are designed to support the same standardized C syntax. (Give or take a few standard revisions). The syntax is not modified to support Propeller features.
Next up we have to consider the environment your C code runs in. One of the reasons C became so popular is that it did specify a minimal input output system based on library functions. Old languages like Algol did not do that and every "Hello world" program ended up using some weird IO system local to that compiler or that machine. Things were very unportable. (N.B. Spin is still in that era). So C has things like printf for dealing with the console and fopen, fclose, fprintf, fscanf etc etc for dealing with files and so on.
So yes any standard code can run on any machine supporting the standard without modification. As Catalina does and propgcc surely will.
Next up we come to all those features of embedded devices that are not mentioned in any standard. The classic simple case being how to set a pin to input or out put and how to read and write it's state. For sure every compiler and every system does this a different way. Remember the C syntax is not changed to do this but different library functions or macros may be supplied to do it. Or perhaps you are just left to know the address of the register that drives that pin and have to figure out how to read/write it your self. (Much like Spin). In the extreme on something like a Linux system or Windows you may end up having to write an entire device driver to connect your user application to the hardware!
In the embedded world all this customization of I/O and special device features is pretty much taken for granted.
I have to take issue with RossH though, the vast majority of C programs that will run on a PC will not run on your embedded system just because your compiler supports the C standard libraries. There is always a pile of operating system and or hardware dependencies that make it unlikely your code will work with out modification or perhaps porting the OS interface from one OS to another. Why do we need things like cygwin and mingw otherwise?
Having said all that. GCC does have a pile of non-standard extensions to the actual C syntax. A wise programmer wanting to create cross-platform code would not use such extensions. I am guilty of using one such extension in order to get my full duplex serial driver compiled into native COG code to run at 115200 baud.
But if you restrict yourself to ANSI C programs (i.e. C programs written in the last 20 years or so) then yes they will - provided (of course) that there is enough RAM space for them to do so. Ummm. This is also a bit of a red herring. Yes, if I write a program that uses some OS-specific libraries, then it will probably not work on another OS. But this has nothing to do with C itself - it is true in any language. A good question - but I'm not sure I see the relevance to this particular argument. Cygwin and/or MinGW are required by GCC because GCC is specifically intended to run in a Linux-like OS environment, not a Windows one. This is not true of other C compilers (including Catalina), and it also has nothing to do with the language supported by the compiler.
Ross.
True, no argument there. But of course programs with those restrictions are
basically useless on most MCU systems. Take that simple I/O pin access for
example. I have an application that uses a lot of general purpose I/O bits
running on an industrial PC under Linux. We created a Linux device driver for
those I/O's. So yes, the app can use standard functions to access those I/Os
now. But would you really want to carry all that baggage to a small system
where INA/OUTA would do?
Yes it is. OS specific or hardware specific the problem is always there.
I'm just hinting that on a Propeller or other MCU we don't really need all those
standard libs. Spin users have not missed such luxuries so far.
A very good point. Given a standardized C language which also standardizes the
I/O and such, the run time environment. How the hell did the GNU guys come
up with a compiler that doesn't just compile anywhere that has a similarly
standardized compiler to start with? After all when all is said and done it's
just read text in, write binary out.
It's been long time since I've been here, and now I see this. However it comes out, my opinion is that C on the Prop is .... hmmmmmmmmm..... unnecessary. You are killing what Prop can do with this. I never could understand all these *MM things. And over 5 KB "Hello world" .... Nothing good.
Bad direction. I have nothing more to say.
Congratulations on writing your first C program! I think you'll find that most fairly generic C code will run on the PropBOE as long as it fits into hub memory. Also, I've just finished an SD cache driver that will allow larger programs to run on the PropBOE or any Propeller board with an SD card interface. This gives an almost limitless amount of code space but data will still be restricted to hub memory since the SD card is currently treated as read-only. Of course, programs will run much slower from the SD card but performance may be good enough for a non-time-critical main program. Anyway, have fun with C on the Propeller and feel free to ask questions if you find yourself getting into difficulties.
David
You can use C as a kind of "structured assembler" and write programs that will never be portable, were never intended to be portable, and would generally be better off actually written in assembler if you could be bothered to do so - Dr_A just posted a perfect example in the Catalina 3.4 thread (a driver for an LCD display).
Or you can use C and write extremely portable programs, but at the cost of efficiency. Rayman's chess program was a good exampe of the latter, also posted in the same thread.
But you also find programs designed to be embedded that are written in ANSI C. Take the Lua scripting language - 100% ANSI C, runs fine on the Propeller (well, a little slow on the Prop 1 perhaps - but it will be perfect for the Prop 2!).
However, most C programs (ANSI or not) fall somewhere in between. A well structured C program would typically have >95% completely portable C that will compile correctly on any C compiler, and <5% platform (or OS) specific C that won't. Good examples are the xvi text editor, or David Betz' xbasic. In both these examples the platform specific code limited to a single file of no more than a couple of dozen lines of code - and both run fine on the Propeller. Are these programs "useless" on an MCU? Well, for you perhaps they are - but for others they most definitely are not!
What baggage? If you don't use any of the standard library functions, they cost you nothing.
Ross.
(And congratulations on joining the ranks of C programmers, Ken!)
Eric
If we're going to be pedantic "syntax that will be very familiar" is not "exactly the same"! Propgcc actually has a pthreads library. It's still a work in progress, but you can launch threads on other cogs in propgcc using standard pthreads functions, not "pthreads-like" functions.
Of course Catalina could adopt the same library -- it's a fine compiler. But none of us are in a position to throw stones about who is "most standards compliant". Neither Catalina nor the current propgcc distribution is fully compliant with the latest C standard (C99). I daresay propgcc is closer to compliant with C99, and Catalina is probably closer to compliant with the previous C89 standard.
As did you
Eric
If you use only gcc (and it's assembler gas) then code in cogs can communicate with the main code -- they can share variables in hub ram on so forth. And in fact you can often write the cog code in C, which is certainly easier than PASM (but a little bigger and slower).
For many drivers restricting the interaction to a memory block pointed to by PAR makes sense, and it does make it easier to port the code between compilers (and lets you do neat things like loading the driver from an sd card, as you showed). So that's probably the preferred way to do it wherever possible.
Eric
Your post is based on the premise that there is one direction and that implementing a C compiler for the prop in some way removes all the other methods of programming the device.
It doesn't.
What it does do is reduce the investment required for an embedded development shop to start using the propeller. They no longer have to learn a chip-specific language or asm, they can use the most commonly used language in the world. C.
So, keep on using SPIN, keep on using PASM. C just opens up our influence to a whole other world of developers and existing code.
Yeep, I guess 99% of embedded Software worldwide is written in C.
For my point of view : THANKS to all the people, they developed Catalina, ICCv7 and now propgcc.
best regards,
Reinhard
The point of C on the propeller chip is to prepare for C on the propeller 2. Alot of bugs and how to do things can be worked out now and then port to the new hardware when it comes out.
@ Ken
If people want ultra portable C code that works on any platform then they must have functions that must be called for any device specific functionality not in the C library or standard. This isn't really a huge issue. Any developer would just have to write the code more carfully and not use DIRA and OUTA but a function that could be replaced that sets DIRA or OUTA to hide the details of the underlying hardware.
http://mindview.net/CDs/ThinkingInC/beta3
There is also a free version of the book "Thinking in C++" in pdf form available here: http://www.lib.ru.ac.th/download/e-books/TIC2Vone.pdf
Volume two is available here: http://www.lib.ru.ac.th/download/e-books/Tic2Vtwo.pdf
However, volume two is less interesting for Microcontroller use.
propgcc does support C++, and we will use it to great effect in the long run.
This on-line tutorial is pretty good: http://www.cprogramming.com/tutorial/c/lesson1.html
Bob Lawrence recommended this book: http://publications.gbdirect.co.uk/c_book/ It is a good reference.
But I'll be quite impressed if propGCC can actually support pthreads (in its entirety) on the Prop 1! I'm not throwing stones - and certainly not about C99 - neither Catalina nor GCC claim to be C99 compliant, and I don't think C99 is all that relevant for embedded systems anyway - but I do think C89 compliance is important, and I have already said I expect GCC to become more C89 compliant over time. The main point (to bring this back to Ken's original posts) is that I believe such compliance to be necessary to the success of C on the Propeller, whereas some others obviously don't. But that's fine - everyone is entitled to their opinion, no matter how muddle-headed it may be
I guess we should let Ken have his thread back at this point - but I'd be happy to carry on this discussion in another thread.
Ross.
Say what? Surely the point of the propGCC project is to be able to offer C on both the Propeller 1 and the Propeller 2 ???? Or is propGCC on the Prop 1 just going to stay in alpha mode?
Ross.
I think we'll leave the rest to another thread and/or another time.
Eric
All my private hobby code is also following C89. There's very little in C99 that I find particularly useful, with the exception of the occasional need for declarations-following-code, but that's often a mis-used feature anyway and all in all it's better without it.
-Tor
Thanks!
BTW: I think GCC and Catalina will both be important for whatever Prop2 is called....