Okay, thanks for the suggestion. That will work for now. I think it would be better to allow the declaration decorations though since your approach depends on the fact that Catalina currently puts the stack and hence local variables in hub memory. If a new memory layout were to be defined that placed the stack in external memory this technique wouldn't work any more. Seems like it would be better to be explicit about where variables are to be allocated.
I found I kept forgetting the rules, so I solved the problem by always adding a comment after an array declaration. Declare an array at the beginning? Then add a comment "// this array is in external memory".
Declare an array in the "main" or in a function? Add a comment "// this array is in hub memory".
This has worked so far for my ailing memory!
Re the RTC, I've got cogjects working in C for a graphics driver and for serial. I like this approach because the C is standard looking C, with no tricks. The pasm part can be loaded, run, and for a RTC, this is where the pasm part could be overwritten with something else once it has run and you have read/changed the time.
Then you just need some C code to replicate the Spin code. I might take a look at this when I get home.
I'm finding it more flexible to do things this way rather than asking Ross to add code 'internally' to catalina.
+-/*sqrt all match and are faster. ATan, ATan2, ACos, ASin are more accurate and faster (uses CORDIC now; original used a 5th order polynomial approximation with 2 sqrts!! Aslo, the original Atan2 performed a division, then called ATan.) I also added Exp2 and Log2, as well as the other functions from Float32A. (the original also had issues with the Exp & Log functions for very large and very small values). All that to say, I'm done testing against Excel, and haven't uncovered any corner cases where it fails, though I'm sure they exist. F32 has at least fixed a bunch of corner cases that existed in the original code.
Jonathan
Thanks Jonathan. I'll look at integrating it for the next release.
... maybe this really belong to the 2.9 thread, it's untested on 3.0.
No worries - I'll try and get time this weekend to come up with a 3.0 equivalent. Also, if you are considering creating a new target directory, consider starting from the "custom" target directory (i.e. C:\Program Files\Catalina\custom) - that is intended to be a "minimal" target directory. It is probably a lot less intimidating to start from that and add stuff rather than starting from the "target" directory and removing stuff!
I'm finding it more flexible to do things this way rather than asking Ross to add code 'internally' to catalina.
Hi Dr_A.
I keep hoping that Catalina is advanced and mature enough that it is no longer necessary for me to do all the work (not that I don't want to - more that I don't always have the the time!).
I think the work that you're doing is good evidence that this may finally be coming true!
More specifically, this is part of another cogject, and these can be loaded and run independent of the catalina system.
This demo program loads up a 64 color demo, draws some boxes and pixels, then unloads that and loads in a 4 color grayscale demo at higher resolution. See attached photo. I think this has the potential to be the beginnings of a GUI for the propeller.
See the attached cog files - unzip these and put them on the sd card. The nice thing about these .cog files is that they are the same ones I'm using for the Spin demonstration. Potentially this could be done in Propbasic as well.
Next step is to create some font libraries. Almost at the "Hello World" stage!
/* PASM cogject demonstration, see also cogject example in spin*/
#include <stdio.h>
#include <string.h>
unsigned long cogarray[512]; // external memory common cog array
unsigned long screen_external[4800] ; // external memory for a screen buffer
// start of C functions
void clearscreen() // white text on dark blue background
{
int i;
for (i=0;i<40;i++)
{
t_setpos(0,0,i); // move cursor to next line
t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100
}
}
void sleep(int milliseconds) // sleep function
{
_waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
}
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void 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
}
int EoF (FILE* stream)
{
register int c, status = ((c = fgetc(stream)) == EOF);
ungetc(c,stream);
return status;
}
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
}
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 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(0); // not video
_cogstop(1); // video shut down this driver
// cog 2 is catalina
_cogstop(3); // video shut down this driver
//_cogstop(4); // not sure what this does, hangs prop when add this
//_cogstop(5); // not video
//_cogstop(6); // not video
external_memory_cog_load(7,cogarray,screen); // load from external ram, pass some values in screen[]
pix_clearscreen(screen); // clear the screen to black
}
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 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 color_demo(unsigned long screen[],char color_parameters[]) // kye graphics demo
{
clearscreen(); // white on blue vga
printf("Clock speed %u \n",_clockfreq()); // see page 28 of the propeller manual for other useful commands
printf("Catalina running in cog number %i \n",_cogid()); // integer
printf("Kye graphics demonstration \n");
//sleep(2000); // 2 sec delay
readcog("vgagraph.cog",cogarray); // read in kye's graphics driver
pixenginestart(7,cogarray,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
}
// ****************** end color screen code *********************************
// ****************** begin gray screen code ********************************
void gray_pixel(unsigned long screen[], unsigned long x, unsigned long y, unsigned long color)
{
// unsigned long bitsperpixel = gray_parameters[3];
unsigned long bitsperpixel = 1; // always is 1
unsigned long i;
i = (x >>4) + (20 * y); // i = the long that contains our pixel
x = ((x & (0x1f >> bitsperpixel)) << bitsperpixel);
//mask = (0x3 << xpixel); // xpixel holds the number of bits to shift to the left, and invert this
//mask = ~mask; // invert this
screen[i] = (( screen[i] & ~(0x3 << x)) | (color << x)); // do the and with a mask and the or in one line
}
void gray_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++)
{
gray_pixel(screen,col,starty,color); // horizontal lines
gray_pixel(screen,col,endy,color);
}
for(row=starty;row <= endy;row++)
{
gray_pixel(screen,startx,row,color);
gray_pixel(screen,endx,row,color);
}
}
void gray_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++)
{
gray_pixel(screen,col,row,color); // horizontal line
}
}
}
void gray_button(unsigned long screen[],int startx, int starty, int sizex, int sizey, char *lineoftext)
{
gray_line(screen, startx, starty, startx, starty+sizey, 0x3); // white vertical line
gray_line(screen, startx, starty, startx+sizex, starty, 0x3); // white horizontal line
gray_line(screen, startx+sizex, starty+1, startx+sizex, starty+sizey-1, 0x1); // vertical dark gray line
gray_line(screen, startx+1, starty+sizey, startx+sizex, starty+sizey, 0x1); // horizontal dark gray line
gray_line(screen, startx+sizex+1, starty, startx+sizex+1, starty+sizey+1, 0x0); // vertical black line
gray_line(screen, startx, starty+sizey+1, startx+sizex+1, starty+sizey+1, 0x0); // horizontal black line
// add in the text
}
void gray_text(unsigned long screen[], int startx, int starty, int sizex, int sizey, char *lineoftext)
{
gray_box_fill(screen, startx+1, starty+1, startx+sizex-1, starty+sizey-1,0x3); // white centre
gray_line(screen, startx, starty, startx, starty+sizey-1, 0x0); // black vertical line
gray_line(screen, startx, starty, startx+sizex-1, starty, 0x0); // black horizontal line
gray_line(screen, startx+sizex, starty, startx+sizex, starty+sizey,0x2); // vertical light gray line
gray_line(screen, startx, starty+sizey, startx+sizex, starty+sizey, 0x2); // horizontal light gray line
gray_line(screen, startx-1, starty-1, startx-1, starty+sizey+1, 0x1); // dark gray vertical line
gray_line(screen, startx-1, starty-1, startx+sizex, starty-1, 0x1); // dark gray horizontal line
gray_line(screen, startx+sizex+1, starty-1, startx+sizex+1, starty+sizey+1, 0x3); // vertical whiteline
gray_line(screen, startx-1, starty+sizey+1, startx+sizex, starty+sizey+1, 0x3); // horizontal whiteline
}
void gray_radio(unsigned long screen[], int startx, int starty, char check)
{
char radio[144] = { // black = 0x0, dark gray = 0x1, light gray = 0x2, white = 0x3
0x2,0x2,0x2,0x2,0x1,0x1,0x1,0x1,0x2,0x2,0x2,0x2, // a radio button
0x2,0x2,0x1,0x1,0x0,0x0,0x0,0x0,0x1,0x1,0x2,0x2,
0x2,0x1,0x0,0x0,0x3,0x3,0x3,0x3,0x0,0x0,0x3,0x2,
0x2,0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,0x2,
0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,
0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,
0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,
0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,
0x2,0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,0x2,
0x2,0x1,0x2,0x2,0x3,0x3,0x3,0x3,0x2,0x2,0x3,0x2,
0x2,0x2,0x3,0x3,0x2,0x2,0x2,0x2,0x3,0x3,0x2,0x2,
0x2,0x2,0x2,0x2,0x3,0x3,0x3,0x3,0x2,0x2,0x2,0x2
};
int x;
int y;
int i = 0;
if(check !=0)
{
radio[53]=radio[54]=0x00;
radio[64]=radio[65]=radio[66]=radio[67]=0x00; // draw the circle in the middle
radio[76]=radio[77]=radio[78]=radio[79]=0x00;
radio[89]=radio[90]=0x00;
}
for(y=0;y<12;y++)
{
for(x=0;x<12;x++)
{
gray_pixel(screen,startx+x, starty+y, radio[i]); // draw a radio button, white centre
i++;
}
}
}
void gray_letter(unsigned long screen[], int startx, int starty) // draw a letter A on the screen in black with white background - for testing
{
char letter[63] = {
0x3,0x3,0x3,0x0,0x3,0x3,0x3,
0x3,0x3,0x3,0x0,0x3,0x3,0x3,
0x3,0x3,0x0,0x3,0x0,0x3,0x3,
0x3,0x3,0x0,0x3,0x0,0x3,0x3,
0x3,0x0,0x3,0x3,0x3,0x0,0x3,
0x3,0x0,0x3,0x3,0x3,0x0,0x3,
0x3,0x0,0x0,0x0,0x0,0x0,0x3,
0x0,0x3,0x3,0x3,0x3,0x3,0x0,
0x0,0x3,0x3,0x3,0x3,0x3,0x0
};
int x,y;
int i =0;
for(y=0;y<9;y++)
{
for(x=0;x<7;x++)
{
gray_pixel(screen,startx+x, starty+y, letter[i]); // draw a letter
i++;
}
}
}
void gray_clearscreen(unsigned long screen[])
{
int i;
for (i=0;i<4800;i++)
{
screen[i] = 0xaaaaaaaa; // fill with light gray, use longs rather than bytes so 4 pixels per loop
}
}
void gray_engine_start(int cognumber, unsigned long cogarray[], unsigned long screen[], unsigned long gray_parameters[])
{
unsigned long pingroup = 2; // pins 16 to 23
int i;
unsigned long frequencystate;
gray_parameters[0] = 0xfca85400; // pixelcolors in reverse order
gray_parameters[1] = 1; // displayindicator
gray_parameters[2] = 0; // syncindicator
gray_parameters[3] = 2; // bitsperpixel, starts at 2 but gets changed later to 1
screen[0] = (unsigned long) &screen[0]; // the first long in the screen array now points to the location of itself
screen[1] = (unsigned long) &gray_parameters[1]; // pointer to displayindicator
screen[2] = (unsigned long) &gray_parameters[2]; // pointer to syncindicator
screen[3] = (0xff << (8*pingroup));
screen[4] = (0x200000ff | (pingroup << 9) | ((gray_parameters[3] -1) << 28)); // (0x200000ff | (pingroup << 9) | ((bitsperpixel-1) << 28)); this one not working
gray_parameters[3] = gray_parameters[3] - 1; // bitsperpixel now equal to 1, used below
frequencystate = ((25175000 + 1600) / 4); // calculate frequencystate
screen[5] = 1;
for(i=0;i<32;i++)
{
frequencystate = frequencystate << 1;
screen[5] = (screen[5] << 1) | (screen[5] >> 31);
if (frequencystate >= _clockfreq())
{
frequencystate -= _clockfreq();
screen[5] += 1;
}
}
screen[6] = 2; // horizontal scaling
screen[7] = 2; // vertical scaling
screen[8] = 320; // horizontal pixels
screen[9] = 240; // vertical pixels
screen[10] = ((screen[6] << 12) + (((640 * 32) >> gray_parameters[3] ) / screen[8])); // visible scale
screen[11] = (((8 << gray_parameters[3] ) << 12) + 160); // invisible scale
screen[12] = (screen[8] / (32 >> gray_parameters[3] )); // horizongal longs
screen[13] = screen[12] * 4; // horizontal loops
screen[14] = (unsigned long) &gray_parameters[0]; // pixel colors
_cogstop(1); // stop graphics cog
_cogstop(3); // stop graphics cog
external_memory_cog_load(7,cogarray,screen); // load from external ram, pass some values in screen[]
gray_clearscreen(screen);
}
void gray_demo(unsigned long screen[], unsigned long gray_parameters[])
{
clearscreen(); // white on blue vga
printf("Clock speed %u \n",_clockfreq()); // see page 28 of the propeller manual for other useful commands
printf("Catalina running in cog number %i \n",_cogid()); // integer
printf("Gray scale 320x240 graphics demonstration \n");
readcog("vga320.cog",cogarray);
gray_engine_start(7,cogarray,screen,gray_parameters);
gray_button(screen, 50,50,40,20,"Button1"); // a button
gray_text(screen, 200,80, 40, 20, "Text1"); // a text box
gray_letter(screen,210,87); // print a letter A in the text box
gray_radio(screen, 10,10,1); // a checked radio button
gray_radio(screen, 10,30,0); // an unchecked radio button
}
void main ()
{
char color_parameters[1]; // displayindicator and syncindicater are permanent hub variables
unsigned long screen[160*120/4]; // graphics and text video buffer in hub - eg 19200 bytes
unsigned long gray_parameters[4]; // permanent hub variables for the gray 320x240 driver
color_demo(screen,color_parameters);
gray_demo(screen, gray_parameters); // gray screen demo
while (1); // endless loop as prop reboots on exit from main()
}
I was thinking of a standardized configuration, like DemoBoard+RamBlade or ProtoBoard in strict "DemoBoard wannabe mode" i.e. with VGA/PS2 kit and RamBlade, using 3 fixed pins from P0..P7 range.
Hi Alesandro,
I have attached a skeleton to show how I would implement such dual-cpu support in Catalina 3.0. I have defined a new platform called DEMOBLADE - which also requires you to specify either CPU_1 or CPU_2.
CPU_1 looks like a DEMO board but without TV support, and CPU_2 looks like a RAMBLADE with XMM support. I have included amended versions of Catalina_Common.spin as well as HMI.inc and XMM.inc (which are the most complex files you would probably need to modify).
I'm sorry I didn't post the inc files too, I made that thing on 2.9 some weeks ago, and completely forgot about having to modify those too.
Now I'm getting there again, translating the build_morpheus_xxx files to build_demoblade_xxx, they are close enough and compilation goes well. I seem to recall I had to tweak the VGA frequencies for my stupid LCD panel, and after that most proxy demos were working fine (using a fixed proxy, loaded in eeprom on the demo/proto side.
When I get the build files done I'll upload them here, Cluso had a similar configuration pictured in the RamBlade thread, so maybe he'll want to give it a try.
The PropGUI is starting to take shape, see photo at the bottom. We have a font now and external memory is the perfect place for this - doesn't take up precious hub ram but is faster than putting it on an SD card.
Textbox, Button, Radio, Menu, Label and Groupbox all working. Easy to use in C:
gray_printf(screen, 8,8,"File Edit Program"); // print menu text on the screen
gray_button(screen, 10, 40,50,19,"Button1"); // a button
gray_button(screen, 100,40,50,19,"Button2"); // a button
gray_text(screen, 10, 70, 70,16, "Textbox1"); // a text box
gray_text(screen, 100,70, 70,16, "Textbox2"); // a text box
gray_radio(screen, 10,100,1,"Radio1"); // a checked radio button
gray_radio(screen, 10,120,0,"Radio2"); // an unchecked radio button
gray_groupbox(screen,140,110,100,85,"GroupBox1"); // draw groupbox
gray_printf(screen, 150,125,"Label1"); // print text on the screen
gray_printf(screen, 150,145,"Label2"); // print text on the screen
gray_printf(screen, 150,165,"Label3"); // print text on the screen
(everything starts with "gray" to distinguish from the chunkier color version).
Full code below including the font array:
/* PASM cogject demonstration, see also cogject example in spin*/
#include <stdio.h>
#include <string.h>
unsigned long cogarray[512]; // external memory common cog array
unsigned long screen_external[4800] ; // external memory for a screen buffer
// 128 rows, one for each ascii character 0 to 127
// first long is 0000HHWW where HH is the height in hex and WW is the width in hex
unsigned long sans_serif_font[1664] =
{
0x00000c00,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c00,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
0x00000c00,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c00,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c00,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c00,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000000, 0x00000002, 0x00000000, 0x00000000,
0x00000c05,0x00000000, 0x00000009, 0x00000009, 0x00000009, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x00000012, 0x00000012, 0x0000003f, 0x00000012, 0x00000012, 0x00000012, 0x0000003f, 0x00000012, 0x00000012, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000004, 0x0000000e, 0x00000015, 0x00000014, 0x0000000c, 0x00000006, 0x00000005, 0x00000015, 0x0000000e, 0x00000004, 0x00000000,
0x00000c08,0x00000000, 0x00000030, 0x00000049, 0x00000032, 0x00000004, 0x00000008, 0x00000010, 0x00000026, 0x00000049, 0x00000006, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000008, 0x00000014, 0x00000014, 0x00000008, 0x00000008, 0x00000015, 0x00000012, 0x00000012, 0x0000000d, 0x00000000, 0x00000000,
0x00000c02,0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000001, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000001,
0x00000c03,0x00000000, 0x00000002, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000002,
0x00000c04,0x00000000, 0x00000005, 0x00000002, 0x00000005, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000004, 0x00000004, 0x0000001f, 0x00000004, 0x00000004, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000002, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000,
0x00000c05,0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000002, 0x00000002, 0x00000004, 0x00000004, 0x00000008, 0x00000008, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000000e, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x0000000e, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000004, 0x0000001c, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000000e, 0x00000011, 0x00000001, 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x0000001f, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000000e, 0x00000011, 0x00000001, 0x00000001, 0x00000006, 0x00000001, 0x00000001, 0x00000011, 0x0000000e, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000002, 0x00000006, 0x00000006, 0x0000000a, 0x0000000a, 0x00000012, 0x0000001f, 0x00000002, 0x00000002, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000001f, 0x00000010, 0x00000010, 0x0000001e, 0x00000011, 0x00000001, 0x00000001, 0x00000011, 0x0000000e, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000000e, 0x00000011, 0x00000010, 0x00000010, 0x0000001e, 0x00000011, 0x00000011, 0x00000011, 0x0000000e, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000001f, 0x00000001, 0x00000002, 0x00000002, 0x00000004, 0x00000004, 0x00000008, 0x00000008, 0x00000008, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000000e, 0x00000011, 0x00000011, 0x00000011, 0x0000000e, 0x00000011, 0x00000011, 0x00000011, 0x0000000e, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000000e, 0x00000011, 0x00000011, 0x00000011, 0x0000000f, 0x00000001, 0x00000001, 0x00000011, 0x0000000e, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000002, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000001f, 0x00000000, 0x0000001f, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000000e, 0x00000011, 0x00000001, 0x00000001, 0x00000002, 0x00000004, 0x00000004, 0x00000000, 0x00000004, 0x00000000, 0x00000000,
0x00000c0b,0x00000000, 0x00000078, 0x00000186, 0x00000102, 0x00000239, 0x00000249, 0x00000249, 0x00000237, 0x00000100, 0x00000180, 0x0000007c, 0x00000000,
0x00000c07,0x00000000, 0x00000008, 0x00000008, 0x00000014, 0x00000014, 0x00000022, 0x00000022, 0x0000003e, 0x00000041, 0x00000041, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x0000003c, 0x00000022, 0x00000022, 0x00000022, 0x0000003c, 0x00000022, 0x00000022, 0x00000022, 0x0000003c, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x0000001e, 0x00000021, 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000021, 0x0000001e, 0x00000000, 0x00000000,
0x00000c08,0x00000000, 0x00000078, 0x00000044, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000044, 0x00000078, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x0000003e, 0x00000020, 0x00000020, 0x00000020, 0x0000003c, 0x00000020, 0x00000020, 0x00000020, 0x0000003e, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x0000001f, 0x00000010, 0x00000010, 0x00000010, 0x0000001e, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000000, 0x00000000,
0x00000c08,0x00000000, 0x0000003c, 0x00000042, 0x00000040, 0x00000040, 0x0000004e, 0x00000042, 0x00000042, 0x00000046, 0x0000003a, 0x00000000, 0x00000000,
0x00000c08,0x00000000, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x0000007e, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000000, 0x00000000,
0x00000c05,0x00000000, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000012, 0x00000012, 0x0000000c, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x00000022, 0x00000024, 0x00000028, 0x00000030, 0x00000030, 0x00000028, 0x00000024, 0x00000022, 0x00000021, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x0000001f, 0x00000000, 0x00000000,
0x00000c09,0x00000000, 0x00000082, 0x00000082, 0x000000c6, 0x000000c6, 0x000000aa, 0x000000aa, 0x00000092, 0x00000092, 0x00000082, 0x00000000, 0x00000000,
0x00000c08,0x00000000, 0x00000042, 0x00000062, 0x00000062, 0x00000052, 0x00000052, 0x0000004a, 0x00000046, 0x00000046, 0x00000042, 0x00000000, 0x00000000,
0x00000c08,0x00000000, 0x0000003c, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x0000003c, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x0000003e, 0x00000021, 0x00000021, 0x00000021, 0x0000003e, 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000000, 0x00000000,
0x00000c08,0x00000000, 0x0000003c, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x0000004a, 0x00000046, 0x0000003c, 0x00000002, 0x00000000,
0x00000c08,0x00000000, 0x0000007c, 0x00000042, 0x00000042, 0x00000042, 0x0000007c, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x0000001c, 0x00000022, 0x00000020, 0x00000020, 0x0000001c, 0x00000002, 0x00000002, 0x00000022, 0x0000001c, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x0000003e, 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000000, 0x00000000,
0x00000c08,0x00000000, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x00000042, 0x0000003c, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x00000041, 0x00000041, 0x00000022, 0x00000022, 0x00000022, 0x00000014, 0x00000014, 0x00000008, 0x00000008, 0x00000000, 0x00000000,
0x00000c0b,0x00000000, 0x00000401, 0x00000401, 0x00000222, 0x00000222, 0x00000222, 0x00000154, 0x00000154, 0x00000088, 0x00000088, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x00000041, 0x00000041, 0x00000022, 0x00000014, 0x00000008, 0x00000014, 0x00000022, 0x00000041, 0x00000041, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x00000041, 0x00000041, 0x00000022, 0x00000014, 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000000, 0x00000000,
0x00000c07,0x00000000, 0x0000007f, 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020, 0x00000040, 0x0000007f, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000003, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000003,
0x00000c05,0x00000000, 0x00000008, 0x00000008, 0x00000008, 0x00000004, 0x00000004, 0x00000002, 0x00000002, 0x00000001, 0x00000001, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000003, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000003,
0x00000c06,0x00000004, 0x0000000a, 0x00000011, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000003f,
0x00000c03,0x00000000, 0x00000002, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000e, 0x00000001, 0x0000000f, 0x00000011, 0x00000011, 0x0000000f, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000010, 0x00000010, 0x00000010, 0x0000001e, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x0000001e, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000e, 0x00000011, 0x00000010, 0x00000010, 0x00000011, 0x0000000e, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x0000000f, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x0000000f, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000e, 0x00000011, 0x0000001f, 0x00000010, 0x00000011, 0x0000000e, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000001, 0x00000002, 0x00000002, 0x00000003, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000f, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x0000000f, 0x00000001, 0x0000001e,
0x00000c06,0x00000000, 0x00000010, 0x00000010, 0x00000010, 0x00000016, 0x00000019, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x00000000, 0x00000000,
0x00000c02,0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000,
0x00000c02,0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001,
0x00000c06,0x00000000, 0x00000010, 0x00000010, 0x00000010, 0x00000012, 0x00000014, 0x00000018, 0x00000014, 0x00000012, 0x00000011, 0x00000000, 0x00000000,
0x00000c02,0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000, 0x00000000,
0x00000c08,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000076, 0x00000049, 0x00000049, 0x00000049, 0x00000049, 0x00000049, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000016, 0x00000019, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000e, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x0000000e, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000001e, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x0000001e, 0x00000010, 0x00000010,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000f, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x0000000f, 0x00000001, 0x00000001,
0x00000c03,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000003, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000000, 0x00000000,
0x00000c05,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000006, 0x00000009, 0x00000004, 0x00000002, 0x00000009, 0x00000006, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000002, 0x00000002, 0x00000003, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000001, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000011, 0x00000011, 0x00000011, 0x00000011, 0x00000013, 0x0000000d, 0x00000000, 0x00000000,
0x00000c06,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000011, 0x00000011, 0x0000000a, 0x0000000a, 0x00000004, 0x00000004, 0x00000000, 0x00000000,
0x00000c08,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000049, 0x00000049, 0x00000055, 0x00000055, 0x00000022, 0x00000022, 0x00000000, 0x00000000,
0x00000c05,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000009, 0x00000009, 0x00000006, 0x00000006, 0x00000009, 0x00000009, 0x00000000, 0x00000000,
0x00000c05,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000009, 0x00000009, 0x00000009, 0x00000009, 0x00000006, 0x00000004, 0x00000004, 0x00000018,
0x00000c05,0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000f, 0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x0000000f, 0x00000000, 0x00000000,
0x00000c04,0x00000001, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000004, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000001, 0x00000000,
0x00000c02,0x00000000, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000001, 0x00000000,
0x00000c04,0x00000004, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000001, 0x00000002, 0x00000002, 0x00000002, 0x00000002, 0x00000004, 0x00000000,
0x00000c07,0x00000000, 0x00000000, 0x00000019, 0x00000026, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000c03,0x00000000, 0x00000000, 0x00000007, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000005, 0x00000007, 0x00000000, 0x00000000,
};
// start of C functions
void clearscreen() // white text on dark blue background
{
int i;
for (i=0;i<40;i++)
{
t_setpos(0,0,i); // move cursor to next line
t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100
}
}
void sleep(int milliseconds) // sleep function
{
_waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
}
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void 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
}
int EoF (FILE* stream)
{
register int c, status = ((c = fgetc(stream)) == EOF);
ungetc(c,stream);
return status;
}
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
}
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 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(0); // not video
_cogstop(1); // video shut down this driver
// cog 2 is catalina
_cogstop(3); // video shut down this driver
//_cogstop(4); // not sure what this does, hangs prop when add this
//_cogstop(5); // not video
//_cogstop(6); // not video
external_memory_cog_load(7,cogarray,screen); // load from external ram, pass some values in screen[]
pix_clearscreen(screen); // clear the screen to black
}
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 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 color_demo(unsigned long screen[],char color_parameters[]) // kye graphics demo
{
clearscreen(); // white on blue vga
printf("Clock speed %u \n",_clockfreq()); // see page 28 of the propeller manual for other useful commands
printf("Catalina running in cog number %i \n",_cogid()); // integer
printf("Kye graphics demonstration \n");
//sleep(2000); // 2 sec delay
readcog("vgagraph.cog",cogarray); // read in kye's graphics driver
pixenginestart(7,cogarray,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
}
// ****************** end color screen code *********************************
// ****************** begin gray screen code ********************************
void gray_pixel(unsigned long screen[], unsigned long x, unsigned long y, unsigned long color)
{
// unsigned long bitsperpixel = gray_parameters[3];
unsigned long bitsperpixel = 1; // always is 1
unsigned long i;
i = (x >>4) + (20 * y); // i = the long that contains our pixel
x = ((x & (0x1f >> bitsperpixel)) << bitsperpixel);
//mask = (0x3 << xpixel); // xpixel holds the number of bits to shift to the left, and invert this
//mask = ~mask; // invert this
screen[i] = (( screen[i] & ~(0x3 << x)) | (color << x)); // do the and with a mask and the or in one line
}
int gray_printf(unsigned long screen[],int startx, int starty, char lineoftext[])
{
int height = sans_serif_font[0] >> 8; // read from the font data the height
int width,ascii,a,x,y,i,curx;
unsigned long font;
curx=startx; // cursor
for(i=0;i<strlen(lineoftext);i++) // get each letter in the string
{
ascii=lineoftext[i];
a=ascii * 13; // calculate the address of the character
width = sans_serif_font[a] & 0x000000FF; // mask out the height
for(y=0;y<height;y++) // one row at a time
{
font=sans_serif_font[a+y+1]; // get the font long
for(x=0;x<width;x++) // do each pixel
{
if((font & 0x00000001) == 1) // test if a 1
{
gray_pixel(screen,curx+(width-1-x),starty+y,0x00);
}
font = font >> 1; // shift in the next bit
}
}
curx += width; // move cursor along by width amount
}
return curx-startx; // return the width of all the text
}
void gray_horizontal_line(unsigned long screen[], int startx, int starty, int sizex, char color)
{
int col;
for(col=startx;col <= startx+sizex ;col++)
{
gray_pixel(screen,col,starty,color); // horizontal line
}
}
void gray_vertical_line(unsigned long screen[], int startx, int starty, int sizey, char color)
{
int row;
for(row=starty;row <= starty+sizey;row++)
{
gray_pixel(screen,startx,row,color); // vertical line
}
}
void gray_box_fill(unsigned long screen[], int startx, int starty, int sizex, int sizey, char color)
{
int row,col;
for(row =starty;row <= starty+sizey; row++)
{
for(col=startx;col <= startx+sizex; col++)
{
gray_pixel(screen,col,row,color); // fill in pixels
}
}
}
void gray_groupbox(unsigned long screen[], int startx, int starty, int sizex, int sizey, char lineoftext[])
{
int textwidth;
gray_horizontal_line(screen,startx,starty,10,0x1); // dark gray horizontal line
gray_horizontal_line(screen,startx+1,starty+1,9,0x3); // white horizontal line
gray_vertical_line(screen,startx,starty,sizey,0x1); // dark gray vertical
gray_vertical_line(screen,startx+1,starty+1,sizey-1,0x3); // white vertical
gray_horizontal_line(screen,startx,starty+sizey,sizex,0x1); // dark gray horizontal line
gray_horizontal_line(screen,startx,starty+sizey+1,sizex+1,0x3); // white horizontal line
gray_vertical_line(screen,startx+sizex,starty,sizey,0x1); // dark gray vertical
gray_vertical_line(screen,startx+sizex+1,starty,sizey,0x3); // white vertical
textwidth = gray_printf(screen, startx+14,starty-4,lineoftext); // print the frame title
gray_horizontal_line(screen,startx+18+textwidth,starty,sizex-textwidth-18,0x1); // dark gray horizontal line
gray_horizontal_line(screen,startx+18+textwidth,starty+1,sizex-textwidth-19,0x3); // white horizontal line
}
void gray_button(unsigned long screen[],int startx, int starty, int sizex, int sizey, char lineoftext[])
{
gray_vertical_line(screen, startx, starty, sizey, 0x3); // white vertical line
gray_horizontal_line(screen, startx, starty, sizex, 0x3); // white horizontal line
gray_vertical_line(screen, startx+sizex, starty+1, sizey-1, 0x1); // vertical dark gray line
gray_horizontal_line(screen, startx+1, starty+sizey, sizex, 0x1); // horizontal dark gray line
gray_vertical_line(screen, startx+sizex+1, starty, sizey+1, 0x0); // vertical black line
gray_horizontal_line(screen, startx, starty+sizey+1, sizex+1, 0x0); // horizontal black line
gray_printf(screen,startx+4,starty+4,lineoftext); // print the button text
}
void gray_text(unsigned long screen[], int startx, int starty, int sizex, int sizey, char lineoftext[])
{
gray_box_fill(screen, startx+1, starty+1, sizex-2, sizey-2,0x3); // white centre
gray_vertical_line(screen, startx, starty, sizey-1, 0x0); // black vertical line
gray_horizontal_line(screen, startx, starty, sizex-1, 0x0); // black horizontal line
gray_vertical_line(screen, startx+sizex, starty+1, sizey,0x2); // vertical light gray line
gray_horizontal_line(screen, startx, starty+sizey, sizex, 0x2); // horizontal light gray line
gray_vertical_line(screen, startx-1, starty-1, sizey+1, 0x1); // dark gray vertical line
gray_horizontal_line(screen, startx-1, starty-1, sizex+1, 0x1); // dark gray horizontal line
gray_vertical_line(screen, startx+sizex+1, starty-1, sizey+2, 0x3); // vertical whiteline
gray_horizontal_line(screen, startx-1, starty+sizey+1, sizex+1, 0x3); // horizontal whiteline
gray_printf(screen,startx+2,starty+2,lineoftext); // print the text
}
void gray_radio(unsigned long screen[], int startx, int starty, char check,char lineoftext[])
{
char radio[144] = { // black = 0x0, dark gray = 0x1, light gray = 0x2, white = 0x3
0x2,0x2,0x2,0x2,0x1,0x1,0x1,0x1,0x2,0x2,0x2,0x2, // a radio button
0x2,0x2,0x1,0x1,0x0,0x0,0x0,0x0,0x1,0x1,0x2,0x2,
0x2,0x1,0x0,0x0,0x3,0x3,0x3,0x3,0x0,0x0,0x3,0x2,
0x2,0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,0x2,
0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,
0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,
0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,
0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,
0x2,0x1,0x0,0x3,0x3,0x3,0x3,0x3,0x3,0x2,0x3,0x2,
0x2,0x1,0x2,0x2,0x3,0x3,0x3,0x3,0x2,0x2,0x3,0x2,
0x2,0x2,0x3,0x3,0x2,0x2,0x2,0x2,0x3,0x3,0x2,0x2,
0x2,0x2,0x2,0x2,0x3,0x3,0x3,0x3,0x2,0x2,0x2,0x2
};
int x;
int y;
int i = 0;
if(check !=0)
{
radio[53]=radio[54]=0x00;
radio[64]=radio[65]=radio[66]=radio[67]=0x00; // draw the circle in the middle
radio[76]=radio[77]=radio[78]=radio[79]=0x00;
radio[89]=radio[90]=0x00;
}
for(y=0;y<12;y++)
{
for(x=0;x<12;x++)
{
gray_pixel(screen,startx+x, starty+y, radio[i]); // draw a radio button, white centre
i++;
}
}
gray_printf(screen,startx+16,starty,lineoftext); // print the text
}
void gray_clearscreen(unsigned long screen[])
{
int i;
for (i=0;i<4800;i++)
{
screen[i] = 0xaaaaaaaa; // fill with light gray, use longs rather than bytes so 4 pixels per loop
}
}
void gray_engine_start(int cognumber, unsigned long cogarray[], unsigned long screen[], unsigned long gray_parameters[])
{
unsigned long pingroup = 2; // pins 16 to 23
int i;
unsigned long frequencystate;
gray_parameters[0] = 0xfca85400; // pixelcolors in reverse order
gray_parameters[1] = 1; // displayindicator
gray_parameters[2] = 0; // syncindicator
gray_parameters[3] = 2; // bitsperpixel, starts at 2 but gets changed later to 1
screen[0] = (unsigned long) &screen[0]; // the first long in the screen array now points to the location of itself
screen[1] = (unsigned long) &gray_parameters[1]; // pointer to displayindicator
screen[2] = (unsigned long) &gray_parameters[2]; // pointer to syncindicator
screen[3] = (0xff << (8*pingroup));
screen[4] = (0x200000ff | (pingroup << 9) | ((gray_parameters[3] -1) << 28)); // (0x200000ff | (pingroup << 9) | ((bitsperpixel-1) << 28)); this one not working
gray_parameters[3] = gray_parameters[3] - 1; // bitsperpixel now equal to 1, used below
frequencystate = ((25175000 + 1600) / 4); // calculate frequencystate
screen[5] = 1;
for(i=0;i<32;i++)
{
frequencystate = frequencystate << 1;
screen[5] = (screen[5] << 1) | (screen[5] >> 31);
if (frequencystate >= _clockfreq())
{
frequencystate -= _clockfreq();
screen[5] += 1;
}
}
screen[6] = 2; // horizontal scaling
screen[7] = 2; // vertical scaling
screen[8] = 320; // horizontal pixels
screen[9] = 240; // vertical pixels
screen[10] = ((screen[6] << 12) + (((640 * 32) >> gray_parameters[3] ) / screen[8])); // visible scale
screen[11] = (((8 << gray_parameters[3] ) << 12) + 160); // invisible scale
screen[12] = (screen[8] / (32 >> gray_parameters[3] )); // horizongal longs
screen[13] = screen[12] * 4; // horizontal loops
screen[14] = (unsigned long) &gray_parameters[0]; // pixel colors
_cogstop(1); // stop graphics cog
_cogstop(3); // stop graphics cog
external_memory_cog_load(7,cogarray,screen); // load from external ram, pass some values in screen[]
gray_clearscreen(screen);
}
void gray_demo(unsigned long screen[], unsigned long gray_parameters[])
{
clearscreen(); // white on blue vga
printf("Clock speed %u \n",_clockfreq()); // see page 28 of the propeller manual for other useful commands
printf("Catalina running in cog number %i \n",_cogid()); // integer
printf("Gray scale 320x240 graphics demonstration \n");
readcog("vga320.cog",cogarray);
gray_engine_start(7,cogarray,screen,gray_parameters);
gray_printf(screen, 8,8,"File Edit Program"); // print menu text on the screen
gray_button(screen, 10, 40,50,19,"Button1"); // a button
gray_button(screen, 100,40,50,19,"Button2"); // a button
gray_text(screen, 10, 70, 70,16, "Textbox1"); // a text box
gray_text(screen, 100,70, 70,16, "Textbox2"); // a text box
gray_radio(screen, 10,100,1,"Radio1"); // a checked radio button
gray_radio(screen, 10,120,0,"Radio2"); // an unchecked radio button
gray_groupbox(screen,140,110,100,85,"GroupBox1"); // draw groupbox
gray_printf(screen, 150,125,"Label1"); // print text on the screen
gray_printf(screen, 150,145,"Label2"); // print text on the screen
gray_printf(screen, 150,165,"Label3"); // print text on the screen
}
void main ()
{
char color_parameters[1]; // displayindicator and syncindicater are permanent hub variables
unsigned long screen[160*120/4]; // graphics and text video buffer in hub - eg 19200 bytes
unsigned long gray_parameters[4]; // permanent hub variables for the gray 320x240 driver
color_demo(screen,color_parameters);
gray_demo(screen, gray_parameters); // gray screen demo
while (1); // endless loop as prop reboots on exit from main()
}
Good question. The gray graphics driver is 320x240 with 4 colors which is the maximum color depth (I think?) for 19200 bytes of hub ram. There are some other functions in there that start with "pix" - these are from Kye's 64 color driver which is 160x120 and "pix" is what Kye used in Spin.
The demo program starts off with white on blue text 80x40, then changes to the 160x120 color and draws some boxes and a color bar, then switches to gray 320x240.
I'd love to get some more colors!
But I don't think the prop can do this... unless you can pull data in from external memory fast enough?
At the moment what I am thinking is you have the color version for games, and things like the movie demo and icons, and then you have the higher resolution gray version for "work", eg a text editor, or a little calculator program.
I did originally code the first few buttons in the color 160x120 version but it looks very chunky on a VGA screen. However, it might look a lot better on a smaller LCD TV screen, and hopefully this might be possible to port over to TV as well.
Lots of scope for improvement here!
BTW, could this run on your nifty 32meg board? I think Ross has catalina working now. I have a feeling this might end up a largish program - it is 128k already and might end up going over 512k so the extra memory could come in very handy.
Maybe some day you can use the full color driver for the GUI. One thing at a time
Regarding SDRAM video: Ding-Batty has made some driver improvements that would let us read display lines straight from SDRAM. A similar approach could be used for 8xSPI SRAM. Guess I'll have to work on that soon.
It's truly hard to beat any kind of synchronous RAM for buffer operations - one could use a video mode for driving 8 address bits on asynchronous SRAM, but the video mode startup time is quite slow. Synchronous -vs- asynchronous memory buffer speed would be like C3 cached -vs- uncached.
Okay, I finally had time to try compiling xbasic with Catalina 3.0 and it mostly works but I'm having some trouble with file I/O. I built xbasic with the same compiler switches you used in your C3 tutorial but Catalina either crashes (resets) or hangs when I try to load a file. I've attached my source code and makefile so you can see if I've done anything wrong in building the executable. I'm assuming that there isn't a special "build_all" argument needed to enable filesystem support, right?
Maybe some day you can use the full color driver for the GUI. One thing at a time
Regarding SDRAM video: Ding-Batty has made some driver improvements that would let us read display lines straight from SDRAM. A similar approach could be used for 8xSPI SRAM. Guess I'll have to work on that soon.
It's truly hard to beat any kind of synchronous RAM for buffer operations - one could use a video mode for driving 8 address bits on asynchronous SRAM, but the video mode startup time is quite slow. Synchronous -vs- asynchronous memory buffer speed would be like C3 cached -vs- uncached.
My ears were burning...
I need update my timing estimates, but I don't think the caching SDRAM driver code would be a very good fit for reading video information -- there is a lot of unneeded overhead, and reading (and writing) video could better use a raw block I/O interface, especially with I/O request queueing. It seems to me that the most important thing to know is the maximum latency for the reads: what is the longest time the code has to wait for the data to be available after the data is requested...
My timing rule-of-thumb for the SDRAM code: the inner loop for a block read uses 12 clocks per byte read, so the max. read speed will be less than 80/12 == 6.67MB/s at 80MHz, and 100/12 == 8.33 MB/s at 100 MHz. The caching SDRAM driver has somewhere in the 500 to 550 clocks of overhead for a block read (including a nearby refresh burst), which is about 40 to 45 bytes worth of overhead per block. So, reading 32-byte blocks will give (32/(40+32))*6.67 = 6.67*4/9 or about 3MB/s with an 80MHz clock. Reading 256-byte blocks will give 6.67 * (256/(256+40)) == 5.77 MB/s with an 80MHz clock. Of course, there will be some additional overhead setting up the requests and retrieving the data, but I believe a "raw" block driver will have speeds slightly better than that, due to elimination of the cache hit/miss calculation overhead.
For latency in the caching SDRAM code, a refresh burst has 50 clocks of overhead, plus 8 clocks for each refresh it issues in the burst, which for a 256 byte block at 80MHz is about 7 refresh commands, totaling about 106 clocks. The clocks to read a 256-byte block are about 500 + 12*256 clocks. So in general, the maximum time from sending a read request for a 256 byte block until the data is available should be about 3678 system clocks, which is about 46 us.
I need update my timing estimates, but I don't think the caching SDRAM driver code would be a very good fit for reading video information ....
I think your analysis is a little pessimistic. There is some look-ahead fetching that can be done while a current graphics line is being drawn. The cache granularity has to be a video line length. Refresh only happens periodically. I've actually been able to read and display data on TV from SDRAM cache before using the old driver which runs at about 4.7MB/s. I'll have to visit it again soon - I'm a little busy now though.
But what happens when you want to refresh your video from the external RAM and execute XMM code from it at the same time as it looks like Dr_A would like to do?
But what happens when you want to refresh your video from the external RAM and execute XMM code from it at the same time as it looks like Dr_A would like to do?
It would be easy enough to suspend the kernel execution whenever the video display code needs access to the external RAM. Both the XMM kernel and the video driver could use a lock to synchronize access to the common resource (i.e. the XMM RAM).
Of course, the end result would be that the kernel might only get a couple of instructions executed between each video refresh, so your C program would run really slowly!
Of course, the end result would be that the kernel might only get a couple of instructions executed between each video refresh, so your C program would run really slowly!
Yes, good point.
I just coded a mouse pointer. At the moment it is only working when aligned with the longs of the screen so it is a bit jumpy in the x direction. I'll need some extra code for when the pointer bridges two longs. For speed it is going to need to work directly with the longs in the screen buffer rather than painting pixels individually.
void gray_mousepointer(unsigned long screen[],int x, int y) // 21 high, 13 wide = 1 long wide
{
unsigned long mask[21] = { // mask 00=pixel, 11=transparant
0xfffffff0,0xffffffc0,0xffffff00,0xfffffc00,0xfffff000,0xffffc000, // order is reversed in each long
0xffff0000,0xfffc0000,0xfff00000,0xffc00000,0xff000000,0xfc000000,
0xfffc0000,0xfffc0000,0xfff00300,0xfff003c0,0xffc00ff0,0xffc00fff,
0xff003fff,0xff003fff,0xffc0ffff
};
unsigned long icon[21] = { // icon, 00, 01, 10 or 11=pixel, 00 = transparent if transparent above
0x00000004,0x00000010,0x0000004c,0x0000013c,0x000004fc,0x000013fc,
0x00004ffc,0x00013ffc,0x0004fffc,0x0013fffc,0x004ffffc,0x01003ffc,
0x00013cfc,0x00013c3c,0x0004f04c,0x0004f010,0x0013c004,0x0013c000,
0x004f0000,0x004f0000,0x00100000
};
// need to add in bitshifts to get the x exactly correct
int i,j,n,m;
n=0;
m=(y*20)+(x/20); // get the screen long - 20 longs per row
for (i=0;i<21;i++) // rows
{
{
screen[m] = screen[m] & mask[n]; // mask out pixel bits, ignore transparent bits
screen[m] = screen[m] | icon[n]; // draw the icon
n++;
}
m = m+20; // next row
}
}
Good to read that it might be possible to suspend Catalina. I've been pondering how to access the LCD display on the dracblade as it needs to take over the first 12 pins of the propeller. I have had an idea that maybe you set a cog going with the data to output, send an instruction to catalina "suspend", the cog detects that the catalina lock has changed so goes off and runs its code, then when finished, changes the catalina lock again?
Would this work? Or would it be easier to custom code the LCD as part of the memory driver?
Good to read that it might be possible to suspend Catalina. I've been pondering how to access the LCD display on the dracblade as it needs to take over the first 12 pins of the propeller. I have had an idea that maybe you set a cog going with the data to output, send an instruction to catalina "suspend", the cog detects that the catalina lock has changed so goes off and runs its code, then when finished, changes the catalina lock again?
Would this work? Or would it be easier to custom code the LCD as part of the memory driver?
I'm not sure. Do you need to constantly refresh your LCD? If not, you don't need to do anything special for this one - just make the LCD driver a plugin. The Catalina kernel is always suspended during a service call to a plugin, so provided you set the pins back the way Catalina expects them to be before returning from the service call, you can do whatever you like and Catalina will not be disturbed.
Ah, that sounds like the way to do things. No, it does not need to be refreshed, so this looks like an answer. The code is contained within a cog and when the cog finishes it can set a flag or something. I'll need to study plugins a bit more though. Thanks Ross
Okay, I finally had time to try compiling xbasic with Catalina 3.0 and it mostly works but I'm having some trouble with file I/O. I built xbasic with the same compiler switches you used in your C3 tutorial but Catalina either crashes (resets) or hangs when I try to load a file. I've attached my source code and makefile so you can see if I've done anything wrong in building the executable. I'm assuming that there isn't a special "build_all" argument needed to enable filesystem support, right?
David,
Yes, there is definitely something odd. It is a memory initialization problem - but I'm not sure if it is your problem or mine. However, it is very reproducible, so it shouldn't be too hard to track down.
Here are the commands I used (after unzipping xbasic into the main Catalina directory) to reproduce it:
cd C:\Program Files\Catalina
use_catalina
cd utilities
build_all C3 CACHED_4K FLASH SD
payload -e flash_boot
cd ..\xbasic
make -f Makefile.cat clean
make -f Makefile.cat
payload XMM bin\xbasic.binary -t 1000
Note that I compile and load the flash_boot utility just so that I don't have to keep reloading xbasic each time after the initial load - I just have to reset the Prop to restart it (This is the reason I add the SD parameter to the build_all script - so that flash_boot will load the SD card driver. If you don't use flash_boot then you don't need that parameter)
When the xbasic prompt appears on the TV display, I try and load a file. It fails, and spits out some rubbish about "expecting a line number" in an infinite loop.
I reset the Prop and try again. It fails again, and spits out different output (this time it's just rubbish).
I reset the Prop and try again. This time I get no error messages or rubbish, but when I list the file I just loaded it is empty.
I reset the Prop and try again. This time it succeeds. I can list the file I just loaded. I can run it.
From now on, everything appears to work correctly. It will continue to run correctly now on every reboot.
But I can make it fail again just by loading and running the ram test program, then reloading the xbasic binary. I then have to go through another three reboots to get it working again.
Obviously the problem is that some memory is being used without being initialized first. Perhaps some memory that your program is expecting to be initialized to zero is not being initialzed by Catalina.
Can you check your program to see if it is dependent on some specific memory initialization? Perhaps you are relying on some initialization that other compilers do but Catalina does not (of course, this might be Catalina's fault - there are some things in C that are always supposed to be initialized, but perhaps I am not doing this correctly in all cases).
Thanks for the analysis. It could very well be that I have some sort of memory issue. This code runs fine under Linux or Mac OS X but, as you say, they may do something different in terms of memory initialization. I spent a fair amount of time trying to reduce the RAM usage of xbasic and I may have over-optimized in some critical place. I'll look over my code before I bother you again! :-)
I hope you don't mind multitasking with two threads running in parallel here?
Anyway
The Catalina kernel is always suspended during a service call to a plugin, so provided you set the pins back the way Catalina expects them to be before returning from the service call, you can do whatever you like and Catalina will not be disturbed.
I have read through the Catalina reference manual, plugin section, and also the Getting Started with Plugins.
Some of this is quite logical. Some is a bit too complex for me (eg page 7 of the plugin manual, I don't know what all those arrows actually mean) but I am getting some idea how plugins work.
As you know, I have kind of bypassed the entire plugin/registry system with cogjects. I define an array in C, pass that array location to the cog code and then interact with that cog via the array. I guess for me this is more flexible in that for one cog, I might want to interact with one byte, but the next one might need to interact with 20 kilobytes. I know that plugins can pass the location via registry of another array, but that is a double step. I also have found it easier to interact with cogs using one array and one array only, because that is how many Obex spin objects have been written. Also, some obex code almost fills up the pasm part and there is no room left for the generic plugin code, so we are sometimes stuck with arrays that start at "par".
Anyway, in all the reading of the plugin manuals, I am still a bit unclear about when Catalina shuts down and when Catalina keeps running. There is mention of a service request, and the spin code contains this
' wait for the service request to complete
common.WaitForRequest(cog)
but I'm not sure where this spin code is used - whether it is internal to catalina or whether that is a demo plugin?
I still haven't quite found all the bits needed to load and run a plugin. I think the bit I am missing is the example code in C. Particularly the C code that i) sets a cog running and keeps C running and ii) the code that sets a cog running and pauses C until the cog returns something.
(Cogjects always keep running C, but do plugin requests *always* stop C, or only sometimes?)
So - let's say I create an array and run an LCD cogject. I'll do this the way I understand it, but maybe there is a plugin way that works in a similar fashion.
The first item I pass to my LCD cogject is the address of the registry. There is a C function to obtain this.
Then I load up my array with some data (I'll pass a group of bytes)
Then I set a flag to say there is some fresh data
Then I (re)start the cog. (I know which cog this is running in.)
The cog code collects the registry pointer, and determines that catalina is still running so the cog holds for the moment.
Now I issue a C command to catalina to tell it to suspend. What is this command? Is it a "service call"? And which registry value does it alter?
Then the cog detects that catalina has stopped. It processes the data.
It then sends a message back to catalina to tell catalina to restart. This is presumably some sort of flag in the registry? If it is a flag, what is its location offset from the start of the registry?
I think some of this is explained in Catalina_Plugin.spin in this block of code
cogid t1 ' get ...
shl t1,#2 ' ... our ...
add t1,par ' ... registry block
rdlong rqstptr,t1 ' register ...
and rqstptr,low_24 ' ... this ...
wrlong zero,rqstptr ' ... plugin ...
mov t2,#PTYPE ' ... as ...
shl t2,#24 ' ... the ...
or t2,rqstptr ' ... appropriate ...
wrlong t2,t1 ' ... type
Have you by any chance got a C program that demonstrates loading and interacting with Catalina_Plugin?
As you know, I have kind of bypassed the entire plugin/registry system with cogjects. I define an array in C, pass that array location to the cog code and then interact with that cog via the array. I guess for me this is more flexible in that for one cog, I might want to interact with one byte, but the next one might need to interact with 20 kilobytes. I know that plugins can pass the location via registry of another array, but that is a double step. I also have found it easier to interact with cogs using one array and one array only, because that is how many Obex spin objects have been written. Also, some obex code almost fills up the pasm part and there is no room left for the generic plugin code, so we are sometimes stuck with arrays that start at "par".
Actually, I like this method of doing things as well. This is basically the approach I took with ZOG. When a ZOG program is started, the only COGs that are running are the COG running the ZOG VM and the COG running the external memory cache interface. Any other COGs that are needed by the application are loaded by C code. It seems like this makes for a much easier to configure system. You don't have to mess around with Spin code to get the right plug-ins loaded and you have full freedom to use any of the remaining COGs in any way you want. What exactly happens when a Catalina code starts? What does the crt0 startup code do and what does it expect for its environment? Is it possible to bypass all of the HMI stuff and just leave it to the C code to initialize its own drivers? In my ZOG runtime I took the approach that the C runtime code called through a set of function pointers to do low-level things like terminal I/O and file open/close. Those functions by default were stubbed out so that they just returned error codes. Then, the user's C initialization code could setup either keyboard/TV/VGA terminal I/O or serial terminal I/O as needed. Similarly, the file I/O support was loaded by the user's initialization code rather than being controlled by which library was linked. The linker takes care that code isn't linked in that isn't referenced so the file I/O support is just left out of the executable if it is not used.
Is there something similar that can be done with Catalina? Can we have alternate C startup code that simply sets up the stack and calls the user's main() letting it do everything else? Actually, ZOG is a bit more sophisticated than that. The C startup code first calls _initIO() and then calls main(). This has the advantage that _initIO() can be used to do any Propeller-specific setup before main() is called allowing the main() code to be generic and not tied to the Propeller. I think adopting this sort of model in Catalina along with the cogjects approach to loading drivers would be useful at least as an alternative to the current approach.
I have read through the Catalina reference manual, plugin section, and also the Getting Started with Plugins.
Some of this is quite logical. Some is a bit too complex for me (eg page 7 of the plugin manual, I don't know what all those arrows actually mean) but I am getting some idea how plugins work.
The arrows are pointers, showing that the lower 24 bits of each registry entry is set up to point to your allocated "request" block. Each pre-allocated request block is two longs, and is used by the registry functions as a request long and a response long. However, you are free to allocate another (larger) comms block if you need to, and update the registry with its address - Catalina doesn't care. However, I have never found a need to do. Mostly, I just use the registry to pass another data block when I have a need to do so, because its much easier.
As you know, I have kind of bypassed the entire plugin/registry system with cogjects.
That's fine - but you will be unable to use all the built-in kernel support for your cogjects,which is really just making life difficult for yourself, and is why you are now going to have to reproduce much of that functionality yourself.
I define an array in C, pass that array location to the cog code and then interact with that cog via the array. I guess for me this is more flexible in that for one cog, I might want to interact with one byte, but the next one might need to interact with 20 kilobytes.
When I need to do this in Catalina, I use the request block to pass the address of another data block. This is a "long request" as opposed to a "short request" where all data is entirely contained in the request block itself.
I know that plugins can pass the location via registry of another array, but that is a double step.
If that bothers you, just do it once on startup - some plugins have an "initialization" service that is used to pass them the address of another data block on startup, and thereafter they do not use the registry - so there is not really a "double step" at run time
I also have found it easier to interact with cogs using one array and one array only, because that is how many Obex spin objects have been written. Also, some obex code almost fills up the pasm part and there is no room left for the generic plugin code, so we are sometimes stuck with arrays that start at "par".
True - but such cases are rare. Usually the PASM code does some initialization itself (since pasm programs rarely stand alone) which can be replaced by the Catalina "plugin" style inialization at little or no cost. You may also notice that some plugins are registered by the SPIN startup code, not in the PASM code - this reduces the overhead required to turn a PASM program into a plugin into just a few instructions.
Anyway, in all the reading of the plugin manuals, I am still a bit unclear about when Catalina shuts down and when Catalina keeps running. There is mention of a service request, and the spin code contains this
' wait for the service request to complete
common.WaitForRequest(cog)
but I'm not sure where this spin code is used - whether it is internal to catalina or whether that is a demo plugin?
That SPIN code is only used during initialization. For all plugins that accept service requests (and not all do) then the Catalina kernel is suspended from the time it sets up the request (in the request long) until the time the plugin sets that long back to zero. Then catalina reads the result (from the result long) and resumes executon of the kernel.
This means that plugins can be written to simply accept a reqest and release the kernel immediately (i.e. before processing the request), or to suspend the kernel until the entire request is completed. Which mechanism you use depends on what the request has to do. If (for example) you need to access external RAM, then you have to leave the kernel suspended until you have finished. On the other hand, if you are sending a byte to a serial port, you can let the kernel resume as soon as you have the byte to send, and then go off and send it while the kernel carries on with something else.
I still haven't quite found all the bits needed to load and run a plugin. I think the bit I am missing is the example code in C. Particularly the C code that i) sets a cog running and keeps C running and ii) the code that sets a cog running and pauses C until the cog returns something.
There is no specific example because there are examples all through Catalina - for example, the TV or LoRes VGA HMI plugins (but not the HiRes VGA one) accept service requests, and while there is no request outstanding, they spends their time flashing the cursor. The graphics plugin accepts a request, then releases the kernel immediately before it goes off and executes the request. The SD plugin always leaves the kernel suspended for the entire time it takes to perform the SD operation (on many platforms it has to use the same pins as are used for other purposes).
(Cogjects always keep running C, but do plugin requests *always* stop C, or only sometimes?)
Plugin requests always stop the kernel that made them (and only that kernel, if there are multiple kernels running) until the plugin says it can proceed - which (as I described above) may be immediately, or it may be sometime later.
So - let's say I create an array and run an LCD cogject. I'll do this the way I understand it, but maybe there is a plugin way that works in a similar fashion.
There are plugins that work in every possible fashion - for your purposes, maybe the graphics plugin might be a good model to use.
The first item I pass to my LCD cogject is the address of the registry. There is a C function to obtain this.
Then I load up my array with some data (I'll pass a group of bytes)
Then I set a flag to say there is some fresh data
Then I (re)start the cog. (I know which cog this is running in.)
The cog code collects the registry pointer, and determines that catalina is still running so the cog holds for the moment.
Now I issue a C command to catalina to tell it to suspend. What is this command? Is it a "service call"? And which registry value does it alter?
Then the cog detects that catalina has stopped. It processes the data.
It then sends a message back to catalina to tell catalina to restart. This is presumably some sort of flag in the registry? If it is a flag, what is its location offset from the start of the registry?
That is an incredibly complex way to do things. It should all be much, much easier than this. Why not just write an LCD plugin that runs constantly, monitoring its request block for new service requests. When it gets one it performs the requested function, writing a zero back into the request long when it is ready to let the kernel proceed.
I think some of this is explained in Catalina_Plugin.spin in this block of code
cogid t1 ' get ...
shl t1,#2 ' ... our ...
add t1,par ' ... registry block
rdlong rqstptr,t1 ' register ...
and rqstptr,low_24 ' ... this ...
wrlong zero,rqstptr ' ... plugin ...
mov t2,#PTYPE ' ... as ...
shl t2,#24 ' ... the ...
or t2,rqstptr ' ... appropriate ...
wrlong t2,t1 ' ... type
Have you by any chance got a C program that demonstrates loading and interacting with Catalina_Plugin?
The code you quote simply registers the plugin - this can be done either in PASM or SPIN. As to examples of prorgams interacting with plugins, nearly all Catalina programs do this. The simplest ones are the ones that use the low-level HMI functions - each of these functions is just a trivial wrapper around a plugin request. For example, here is the entire C code for the t_char function:
As you can see, all this code does is call short_plugin_request. Here is that function in full:
int _short_plugin_request (long plugin_type, long code, long param) {
return _sys_plugin (plugin_type, (code<<24) + (param & 0x00FFFFFF));
}
This code actually executes the _sys_plugin service request that stops the kernel until the plugin says it can proceed again. I don't think you can come up with a mechanism that is much simpler than this - and it is flexible enough to do anything you might need to do. If a short request is not enough (short requests can only include 24 bits of data) then use a long request (long requests can include 32 bits of data). If that's not enough, make the parameter to the long request the address of another data block (which can be any size you like).
Thanks for the analysis. It could very well be that I have some sort of memory issue. This code runs fine under Linux or Mac OS X but, as you say, they may do something different in terms of memory initialization. I spent a fair amount of time trying to reduce the RAM usage of xbasic and I may have over-optimized in some critical place. I'll look over my code before I bother you again! :-)
David,
Don't assume your code is at fault. It could well be a Catalina issue. When I get time I'l check out all the cases where a C compiler is supposed to guarantee memory is initialized to zero. But if you do find clue, please let me know!
Actually, I like this method of doing things as well. This is basically the approach I took with ZOG. When a ZOG program is started, the only COGs that are running are the COG running the ZOG VM and the COG running the external memory cache interface. Any other COGs that are needed by the application are loaded by C code. It seems like this makes for a much easier to configure system. You don't have to mess around with Spin code to get the right plug-ins loaded and you have full freedom to use any of the remaining COGs in any way you want. What exactly happens when a Catalina code starts? What does the crt0 startup code do and what does it expect for its environment? Is it possible to bypass all of the HMI stuff and just leave it to the C code to initialize its own drivers? In my ZOG runtime I took the approach that the C runtime code called through a set of function pointers to do low-level things like terminal I/O and file open/close. Those functions by default were stubbed out so that they just returned error codes. Then, the user's C initialization code could setup either keyboard/TV/VGA terminal I/O or serial terminal I/O as needed. Similarly, the file I/O support was loaded by the user's initialization code rather than being controlled by which library was linked. The linker takes care that code isn't linked in that isn't referenced so the file I/O support is just left out of the executable if it is not used.
Is there something similar that can be done with Catalina? Can we have alternate C startup code that simply sets up the stack and calls the user's main() letting it do everything else? Actually, ZOG is a bit more sophisticated than that. The C startup code first calls _initIO() and then calls main(). This has the advantage that _initIO() can be used to do any Propeller-specific setup before main() is called allowing the main() code to be generic and not tied to the Propeller. I think adopting this sort of model in Catalina along with the cogjects approach to loading drivers would be useful at least as an alternative to the current approach.
With Catalina you are free to do whatever you want. You can create your own target directory that doesn't load any plugins at all if you want to - for example the custom target directory is a fairly minimalist one. It loads only one plugin (intended as an example). You could use something like that to start your C program, and load everything else you need to do from within C if you want to (e.g. using Dr_A's cogjects).
However, Catalina is primarily designed to provide users with a complete ANSI C compliant environment. This means all the things mandated by the ANSI C standard (e.g. stdin/stdout/stderr support - which in Catalina means HMI drivers, file system support - which in Catalina means an SD card driver, real-time clock support, floating point support etc) are all there on any platform capable of suporting them - all without you having to write a single line of code.
People like us might like messing about with LCD drivers and file systems - but Catalina is also intended for people who just want to write programs!
Thanks for all that. I think I am getting closer to a solution.
I'd like to think about an absolute minimalist plugin. All it does is tells Catalina that it can keep running. To do this I understand it needs to write a zero somewhere. If the plugin knows the location of the registry, and it is running in cog 7, what is the location of the long in which to write that zero?
I am guessing here, but is it one of registry+7, or registry-7, or registry+14, or registry-14, or registry+15, or registry-15, or some other location?
Comments
I found I kept forgetting the rules, so I solved the problem by always adding a comment after an array declaration. Declare an array at the beginning? Then add a comment "// this array is in external memory".
Declare an array in the "main" or in a function? Add a comment "// this array is in hub memory".
This has worked so far for my ailing memory!
Re the RTC, I've got cogjects working in C for a graphics driver and for serial. I like this approach because the C is standard looking C, with no tricks. The pasm part can be loaded, run, and for a RTC, this is where the pasm part could be overwritten with something else once it has run and you have read/changed the time.
Then you just need some C code to replicate the Spin code. I might take a look at this when I get home.
I'm finding it more flexible to do things this way rather than asking Ross to add code 'internally' to catalina.
Thanks Jonathan. I'll look at integrating it for the next release.
Ross.
No worries - I'll try and get time this weekend to come up with a 3.0 equivalent. Also, if you are considering creating a new target directory, consider starting from the "custom" target directory (i.e. C:\Program Files\Catalina\custom) - that is intended to be a "minimal" target directory. It is probably a lot less intimidating to start from that and add stuff rather than starting from the "target" directory and removing stuff!
Ross.
Hi Dr_A.
I keep hoping that Catalina is advanced and mature enough that it is no longer necessary for me to do all the work (not that I don't want to - more that I don't always have the the time!).
I think the work that you're doing is good evidence that this may finally be coming true!
Ross.
I managed to take some quite obfuscated Spin code and convert it into C, and I didn't have to ask once for help:
More specifically, this is part of another cogject, and these can be loaded and run independent of the catalina system.
This demo program loads up a 64 color demo, draws some boxes and pixels, then unloads that and loads in a 4 color grayscale demo at higher resolution. See attached photo. I think this has the potential to be the beginnings of a GUI for the propeller.
See the attached cog files - unzip these and put them on the sd card. The nice thing about these .cog files is that they are the same ones I'm using for the Spin demonstration. Potentially this could be done in Propbasic as well.
Next step is to create some font libraries. Almost at the "Hello World" stage!
Hi Alesandro,
I have attached a skeleton to show how I would implement such dual-cpu support in Catalina 3.0. I have defined a new platform called DEMOBLADE - which also requires you to specify either CPU_1 or CPU_2.
CPU_1 looks like a DEMO board but without TV support, and CPU_2 looks like a RAMBLADE with XMM support. I have included amended versions of Catalina_Common.spin as well as HMI.inc and XMM.inc (which are the most complex files you would probably need to modify).
Of course, I have not actually tested any of it!
Let me know if you have any questions.
Ross.
thanks so much for spending some time on this.
I'm sorry I didn't post the inc files too, I made that thing on 2.9 some weeks ago, and completely forgot about having to modify those too.
Now I'm getting there again, translating the build_morpheus_xxx files to build_demoblade_xxx, they are close enough and compilation goes well. I seem to recall I had to tweak the VGA frequencies for my stupid LCD panel, and after that most proxy demos were working fine (using a fixed proxy, loaded in eeprom on the demo/proto side.
When I get the build files done I'll upload them here, Cluso had a similar configuration pictured in the RamBlade thread, so maybe he'll want to give it a try.
Thanks again
Alessandro
Textbox, Button, Radio, Menu, Label and Groupbox all working. Easy to use in C:
(everything starts with "gray" to distinguish from the chunkier color version).
Full code below including the font array:
Eventually someone may like other colors which provide more shades than gray.
Good question. The gray graphics driver is 320x240 with 4 colors which is the maximum color depth (I think?) for 19200 bytes of hub ram. There are some other functions in there that start with "pix" - these are from Kye's 64 color driver which is 160x120 and "pix" is what Kye used in Spin.
The demo program starts off with white on blue text 80x40, then changes to the 160x120 color and draws some boxes and a color bar, then switches to gray 320x240.
I'd love to get some more colors!
But I don't think the prop can do this... unless you can pull data in from external memory fast enough?
At the moment what I am thinking is you have the color version for games, and things like the movie demo and icons, and then you have the higher resolution gray version for "work", eg a text editor, or a little calculator program.
I did originally code the first few buttons in the color 160x120 version but it looks very chunky on a VGA screen. However, it might look a lot better on a smaller LCD TV screen, and hopefully this might be possible to port over to TV as well.
Lots of scope for improvement here!
BTW, could this run on your nifty 32meg board? I think Ross has catalina working now. I have a feeling this might end up a largish program - it is 128k already and might end up going over 512k so the extra memory could come in very handy.
Regarding SDRAM video: Ding-Batty has made some driver improvements that would let us read display lines straight from SDRAM. A similar approach could be used for 8xSPI SRAM. Guess I'll have to work on that soon.
It's truly hard to beat any kind of synchronous RAM for buffer operations - one could use a video mode for driving 8 address bits on asynchronous SRAM, but the video mode startup time is quite slow. Synchronous -vs- asynchronous memory buffer speed would be like C3 cached -vs- uncached.
I'll check this out when I get home tonight.
Ross.
My ears were burning...
I need update my timing estimates, but I don't think the caching SDRAM driver code would be a very good fit for reading video information -- there is a lot of unneeded overhead, and reading (and writing) video could better use a raw block I/O interface, especially with I/O request queueing. It seems to me that the most important thing to know is the maximum latency for the reads: what is the longest time the code has to wait for the data to be available after the data is requested...
My timing rule-of-thumb for the SDRAM code: the inner loop for a block read uses 12 clocks per byte read, so the max. read speed will be less than 80/12 == 6.67MB/s at 80MHz, and 100/12 == 8.33 MB/s at 100 MHz. The caching SDRAM driver has somewhere in the 500 to 550 clocks of overhead for a block read (including a nearby refresh burst), which is about 40 to 45 bytes worth of overhead per block. So, reading 32-byte blocks will give (32/(40+32))*6.67 = 6.67*4/9 or about 3MB/s with an 80MHz clock. Reading 256-byte blocks will give 6.67 * (256/(256+40)) == 5.77 MB/s with an 80MHz clock. Of course, there will be some additional overhead setting up the requests and retrieving the data, but I believe a "raw" block driver will have speeds slightly better than that, due to elimination of the cache hit/miss calculation overhead.
For latency in the caching SDRAM code, a refresh burst has 50 clocks of overhead, plus 8 clocks for each refresh it issues in the burst, which for a 256 byte block at 80MHz is about 7 refresh commands, totaling about 106 clocks. The clocks to read a 256-byte block are about 500 + 12*256 clocks. So in general, the maximum time from sending a read request for a 256 byte block until the data is available should be about 3678 system clocks, which is about 46 us.
I think your analysis is a little pessimistic. There is some look-ahead fetching that can be done while a current graphics line is being drawn. The cache granularity has to be a video line length. Refresh only happens periodically. I've actually been able to read and display data on TV from SDRAM cache before using the old driver which runs at about 4.7MB/s. I'll have to visit it again soon - I'm a little busy now though.
It would be easy enough to suspend the kernel execution whenever the video display code needs access to the external RAM. Both the XMM kernel and the video driver could use a lock to synchronize access to the common resource (i.e. the XMM RAM).
Of course, the end result would be that the kernel might only get a couple of instructions executed between each video refresh, so your C program would run really slowly!
Ross.
Yes, good point.
I just coded a mouse pointer. At the moment it is only working when aligned with the longs of the screen so it is a bit jumpy in the x direction. I'll need some extra code for when the pointer bridges two longs. For speed it is going to need to work directly with the longs in the screen buffer rather than painting pixels individually.
Good to read that it might be possible to suspend Catalina. I've been pondering how to access the LCD display on the dracblade as it needs to take over the first 12 pins of the propeller. I have had an idea that maybe you set a cog going with the data to output, send an instruction to catalina "suspend", the cog detects that the catalina lock has changed so goes off and runs its code, then when finished, changes the catalina lock again?
Would this work? Or would it be easier to custom code the LCD as part of the memory driver?
I'm not sure. Do you need to constantly refresh your LCD? If not, you don't need to do anything special for this one - just make the LCD driver a plugin. The Catalina kernel is always suspended during a service call to a plugin, so provided you set the pins back the way Catalina expects them to be before returning from the service call, you can do whatever you like and Catalina will not be disturbed.
Ross.
Did you ever have a chance to look at this file I/O problem?
Thanks,
David
David,
Yes, there is definitely something odd. It is a memory initialization problem - but I'm not sure if it is your problem or mine. However, it is very reproducible, so it shouldn't be too hard to track down.
Here are the commands I used (after unzipping xbasic into the main Catalina directory) to reproduce it:
Note that I compile and load the flash_boot utility just so that I don't have to keep reloading xbasic each time after the initial load - I just have to reset the Prop to restart it (This is the reason I add the SD parameter to the build_all script - so that flash_boot will load the SD card driver. If you don't use flash_boot then you don't need that parameter)
When the xbasic prompt appears on the TV display, I try and load a file. It fails, and spits out some rubbish about "expecting a line number" in an infinite loop.
I reset the Prop and try again. It fails again, and spits out different output (this time it's just rubbish).
I reset the Prop and try again. This time I get no error messages or rubbish, but when I list the file I just loaded it is empty.
I reset the Prop and try again. This time it succeeds. I can list the file I just loaded. I can run it.
From now on, everything appears to work correctly. It will continue to run correctly now on every reboot.
But I can make it fail again just by loading and running the ram test program, then reloading the xbasic binary. I then have to go through another three reboots to get it working again.
Obviously the problem is that some memory is being used without being initialized first. Perhaps some memory that your program is expecting to be initialized to zero is not being initialzed by Catalina.
Can you check your program to see if it is dependent on some specific memory initialization? Perhaps you are relying on some initialization that other compilers do but Catalina does not (of course, this might be Catalina's fault - there are some things in C that are always supposed to be initialized, but perhaps I am not doing this correctly in all cases).
Ross.
I hope you don't mind multitasking with two threads running in parallel here?
Anyway
I have read through the Catalina reference manual, plugin section, and also the Getting Started with Plugins.
Some of this is quite logical. Some is a bit too complex for me (eg page 7 of the plugin manual, I don't know what all those arrows actually mean) but I am getting some idea how plugins work.
As you know, I have kind of bypassed the entire plugin/registry system with cogjects. I define an array in C, pass that array location to the cog code and then interact with that cog via the array. I guess for me this is more flexible in that for one cog, I might want to interact with one byte, but the next one might need to interact with 20 kilobytes. I know that plugins can pass the location via registry of another array, but that is a double step. I also have found it easier to interact with cogs using one array and one array only, because that is how many Obex spin objects have been written. Also, some obex code almost fills up the pasm part and there is no room left for the generic plugin code, so we are sometimes stuck with arrays that start at "par".
Anyway, in all the reading of the plugin manuals, I am still a bit unclear about when Catalina shuts down and when Catalina keeps running. There is mention of a service request, and the spin code contains this
but I'm not sure where this spin code is used - whether it is internal to catalina or whether that is a demo plugin?
I still haven't quite found all the bits needed to load and run a plugin. I think the bit I am missing is the example code in C. Particularly the C code that i) sets a cog running and keeps C running and ii) the code that sets a cog running and pauses C until the cog returns something.
(Cogjects always keep running C, but do plugin requests *always* stop C, or only sometimes?)
So - let's say I create an array and run an LCD cogject. I'll do this the way I understand it, but maybe there is a plugin way that works in a similar fashion.
The first item I pass to my LCD cogject is the address of the registry. There is a C function to obtain this.
Then I load up my array with some data (I'll pass a group of bytes)
Then I set a flag to say there is some fresh data
Then I (re)start the cog. (I know which cog this is running in.)
The cog code collects the registry pointer, and determines that catalina is still running so the cog holds for the moment.
Now I issue a C command to catalina to tell it to suspend. What is this command? Is it a "service call"? And which registry value does it alter?
Then the cog detects that catalina has stopped. It processes the data.
It then sends a message back to catalina to tell catalina to restart. This is presumably some sort of flag in the registry? If it is a flag, what is its location offset from the start of the registry?
I think some of this is explained in Catalina_Plugin.spin in this block of code
Have you by any chance got a C program that demonstrates loading and interacting with Catalina_Plugin?
Actually, I like this method of doing things as well. This is basically the approach I took with ZOG. When a ZOG program is started, the only COGs that are running are the COG running the ZOG VM and the COG running the external memory cache interface. Any other COGs that are needed by the application are loaded by C code. It seems like this makes for a much easier to configure system. You don't have to mess around with Spin code to get the right plug-ins loaded and you have full freedom to use any of the remaining COGs in any way you want. What exactly happens when a Catalina code starts? What does the crt0 startup code do and what does it expect for its environment? Is it possible to bypass all of the HMI stuff and just leave it to the C code to initialize its own drivers? In my ZOG runtime I took the approach that the C runtime code called through a set of function pointers to do low-level things like terminal I/O and file open/close. Those functions by default were stubbed out so that they just returned error codes. Then, the user's C initialization code could setup either keyboard/TV/VGA terminal I/O or serial terminal I/O as needed. Similarly, the file I/O support was loaded by the user's initialization code rather than being controlled by which library was linked. The linker takes care that code isn't linked in that isn't referenced so the file I/O support is just left out of the executable if it is not used.
Is there something similar that can be done with Catalina? Can we have alternate C startup code that simply sets up the stack and calls the user's main() letting it do everything else? Actually, ZOG is a bit more sophisticated than that. The C startup code first calls _initIO() and then calls main(). This has the advantage that _initIO() can be used to do any Propeller-specific setup before main() is called allowing the main() code to be generic and not tied to the Propeller. I think adopting this sort of model in Catalina along with the cogjects approach to loading drivers would be useful at least as an alternative to the current approach.
This means that plugins can be written to simply accept a reqest and release the kernel immediately (i.e. before processing the request), or to suspend the kernel until the entire request is completed. Which mechanism you use depends on what the request has to do. If (for example) you need to access external RAM, then you have to leave the kernel suspended until you have finished. On the other hand, if you are sending a byte to a serial port, you can let the kernel resume as soon as you have the byte to send, and then go off and send it while the kernel carries on with something else. There is no specific example because there are examples all through Catalina - for example, the TV or LoRes VGA HMI plugins (but not the HiRes VGA one) accept service requests, and while there is no request outstanding, they spends their time flashing the cursor. The graphics plugin accepts a request, then releases the kernel immediately before it goes off and executes the request. The SD plugin always leaves the kernel suspended for the entire time it takes to perform the SD operation (on many platforms it has to use the same pins as are used for other purposes). Plugin requests always stop the kernel that made them (and only that kernel, if there are multiple kernels running) until the plugin says it can proceed - which (as I described above) may be immediately, or it may be sometime later. There are plugins that work in every possible fashion - for your purposes, maybe the graphics plugin might be a good model to use. That is an incredibly complex way to do things. It should all be much, much easier than this. Why not just write an LCD plugin that runs constantly, monitoring its request block for new service requests. When it gets one it performs the requested function, writing a zero back into the request long when it is ready to let the kernel proceed.
The code you quote simply registers the plugin - this can be done either in PASM or SPIN. As to examples of prorgams interacting with plugins, nearly all Catalina programs do this. The simplest ones are the ones that use the low-level HMI functions - each of these functions is just a trivial wrapper around a plugin request. For example, here is the entire C code for the t_char function:
As you can see, all this code does is call short_plugin_request. Here is that function in full: This code actually executes the _sys_plugin service request that stops the kernel until the plugin says it can proceed again. I don't think you can come up with a mechanism that is much simpler than this - and it is flexible enough to do anything you might need to do. If a short request is not enough (short requests can only include 24 bits of data) then use a long request (long requests can include 32 bits of data). If that's not enough, make the parameter to the long request the address of another data block (which can be any size you like).
Ross.
David,
Don't assume your code is at fault. It could well be a Catalina issue. When I get time I'l check out all the cases where a C compiler is supposed to guarantee memory is initialized to zero. But if you do find clue, please let me know!
Ross.
With Catalina you are free to do whatever you want. You can create your own target directory that doesn't load any plugins at all if you want to - for example the custom target directory is a fairly minimalist one. It loads only one plugin (intended as an example). You could use something like that to start your C program, and load everything else you need to do from within C if you want to (e.g. using Dr_A's cogjects).
However, Catalina is primarily designed to provide users with a complete ANSI C compliant environment. This means all the things mandated by the ANSI C standard (e.g. stdin/stdout/stderr support - which in Catalina means HMI drivers, file system support - which in Catalina means an SD card driver, real-time clock support, floating point support etc) are all there on any platform capable of suporting them - all without you having to write a single line of code.
People like us might like messing about with LCD drivers and file systems - but Catalina is also intended for people who just want to write programs!
Ross.
Thanks for all that. I think I am getting closer to a solution.
I'd like to think about an absolute minimalist plugin. All it does is tells Catalina that it can keep running. To do this I understand it needs to write a zero somewhere. If the plugin knows the location of the registry, and it is running in cog 7, what is the location of the long in which to write that zero?
I am guessing here, but is it one of registry+7, or registry-7, or registry+14, or registry-14, or registry+15, or registry-15, or some other location?