We may be converging on something here - more objects are working and now the mouse is bound within the screen, and clicking on a button triggers a change to a label. This is starting to look and feel like a real GUI.
Next step is to add a textbox cursor so that it is possible to edit text. This could eventually become the front end for a self compiling compiler.
Is anyone else able to run this code? (It needs a propeller with external memory, mouse, keyboard, vga and sd card)
Would it be better to put the cogjects into arrays within the program, so that the sd card is not needed?
/* PASM cogject demonstration, see also cogject example in spin*/
#include <stdio.h>
#include <string.h>
// cogjects - sd card driver in catalina is a little slow so load up once into external ram then (re) load quicker from external ram
unsigned long cogject_color[512]; // external memory for color vga driver
unsigned long cogject_gray[512]; // external memory for gray vga driver
unsigned long cogject_mouse[512]; // external memory for mouse driver
int click_area[100][5] = {0}; // list of buttons, text boxes, radio etc where a mouseclick does something x1,y1,x2,y2,objectnumber and initialize to zero with the {0}
unsigned long screen_external[4800] ; // external memory for a screen buffer
// MS Sans Serif font - 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 (HH is always 0c for this font)
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 text_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;
}
// *************************************************
// Runtime Functions from BCX
// *************************************************
char *BCX_TmpStr (size_t Bites)
{
static int StrCnt;
static char *StrFunc[2048];
StrCnt=(StrCnt + 1) & 2047;
if(StrFunc[StrCnt]) free (StrFunc[StrCnt]);
return StrFunc[StrCnt]=(char*)calloc(Bites+128,sizeof(char));
}
char *str (double d)
{
register char *strtmp = BCX_TmpStr(24);
sprintf(strtmp,"% .15G",d);
return strtmp;
}
char *hex (int a)
{
register char *strtmp = BCX_TmpStr(16);
sprintf(strtmp,"%X",a);
return strtmp;
}
// *************************************************************
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 pix_screengray(unsigned long screen[]) // whole screen gray
{
int i;
for(i=0;i<4800;i++)
{
screen[i] = 0xa8a8a8a8; // same as pix_color(2,2,2)
}
}
void pixenginestart(int cognumber, unsigned long cogarray[], unsigned long screen[], unsigned long pingroup, char color_parameters[])
{
int i;
unsigned long frequencystate;
screen[2] = (0xff << (8*pingroup));
screen[3] = (0x300000ff | (pingroup << 9));
frequencystate = ((25175000 + 1600) / 4);
screen[4] = 1;
for(i=0;i<32;i++)
{
frequencystate = frequencystate << 1;
screen[4] = (screen[4] << 1) | (screen[4] >> 31);
if (frequencystate >= _clockfreq())
{
frequencystate -= _clockfreq();
screen[4] += 1;
}
}
color_parameters[0] = 1; // displayindicator
color_parameters[1] = 0; // syncindicator
screen[0] = (unsigned long) &color_parameters[0]; // pointer to displayindicator
screen[1] = (unsigned long) &color_parameters[1]; // pointer to syncindicator
_cogstop(cognumber); // stop just before starting the next one
external_memory_cog_load(cognumber,cogarray,screen); // load from external ram, pass some values in screen[]
//pix_clearscreen(screen); // clear the screen to black
pix_screengray(screen); // screen gray
}
void pix_colorbar(unsigned long screen[])
{
int x;
int c;
x = 0;
for (c=0;c<64;c++)
{
pix_pixel(screen,x+10,6,(c<<2)); // print all the colors in a bar
x++;
}
}
void pix_line(unsigned long screen[], int startx, int starty, int endx, int endy, char color)
{
int row; // also does an unfilled box
int col;
for(col=startx;col <= endx ;col++)
{
pix_pixel(screen,col,starty,color); // horizontal lines
pix_pixel(screen,col,endy,color);
}
for(row=starty;row <= endy;row++)
{
pix_pixel(screen,startx,row,color);
pix_pixel(screen,endx,row,color);
}
}
void pix_box_fill(unsigned long screen[], int startx, int starty, int endx, int endy, char color)
{
int row;
int col;
for(row =starty;row <= endy; row++)
{
for(col=startx;col <=endx; col++)
{
pix_pixel(screen,col,row,color); // horizontal line
}
}
}
void copyscreen(unsigned long source[],unsigned long destination[])
{
memcpy(destination,source,19200); // a strings.h function
}
void color_demo(unsigned long screen[],char color_parameters[],int cognumber) // kye graphics demo
{
pixenginestart(cognumber,cogject_color,screen,2,color_parameters); // start the driver
pix_pixel(screen,3,3,0x44);
pix_pixel(screen,4,4,0xFF);
pix_pixel(screen,5,5,pix_color(1,2,3)); // rgb each 0,1,2 or 3
pix_line(screen,0,0,159,119,pix_color(3,0,0)); // red border for screen
pix_colorbar(screen); // a color bar
pix_line(screen,10,10,30,30,pix_color(3,3,0)); // a yellow box
pix_box_fill(screen,10,50,40,60,pix_color(3,0,3)); // magenta filled rectangle
//pix_readscreen("wallaby.vga",screen); // display whole screen image
//copyscreen(screen,screen_external); // copy to external ram
//pix_readscreen("Giraffe.vga",screen); // display whole screen image
//copyscreen(screen_external,screen); // copy back to hub
//pix_readscreen("prop160.vga",screen); // display whole screen image
//pix_box_fill(screen, 0, 0, 159,119,pix_color(2,2,2)); // whole screen gray
}
// ****************** 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_debug_hex(unsigned long screen[],unsigned long hexnumber) // print a debug hex value on the screen at x=10, y=200
{
int i,j;
char lineoftext[20];
for(j=0;j<12;j++)
{
for(i=4000;i<4006;i++) // clear the previous text
{
screen[i+(20*j)]=0xaaaaaaaa; // light gray
//screen[i+(20*j)]=0xffffffff; // white
}
}
strcpy(lineoftext,hex(hexnumber));
gray_printf(screen,10,200,lineoftext);
}
void gray_debug_dec(unsigned long screen[],unsigned long decnumber) // print a debug decimal value on the screen at x=10, y=212
{
int i,j;
char lineoftext[20];
for(j=0;j<12;j++)
{
for(i=4240;i<4246;i++) // clear the previous text
{
screen[i+(20*j)]=0xaaaaaaaa; // light gray
//screen[i+(20*j)]=0xffffffff; // white
}
}
strcpy(lineoftext,str(decnumber));
gray_printf(screen,10,212,lineoftext);
}
void click_areas_add(int x, int y, int sizex, int sizey, int objectnumber)
{
int i = 0;
while (click_area[i][0] != 0) // get the first free entry
{
i +=1;
}
click_area[i][0] = x;
click_area[i][1] = y;
click_area[i][2] = x + sizex;
click_area[i][3] = y + sizey;
click_area[i][4] = objectnumber;
}
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[],int objectnumber,int task)
{
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
if (task == 1) click_areas_add(startx,starty,sizex,sizey,objectnumber); // add to list of areas that can be clicked
}
void gray_text(unsigned long screen[], int startx, int starty, int sizex, int sizey, char lineoftext[], int objectnumber,int task)
{
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
if (task == 1) click_areas_add(startx,starty,sizex,sizey,objectnumber); // add to list of areas that can be clicked
}
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_mousepointer(unsigned long screen[],unsigned long mousebuffer[],int x, int y) // 21 high, 13 wide = 1 long wide
{
unsigned long mouse_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 mouse_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
};
// mousebuffer 0-41 is the screen to restore, 42 is 0 for startup, 1 for redraw, 43 = oldx, 44=oldy
int i,j,n,m,r,s;
unsigned long pixels;
if (mousebuffer[42] ==1) // restore. Skip first time if is zero
{
m=(mousebuffer[44]*20)+(mousebuffer[43]/16); // oldy and oldx
for(i=0;i<21;i++) // store the longs behind the mouse
{
screen[m]=mousebuffer[i];
screen[m+1]=mousebuffer[i+21];
m = m+20;
}
}
mousebuffer[42] = 1; // always restore from now on
mousebuffer[43]=x; // store current x
mousebuffer[44]=y;
n=0;
m=(y*20)+(x/16); // get the screen long - 20 longs per row
r=x%16; // work out the bitshift remainder - 16 pixels per long
//gray_debug_dec(screen,r);
for(i=0;i<21;i++) // store the longs behind the mouse
{
mousebuffer[i]=screen[m];
mousebuffer[i+21]=screen[m+1];
m = m+20;
}
m=(y*20)+(x/16); // recalculate m
if (r==0) // no remainder so fits exactly in a long
{
for (i=0;i<21;i++) // rows
{
{
screen[m] = screen[m] & mouse_mask[n]; // mask out pixel bits, ignore transparent bits
screen[m] = screen[m] | mouse_icon[n]; // draw the icon
n++;
}
m = m+20; // next row
}
}
else
// bitshift so fits
{
s=r<<1; // multiply by 2
for (i=0;i<21;i++) // rows
{
{
// move mask and icon value into pixels. Shift. Leave bits on left as 11 for mask(transparent) and 00 for icon
// note also the order is reversed so shifts to the left move bits to the right. Very confusing!
pixels = mouse_mask[n] << s; // shift s=2 to 30 step 2
pixels = pixels | (0xffffffff >> (32-s)); // leave in transparent bits
screen[m] = screen[m] & pixels; // mask out pixel bits, ignore transparent bits
pixels = mouse_mask[n] >> (32-s); // shift in next long to the right
pixels = pixels | (0xffffffff << s);
screen[m+1] = screen[m+1] & pixels; // finished drawing the mask, now the icon
pixels = mouse_icon[n] << s; // shift s=2 to 30 step 2
screen[m] = screen[m] | pixels; // mask out pixel bits, ignore transparent bits
pixels = mouse_icon[n] >> (32-s); // shift in next long to the right
screen[m+1] = screen[m+1] | pixels;
n++;
}
m = m+20; // next row
}
}
}
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(cognumber); // stop graphics cog
external_memory_cog_load(cognumber,cogarray,screen); // load from external ram to this cog, pass some values in screen[]
gray_clearscreen(screen);
}
void object1_click(unsigned long screen[])
{
gray_printf(screen, 150,200,"Object 1"); // print text on the screen
}
void object2_click(unsigned long screen[])
{
gray_printf(screen, 150,220,"Object 2"); // print text on the screen
}
void object3_click(unsigned long screen[])
{
}
void object4_click(unsigned long screen[])
{
}
void mouse_button(unsigned long screen[], unsigned long mouse_parameters[])
{
unsigned long mx,my;
int i;
int n;
mx = mouse_parameters[0];
my = 219-mouse_parameters[1]; // so top left is 0,0
if (mx>0x80000000) mx = 0; // x < 0
if (mx > 307) mx = 307; // x > 307
if (my>0x80000000) my = 0; // y < 0
if (my>219) my=219; // y > 219
i = 0;
while (click_area[i][0] != 0) // test for clickable areas - added when objects drawn on screen
{
if ((mx > click_area[i][0]) & (my > click_area[i][1]) & (mx < click_area[i][2]) & (my < click_area[i][3]))
{
// process the click if matches an object
//gray_debug_dec(screen,click_area[i][4]); // print the x value
switch (click_area[i][4])
{
case 1: object1_click(screen);
break;
case 2: object2_click(screen);
break;
case 3: object3_click(screen);
break;
case 4: object4_click(screen);
break;
}
}
i +=1;
}
}
void gray_demo(unsigned long screen[], unsigned long mousebuffer[], unsigned long gray_parameters[],unsigned long mouse_parameters[], int cognumber)
{
unsigned long oldx,oldy = 1; // previous mouse pointers, set to anything except 0 so draws the mouse on startup
unsigned long mx,my;
unsigned long old_button = 0; // previous button state 1=left button, 2=right button, 0=no button
unsigned long new_button = 0; // new button press
gray_engine_start(cognumber,cogject_gray,screen,gray_parameters);
gray_debug_hex(screen,m_present()); // is the mouse present?
gray_printf(screen, 8,8,"File Edit Program"); // print menu text on the screen
gray_button(screen, 10, 40,50,19,"Button1",1,1); // a button - object 1, 1=add to list
gray_button(screen, 100,40,50,19,"Button2",2,1); // a button - object 2, 1 = add to list
gray_text(screen, 10, 70, 70,16, "Textbox1",3,1); // a text box - object 3, 1= add to list
gray_text(screen, 100,70, 70,16, "Textbox2",4,1); // a text box - object 4, 1=add to list
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
gray_printf(screen,10,186,"Debug value");
// gray_debug_hex(screen,123456); // print a debug hex value on the screen at 10,200
// gray_debug_dec(screen,123456);
// gray_mousepointer(screen,mousebuffer,21,45); // print a mouse pointer
// gray_mousepointer(screen,mousebuffer,25,48); // move the mouse pointer
while(1)
{
if ((oldx != mouse_parameters[0]) | (oldy != mouse_parameters[1]))
{
mx = mouse_parameters[0];
my = mouse_parameters[1];
if (mx>0x80000000) mx = 0; // x < 0
if (mx > 307) mx = 307; // x > 307
if (my>0x80000000) my = 0; // y < 0
if (my>219) my=219; // y > 219
gray_mousepointer(screen,mousebuffer,mx, 219-my); // move the pointer
//gray_debug_hex(screen,mouse_parameters[1]); // print the value
oldx = mouse_parameters[0]; // store the previous value
oldy = mouse_parameters[1];
}
new_button = mouse_parameters[3]; // in case state changes in the next few lines
if ((old_button == 0) & (new_button == 1)) // mouse has changed from zero to 1
{
mouse_button(screen, mouse_parameters); // mouse down
}
old_button = new_button; // store current state of buttons
}
}
void mouse_engine_start(int cognumber, unsigned long cogarray[], unsigned long mouse_parameters[], int dpin, int cpin)
{
mouse_parameters[0] = (unsigned long) &mouse_parameters[0]; // the first long of the array points to the location of the array itself
mouse_parameters[5] = (unsigned long) dpin; // data pin
mouse_parameters[6] = (unsigned long) cpin;
_cogstop(cognumber); // stop this cog
external_memory_cog_load(cognumber,cogarray,mouse_parameters); // load from external ram to this cog, pass location of the array
}
void main ()
{
char color_parameters[1]; // displayindicator and syncindicater are permanent hub variables
unsigned long screen[4800]; // graphics and text video buffer in hub - eg 19200 bytes = 4800 longs
unsigned long gray_parameters[4]; // permanent hub variables for the gray 320x240 driver
unsigned long mousebuffer[45]; // pixels behind the mouse cursor 42 longs then some data
unsigned long mouse_parameters[19]; // group of 7 plus 12 longs in par_x in the spin version
int i;
mousebuffer[42] = 0; // mouse 0 if startup, 1 if redraw
//m_bound_limits(0,300,0,0,220,0); // set the mouse limits
text_clearscreen(); // white on blue vga clearscreen
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("Read vga color cogject into external ram array cogject_color\n");
readcog("vgagraph.cog",cogject_color); // read in kye's graphics driver
printf("Read vga gray cogject into external ram array cogject_gray\n");
readcog("vga320.cog",cogject_gray);
printf("Read mouse cogject into external ram array cogject_mouse\n");
readcog("mouse.cog",cogject_mouse);
mouse_engine_start(7, cogject_mouse, mouse_parameters, 24, 25); // start the mouse driver (takes 2 secs to reply with the mouse type)
printf("Finished reading cogjects into external ram \n");
_cogstop(3); // stop cog 3 = one of the two default graphics cogs
color_demo(screen,color_parameters,1); // start in cog 1 (stops cog 1 prior to restarting)
sleep(1000); // so can see it
gray_demo(screen, mousebuffer, gray_parameters, mouse_parameters, 1); // gray screen demo in cog 1
while (1); // endless loop as prop reboots on exit from main()
}
I'm sure I could figure this out myself by looking through the Catalina documents and source code but I haven't found all that I need in a few hours of looking so I thought I'd ask here to see if there is a simple answer. I know that an XMM binary file produced by the Catalina binder starts with a program to load into hub memory that contains the kernel as well as any plugins needed by the program. Following that is an image to load into external memory. What I'd like to do is write a PC-based program that parses a Catalina binary file and downloads the portion that is supposed to be written to the flash on a C3. To do this I have to be able to find the XMM image in the binary file. How do I do that? Does the hub image take up exactly 32k at the start of the file? I suspect that is not true since I did a hex dump of xbasic.binary and it doesn't look like an XMM image starting at 0x8000. Can you tell me how to parse the binary file to find the section that needs to be written to flash?
Is anyone else able to run this code? (It needs a propeller with external memory, mouse, keyboard, vga and sd card)
Hi Dr_A,
I presume you are running this on your DracBlade, but modified to include a mouse port? Are there any other single-CPU boards one which all these devices can be used at once? If not, then you won't find many users able to run this code. Can you make some of the devices "optional"?
Does the hub image take up exactly 32k at the start of the file? I suspect that is not true since I did a hex dump of xbasic.binary and it doesn't look like an XMM image starting at 0x8000. Can you tell me how to parse the binary file to find the section that needs to be written to flash?
Hi David,
Yes, the target always takes exactly 32k, so $8000 is always the start of the XMM binary - specifically, it will be the start of the Catalina prologue (which you need to decode to figure out where everything else is).
It would be useful to generate a listing (just add -y to your compilation command) and use that as a reference when examining the binary file. There is no complexity in the XMM binary file format for the Flash layouts (there is some for the other layouts) - whatever you see in the listing at $XXXX, you should find in the binary file at $XXXX + $8000.
Yes, the target always takes exactly 32k, so $8000 is always the start of the XMM binary - specifically, it will be the start of the Catalina prologue (which you need to decode to figure out where everything else is).
It would be useful to generate a listing (just add -y to your compilation command) and use that as a reference when examining the binary file. There is no complexity in the XMM binary file format for the Flash layouts (there is some for the other layouts) - whatever you see in the listing at $XXXX, you should find in the binary file at $XXXX + $8000.
Ross.
I guess I'm confused because this is part of a dump of xbasic.binary:
I thought that the prolog always started with two longs of zero so it didn't seem like it was really at 0x8000 but more likely at 0x8018.
All Catalina programs are compiled with a SPIN/PASM compiler, so the PASM is always preceeded by the usual SPIN introduction - which in this case is $018 bytes long. This will match exactly what is shown in the listing.
All Catalina programs are compiled with a SPIN/PASM compiler, so the PASM is always preceeded by the usual SPIN introduction - which in this case is $018 bytes long. This will match exactly what is shown in the listing.
Yes - the size is determined by the number of SPIN "methods" included in the "object" being compiled- but Catalina only ever includes one (i.e. "Base").
Ross.
Oops - I was wrong - (now fixed above!). It's the number of methods, not objects that determine this offset - but Catalina only ever includes one.
Oops - wrong again (slightly) - Kuroneko has pointed out that it is both the number of methods and the number of objects that determine this offset in general - it is $18 for Catalina because Catalina.spin does not include any other objects, as well as only having one method.
I guess I should learn how to parse Spin file headers. I didn't realize that the XMM image was just another Spin binary file. I thought it was in some special Catalina format.
I guess I should learn how to parse Spin file headers.
Me too! Mostly I just ignore them until I need to know something. There is some documentation on them in the forums somewhere, but I can never seem to find it when I need it.
I didn't realize that the XMM image was just another Spin binary file. I thought it was in some special Catalina format.
Most of the complexity is in the load process. This ties in with the discussion I have been having with David Saunders - I chose to do things this way (i.e. simple binary format, complex load process) to avoid the alternative (simple load process, but complex binary format - which would also necessitate writing my own assembler, designing my own object format and building my own binary linker!).
Ok I can understand the not wanting to write your own linker and create your own binary format, this is a bit of extra work (even with a simple binary instruction format as the prop). It is almost to bad that I do not have the time to do these things (I started a simple Pascal & Structured BASIC Compiler, for the prop [yes one compiler for both], about a month ago, and have only got as far as simple expression parsing [supporting no more than 8 level expression embedding and simple function calls (not much use with out functions )]). Oh what I would give for a HW stack (I guess we are stuck with software stacks for prop hosted compilers and there output).
I chose to do things this way (i.e. simple binary format, complex load process) to avoid the alternative (simple load process, but complex binary format - which would also necessitate writing my own assembler, designing my own object format and building my own binary linker!).
Not necessarily. You could use the ELF binary format and GNU binutils. Yes, ELF is a complicated format but that complexity is mostly handled automatically by binutils.
I presume you are running this on your DracBlade, but modified to include a mouse port?
Hardware is a dracblade with mouse on pins 24/25, keyboard on 26/27, serial 30/31, eeprom 28,29, SD 12-15, VGA 16-23 and external ram on 0-11.
Uses the default demoboard pins as much as possible.
I'll keep chugging along with the coding - soon it will be ready for a prime-time announcement.
@davidsaunders
(I started a simple Pascal & Structured BASIC Compiler, for the prop [yes one compiler for both], about a month ago, and have only got as far as simple expression parsing [supporting no more than 8 level expression embedding and simple function calls (not much use with out functions )]). Oh what I would give for a HW stack (I guess we are stuck with software stacks for prop hosted compilers and there output).
Big picture, where are you heading with this? Is the ultimate goal self-compilation on the prop? Or is there some other bigger picture goal?
Not necessarily. You could use the ELF binary format and GNU binutils. Yes, ELF is a complicated format but that complexity is mostly handled automatically by binutils.
This is fine for us geeks who write XMM programs, but it would not be appropriate for most LMM programs. For one thing, you'd end up with a program that cannot be loaded by an existing Propeller "out of the box" - and this capability is not something to be given away lightly. I wouldn't be at all surprised to find (even since the advent of the C3) that 99% of all Propellers sold are still not equipped with any XMM RAM capability, and their users just want to load simple binaries of 32kb or less. A compiler that can't generate such binaries is not going to be very popular.
Of course we could have a special format - and a whole new toolset - specifically for XMM programs. But given that all we really need is the current toolset plus a few different second-stage loaders (each consisting of a few hundred PASM instructions, since by necessity they must fit into a single cog), this seems like a fairly silly choice to make - unless you had another reason for doing so.
A good example of such a reason would be if you knew Parallax was going to choose the ELF format and gcc toolchain for the Propeller II. But if that were the case, I'm fairly sure that by now they would already be funding someone to do a binutils/gcc port. And why would I want to spend my spare time replicating a fairly pedestrian task that someone else is already being paid to do?
I wouldn't be at all surprised to find (even since the advent of the C3) that 99% of all Propellers sold are still not equipped with any XMM RAM capability, and their users just want to load simple binaries of 32kb or less.
Yes, I would agree with that. At the same time, I am totally hooked on Catalina and XMM. When I first started coding on CP/M 25 years ago I was always running out of memory. Finally by the mid '90s, PCs came along where the memory was sufficient that coding was limited by imagination. Then we were tempted with single chip micro-controllers, like the Picaxe and Arduino, and running out of memory became a big problem again (256 bytes, anyone?).
Even the propeller has only half the memory of a Z80 from 30 years ago.
But out of this comes Ross with Catalina running in XMM and suddenly there is no limit to external memory.
What you can do with this is amazing!
But this is all very cutting-edge, and I suspect it will take more nifty demos to take the propeller to the next level, which I believe to be large programs with cogs being loaded and re-loaded many times within the code.
Kudos and thanks to Ross for what he has created with Catalina, because I can't think of anything quite like it!
This is fine for us geeks who write XMM programs, but it would not be appropriate for most LMM programs. For one thing, you'd end up with a program that cannot be loaded by an existing Propeller "out of the box" - and this capability is not something to be given away lightly. I wouldn't be at all surprised to find (even since the advent of the C3) that 99% of all Propellers sold are still not equipped with any XMM RAM capability, and their users just want to load simple binaries of 32kb or less. A compiler that can't generate such binaries is not going to be very popular.
Of course we could have a special format - and a whole new toolset - specifically for XMM programs. But given that all we really need is the current toolset plus a few different second-stage loaders (each consisting of a few hundred PASM instructions, since by necessity they must fit into a single cog), this seems like a fairly silly choice to make - unless you had another reason for doing so.
A good example of such a reason would be if you knew Parallax was going to choose the ELF format and gcc toolchain for the Propeller II. But if that were the case, I'm fairly sure that by now they would already be funding someone to do a binutils/gcc port. And why would I want to spend my spare time replicating a fairly pedestrian task that someone else is already being paid to do?
I may be crazy, but I'm not that crazy!
Ross.
One of the tools in binutils is a tool that will read an ELF file and write it out into various formats. That tool could be easily extended to write a format compatible with the Propeller ROM loader for programs that are small enough to fit in hub memory. Alternatively, binutils provides a C library that makes it very easy to read the ELF format. You could use that to make a custom utility to write .binary files. The advantage is that you get the full binutils functionality all the way up until you actually need to make a downloadable image.
Big picture, where are you heading with this? Is the ultimate goal self-compilation on the prop? Or is there some other bigger picture goal?
The goal of my compilers is indeed self compilation on the prop. Unfortunately they are moving extremely slow as I do not have much time to work on them (the point of mentioning them above).
The goal of my compilers is indeed self compilation on the prop.
Maybe Ross can add something here. I took a look at the source code of Catalina itself (catalina.c in the source\catalina folder) and it looks like a very standard C program. It is 45kb in size, and this is slightly smaller than the size of the windows emulation I am writing in C (49kb and growing). Now, maybe there are some extra files here but it would be interesting to ask if the entire package can come in under 512k. Or 32Mhb using jazzed's external ram.
So two questions to ask: 1) what is the size of catalina and its support programs and 2) how many lines per second can it compile if running on the propeller?
Maybe there is a catch - eg is catalina shelling out to BTSC or some other program for some of the compilation?
Maybe Ross can add something here. I took a look at the source code of Catalina itself (catalina.c in the source\catalina folder) and it looks like a very standard C program. It is 45kb in size, and this is slightly smaller than the size of the windows emulation I am writing in C (49kb and growing). Now, maybe there are some extra files here but it would be interesting to ask if the entire package can come in under 512k. Or 32Mhb using jazzed's external ram.
So two questions to ask: 1) what is the size of catalina and its support programs and 2) how many lines per second can it compile if running on the propeller?
Maybe there is a catch - eg is catalina shelling out to BTSC or some other program for some of the compilation?
catalina.c is just a driver program that runs other programs to do its work. You need to look at the sources for the LCC compiler and the Catalina binder to get an idea of how much code needs to be ported to the Propeller.
catalina.c is just a driver program that runs other programs to do its work. You need to look at the sources for the LCC compiler and the Catalina binder to get an idea of how much code needs to be ported to the Propeller.
Hi Dr_A, Hi David ...
David is correct. Here is a brief taxonomy of the major components of Catalina that might help the discussion:
catalina : This is a wrapper program that is used to decode the command line and then call lcc.
lcc : This is another wrapper program that is used to perform the following steps:
preprocessing. This is done by calling cpp.
compiling. This is done by calling rcc. This converts the C code to an intermediate form, and then calls the code generator to convert that intermediate form to PASM. There are two different code generators that might be used depending on the program being compiled:
catalina.md (for TINY and SMALL programs)
catalina_large.md (for LARGE programs)
assembling. This step is not used by Catalina - assembly is deferred until the load phase.
loading. This is done by calling catbind. This binds the PASM sources together, resolves all the C library references and then calls:
homespun (or bstc) to assemble the final combined PASM file.
targetting. For SMALL and LARGE programs, catbind also compiles the chosen target (which is a SPIN program, so it uses homespun for this as well) and then combines the target and the assembled PASM program into a single executable (for TINY programs, the target SPIN program and the PASM program can be compiled in a single invokcation of homespun during the load phase).
To tie together a couple of different ongoing sub-threads, it is the catbind & homespun components that David is suggesting be replaced by binutils components (specifically, ld & as). The output of the compilation could then be ELF objects and binaries that would either need to be converted to Propeller binary format, or loaded using a new load program (similar to Catalina's XMM loader). But to use as (the GNU assembler) would also necessitate a substantial rewrite of the code generators (catalina.md and catalina_large.md), and also a rewrite of all the targets and drivers.
My own view is that this would take more work than it would to simply do a straight port of the whole of gcc itself - which in turn would have taken much more work than I have so far had to spend to port lcc. (I shoud point out here that my original lcc port only took a couple of months in total. Apart from a few bug fixes and optimizations, I have not really had to touch it now for many, many months - most of my time is now spent on various drivers and platform-dependent support issues).
Of course there are good reasons why Parallax may choose to go down the gcc path - but it would be primarily a commercial decision, and most of us here in these forums do what we do for the fun of it, and also for what we can learn along the way. How much fun would there be in simply porting gcc? How much would you learn? And would the final result be any better? There is no particular reason to think so, and in fact there is some concrete evidence to the contrary!
Of course there are good reasons why Parallax may choose to go down the gcc path - but it would be primarily a commercial decision, and most of us here in these forums do what we do for the fun of it, and also for what we can learn along the way. How much fun would there be in simply porting gcc? How much would you learn? And would the final result be any better? There is no particular reason to think so, and in fact there is some concrete evidence to the contrary!
Can you say what evidence there is to the contrary? I had thought that GCC contained a fairly capable target-independent optimizer that should generate decent code even without heavy target-dependent optimizations. I'm not that familiar with LCC though. Does it do any optimization itself? I know you've provded a Catalina optimizer but I assume that is a target-dependent optimizer. What level of optimizations is LCC capable of on its own?
Can you say what evidence there is to the contrary? I had thought that GCC contained a fairly capable target-independent optimizer that should generate decent code even without heavy target-dependent optimizations. I'm not that familiar with LCC though. Does it do any optimization itself? I know you've provded a Catalina optimizer but I assume that is a target-dependent optimizer. What level of optimizations is LCC capable of on its own?
Hi David,
Actually, lcc does do some fairly sophisticated optimization - but I'm sure gcc's is much better. I wasn't really thinking about optimization - the "evidence to the contrary" I mentioned is the only current port of a gcc toolchain we have - i.e. Zog. But I don't mean to belittle Zog here (who would! - he's one mean ice monster!). I meant instead that Zog illustrates how heavily the success of a gcc port is going to be dependent on the target you choose to port it to - you can't port gcc directly to PASM, so you have to port it to either a bytecode interpreter, or a variant of LMM PASM (have I missed any possibilities?).
So let's look at the options we currently have:
Bytecode interpreters. Here we have:
The SPIN bytecode interpreter. This would not be an ideal choice. It was optimized for interpreting SPIN, and I believe it was primarily optimized to maximize the functionality in the limited space available - not to optimize performance for a compiled language.
The JVM. Just add a C to Java translator and you're there! Well, not really. The JVM has both performance and size issues, as well as the fact that the JVM is really designed for Java, not C. But still a possibility.
The ZPU bytecode interpreter (as chosen for Zog). I think this has also proven to be not an ideal choice. I believe it was chosen for ease of implementation rather than performance - i.e. there was already a gcc toolchain for this particular target, so most of the work was already done. But this illustrates how having a good optimizer can't make up for deficiencies in the chosen target.
A new bytecode interpreter. Anything is possible with the Propeller, but I think you'll find that ZPU interpreter is actually fairly typical of the breed - it may even be better than average. I am fairly sure that no bytecode implementation will ever approach an LMM implementation for speed. A good bytecode implementation can keep the code size down, but at the expense of speed. So that brings us to ...
LMM implementations. Here we have:
Bill Henning's original LMM (or one of the variants of it). These are all quite minimalist implementations, and might seem like a good choice - but while they are a good choice for hand coded LMM PASM, for various reasons they aren't ideal for a stack oriented compiled language. They are a bit too minimalist and would have to be extended to both keep the code size down and give adequate performance.
The ImageCraft LMM implementation. This implementation gives very good performance for small programs, but cannot support large programs.
Catalina's LMM implementation. Naturally, I happen to think this is the best choice currently available - but even I would admit it is not perfect. For one thing, Catalina's LMM implementation was devised specifically to be a good fit for the lcc intermediate representation and code generation engine, and it would probably not be an ideal fit for the gcc equivalent.
A new LMM implementation. The best LMM implementation for a gcc port has probably not been written yet. But the success of gcc will depend very heavily on how good it is. A good one may indeed give you better results than Catalina - if you can devise one!
You seem to be implying that you could choose a fairly minimalist target and then just let the gcc optimizer take care of the rest - but no optimizer is going to get you around a poor choice of bytecodes or LMM primitives - both your code size and your performance will suffer significantly. The fact is that the Propeller wasn't designed with support for high-level languages in mind - and this means your target has to make up for it.
But the very sophistication of gcc means that getting this target right could be much harder than it was with lcc. Putting it another way, the back-end of lcc is fairly simple-minded and you don't have a huge amount of choice as to what primitive operations are needed - but the upside of this is that there's not too many ways you can go badly wrong. I suspect that with gcc there would be far more opportunities for things to turn out very poorly if you make a poor initial choice - and (like Zog!) you may not find out about it until you have virtually finished the port.
An excellent summary of the compiler/VM landscape. As it's Easter I'm not going to disagree with anything you have written there
Just in case anyone is wondering. Zog only exists because:
1) The GCC compiler for the ZPU architecture already existed.
2) It was simple to do, a nice little challenge to get the interpreter into a COG.
3) It works, even compares favourably to the Spin interpreter and Catalina XMM in some cases.
The ZPU byte codes are probably far from optimal for a prop implementation but the the ZPU architecture was designed to be a implemented on FPGAs with a minimal use of logic blocks for a 32 bit CPU. As support to custom hardware on an FPGA performance was not a priority.
Moving on to things I know very little about, I think you might be right about the work involved with using GCC. There is this wonderful quote from wikipedia:
GCC is a large and somewhat cumbersome system to develop; as one long-time GCC developer put it, "Trying to make the hippo dance is not really a lot of fun."
Today there is one more option that I don't recall seeing discussed here and that is LLVM and Clang. This combo is intended to be a replacement for GCC and by some accounts is easier to work with than GCC.
Today there is one more option that I don't recall seeing discussed here and that is LLVM and Clang. This combo is intended to be a replacement for GCC and by some accounts is easier to work with than GCC.
Hi Heater,
I looked at LLVM before deciding on LCC - it looked very promising, and I would agree with you that it is probably in between lcc and gcc in implementation complexity. What turned me off was the size and complexity of the VM it requires. I wasn't sure it could be done in a single cog. To get an idea of the size of the VM required, check out the list of instructions in the LLVM intermediate representation: http://llvm.org/docs/LangRef.html. In addition to the number of instructions, you also also have to consider their complexity (or their degree of abstraction from the hardware level) - for instance, the LLVM implements a "switch" statement as a single instruction. The LLVM is a very high level intermediate language that would take a lot of work to map efficiently to the architecture of the Propeller.
Given what Chip managed to accomplish with the SPIN VM in a single cog, I can't definitively say it wouldn't be possible to implement an LLVM in a single cog - but I can certainly say it was beyond my capabilites - so I chose LCC instead.
By the way, I also looked at CLR/CIL - especially is there is an LCC back end that generates CIL - but again decided that it was unlikely I could fit the required VM in a single cog.
What turned me off was the size and complexity of the VM it [LLVM] requires.
This does not sound quite right. Isn't LLVM supposed to be an "intermediate form". That is to say it is the common output of a number of different compilers of different languages which is then amenable to optimization and transformation that works for all languages. The LLVM finally being translated into the native instruction sets of whatever targets you have. It is not intended as a virtual machine to run on the targets Java style. Although I guess that is one approach.
As such using CLANG/LLVM for the Prop would be a case of creating the transformation from LLVM to Propeller LMM and defining that LMM kernel. Much like you have done for lcc.
This does not sound quite right. Isn't LLVM supposed to be an "intermediate form". That is to say it is the common output of a number of different compilers of different languages which is then amenable to optimization and transformation that works for all languages. The LLVM finally being translated into the native instruction sets of whatever targets you have. It is not intended as a virtual machine to run on the targets Java style. Although I guess that is one approach.
As such using CLANG/LLVM for the Prop would be a case of creating the transformation from LLVM to Propeller LMM and defining that LMM kernel. Much like you have done for lcc.
Ok, you got me - I'm simplifying a bit. But it is true to say that you can either do a simplistic implementation, where each LLVM instruction is mapped directly to either a PASM instruction or an LMM primitive (and some of them do map fairly directly to PASM instructions) - or you can further process the intermediate representation into something more suitable for your architecture. The problem is that the number of decisions you need to make here - and the number of potential redefinitions of your LMM implementation every time you realize in hindsight that you had made a sub-optimal decision - is what makes this process time consuming, complex and not guaranteed to converge on an optimal solution.
On the other hand, the VM targeted by lcc maps nearly one-for-one to actual machine instructions on many architectures (including the propeller) with just a few exceptions that typically map to a small number of instructions each. For comparison with the LLVM, the entire LCC VM is summarized by the simple table on page 10 of this document.
So the upside is that lcc does more of the work for you, and makes more of the hard decisions. The downside is that lcc potentially generates slightly less optimal code - assmuming (and this is a big assumption!) that you could make better optimization decisions than it can.
But the real decider was that as soon as I saw that table, I knew I could easily fit the LCC VM into a single cog!
You seem to be implying that you could choose a fairly minimalist target and then just let the gcc optimizer take care of the rest - but no optimizer is going to get you around a poor choice of bytecodes or LMM primitives - both your code size and your performance will suffer significantly. The fact is that the Propeller wasn't designed with support for high-level languages in mind - and this means your target has to make up for it.
Actually, if I were going to attempt a GCC port, my first choice would probably to use your Catalina LMM implementation because it already works and performs pretty well. In fact, I'm not entirely certain that it is possible to do much better. My reason for wanting GCC is not because there is anything wrong with Catalina. I just think that there is a big advantage to having the entire GCC toolchain available.
Also, I don't if I agree that the ZPU was a poor choice for ZOG. The version of ZOG that I have been working with most recently works pretty well. I like the development environment that we've setup and I would be still using it except that I wanted a compiler that could generate code that runs from flash with all data in hub memory and I couldn't get that to work with ZOG. It mostly works but has trouble communicating with PASM code loaded into other COGs. It is my suspicion that this is due to the fact that the ZPU is big-endian but I have not proven that. To be honest, I was tired of working on tools. I got interested in the Propeller because I wanted to learn to use its unique architecture not so I could spend endless time working on a C compiler. I was glad to move over to Catalina C that you support so well.
There is a fairly new optimizing C compiler, that is not yet popular, that has proven it self to optimize (for speed, or for size, or a good balance of both) much better than gcc. And it is fully retargetable and self hosting. It is called OCC, was coded by a single person (the same that had previously coded cc386, and cc68k). And a link : http://ladsoft.tripod.com/orangec.htm .
There is a fairly new optimizing C compiler, that is not yet popular, that has proven it self to optimize (for speed, or for size, or a good balance of both) much better than gcc. And it is fully retargetable and self hosting. It is called OCC, was coded by a single person (the same that had previously coded cc386, and cc68k). And a link : http://ladsoft.tripod.com/orangec.htm .
Hi David,
It is not retargetable yet - the web page says it is "heading in the direction of of being fully retargetable" and the documents in the source package say it requires Win32. Also, it doesn't look like it is intended to be a cross-compiler.
Comments
Next step is to add a textbox cursor so that it is possible to edit text. This could eventually become the front end for a self compiling compiler.
Is anyone else able to run this code? (It needs a propeller with external memory, mouse, keyboard, vga and sd card)
Would it be better to put the cogjects into arrays within the program, so that the sd card is not needed?
I'm sure I could figure this out myself by looking through the Catalina documents and source code but I haven't found all that I need in a few hours of looking so I thought I'd ask here to see if there is a simple answer. I know that an XMM binary file produced by the Catalina binder starts with a program to load into hub memory that contains the kernel as well as any plugins needed by the program. Following that is an image to load into external memory. What I'd like to do is write a PC-based program that parses a Catalina binary file and downloads the portion that is supposed to be written to the flash on a C3. To do this I have to be able to find the XMM image in the binary file. How do I do that? Does the hub image take up exactly 32k at the start of the file? I suspect that is not true since I did a hex dump of xbasic.binary and it doesn't look like an XMM image starting at 0x8000. Can you tell me how to parse the binary file to find the section that needs to be written to flash?
Thanks,
David
Hi Dr_A,
I presume you are running this on your DracBlade, but modified to include a mouse port? Are there any other single-CPU boards one which all these devices can be used at once? If not, then you won't find many users able to run this code. Can you make some of the devices "optional"?
Ross.
Hi David,
Yes, the target always takes exactly 32k, so $8000 is always the start of the XMM binary - specifically, it will be the start of the Catalina prologue (which you need to decode to figure out where everything else is).
It would be useful to generate a listing (just add -y to your compilation command) and use that as a reference when examining the binary file. There is no complexity in the XMM binary file format for the Flash layouts (there is some for the other layouts) - whatever you see in the listing at $XXXX, you should find in the binary file at $XXXX + $8000.
Ross.
I guess I'm confused because this is part of a dump of xbasic.binary:
008000 00b71b00 00101700 a64ca644 a650a63c
008010 0002a634 0000a62c 00000000 00000000
008020 5c3c0000 5c3c0000 5c3c0000 5c3c0000
I thought that the prolog always started with two longs of zero so it didn't seem like it was really at 0x8000 but more likely at 0x8018.
All Catalina programs are compiled with a SPIN/PASM compiler, so the PASM is always preceeded by the usual SPIN introduction - which in this case is $018 bytes long. This will match exactly what is shown in the listing.
Ross.
Can I count on it always being 0x18 bytes long?
Ross.
Oops - I was wrong - (now fixed above!). It's the number of methods, not objects that determine this offset - but Catalina only ever includes one.
Oops - wrong again (slightly) - Kuroneko has pointed out that it is both the number of methods and the number of objects that determine this offset in general - it is $18 for Catalina because Catalina.spin does not include any other objects, as well as only having one method.
Ross.
Not necessarily. You could use the ELF binary format and GNU binutils. Yes, ELF is a complicated format but that complexity is mostly handled automatically by binutils.
Hardware is a dracblade with mouse on pins 24/25, keyboard on 26/27, serial 30/31, eeprom 28,29, SD 12-15, VGA 16-23 and external ram on 0-11.
Uses the default demoboard pins as much as possible.
I'll keep chugging along with the coding - soon it will be ready for a prime-time announcement.
@davidsaunders
Big picture, where are you heading with this? Is the ultimate goal self-compilation on the prop? Or is there some other bigger picture goal?
This is fine for us geeks who write XMM programs, but it would not be appropriate for most LMM programs. For one thing, you'd end up with a program that cannot be loaded by an existing Propeller "out of the box" - and this capability is not something to be given away lightly. I wouldn't be at all surprised to find (even since the advent of the C3) that 99% of all Propellers sold are still not equipped with any XMM RAM capability, and their users just want to load simple binaries of 32kb or less. A compiler that can't generate such binaries is not going to be very popular.
Of course we could have a special format - and a whole new toolset - specifically for XMM programs. But given that all we really need is the current toolset plus a few different second-stage loaders (each consisting of a few hundred PASM instructions, since by necessity they must fit into a single cog), this seems like a fairly silly choice to make - unless you had another reason for doing so.
A good example of such a reason would be if you knew Parallax was going to choose the ELF format and gcc toolchain for the Propeller II. But if that were the case, I'm fairly sure that by now they would already be funding someone to do a binutils/gcc port. And why would I want to spend my spare time replicating a fairly pedestrian task that someone else is already being paid to do?
I may be crazy, but I'm not that crazy!
Ross.
Yes, I would agree with that. At the same time, I am totally hooked on Catalina and XMM. When I first started coding on CP/M 25 years ago I was always running out of memory. Finally by the mid '90s, PCs came along where the memory was sufficient that coding was limited by imagination. Then we were tempted with single chip micro-controllers, like the Picaxe and Arduino, and running out of memory became a big problem again (256 bytes, anyone?).
Even the propeller has only half the memory of a Z80 from 30 years ago.
But out of this comes Ross with Catalina running in XMM and suddenly there is no limit to external memory.
What you can do with this is amazing!
But this is all very cutting-edge, and I suspect it will take more nifty demos to take the propeller to the next level, which I believe to be large programs with cogs being loaded and re-loaded many times within the code.
Kudos and thanks to Ross for what he has created with Catalina, because I can't think of anything quite like it!
One of the tools in binutils is a tool that will read an ELF file and write it out into various formats. That tool could be easily extended to write a format compatible with the Propeller ROM loader for programs that are small enough to fit in hub memory. Alternatively, binutils provides a C library that makes it very easy to read the ELF format. You could use that to make a custom utility to write .binary files. The advantage is that you get the full binutils functionality all the way up until you actually need to make a downloadable image.
Maybe Ross can add something here. I took a look at the source code of Catalina itself (catalina.c in the source\catalina folder) and it looks like a very standard C program. It is 45kb in size, and this is slightly smaller than the size of the windows emulation I am writing in C (49kb and growing). Now, maybe there are some extra files here but it would be interesting to ask if the entire package can come in under 512k. Or 32Mhb using jazzed's external ram.
So two questions to ask: 1) what is the size of catalina and its support programs and 2) how many lines per second can it compile if running on the propeller?
Maybe there is a catch - eg is catalina shelling out to BTSC or some other program for some of the compilation?
catalina.c is just a driver program that runs other programs to do its work. You need to look at the sources for the LCC compiler and the Catalina binder to get an idea of how much code needs to be ported to the Propeller.
Hi Dr_A, Hi David ...
David is correct. Here is a brief taxonomy of the major components of Catalina that might help the discussion:
- catalina : This is a wrapper program that is used to decode the command line and then call lcc.
- lcc : This is another wrapper program that is used to perform the following steps:
- preprocessing. This is done by calling cpp.
- compiling. This is done by calling rcc. This converts the C code to an intermediate form, and then calls the code generator to convert that intermediate form to PASM. There are two different code generators that might be used depending on the program being compiled:
- catalina.md (for TINY and SMALL programs)
- catalina_large.md (for LARGE programs)
- assembling. This step is not used by Catalina - assembly is deferred until the load phase.
- loading. This is done by calling catbind. This binds the PASM sources together, resolves all the C library references and then calls:
- homespun (or bstc) to assemble the final combined PASM file.
- targetting. For SMALL and LARGE programs, catbind also compiles the chosen target (which is a SPIN program, so it uses homespun for this as well) and then combines the target and the assembled PASM program into a single executable (for TINY programs, the target SPIN program and the PASM program can be compiled in a single invokcation of homespun during the load phase).
To tie together a couple of different ongoing sub-threads, it is the catbind & homespun components that David is suggesting be replaced by binutils components (specifically, ld & as). The output of the compilation could then be ELF objects and binaries that would either need to be converted to Propeller binary format, or loaded using a new load program (similar to Catalina's XMM loader). But to use as (the GNU assembler) would also necessitate a substantial rewrite of the code generators (catalina.md and catalina_large.md), and also a rewrite of all the targets and drivers.My own view is that this would take more work than it would to simply do a straight port of the whole of gcc itself - which in turn would have taken much more work than I have so far had to spend to port lcc. (I shoud point out here that my original lcc port only took a couple of months in total. Apart from a few bug fixes and optimizations, I have not really had to touch it now for many, many months - most of my time is now spent on various drivers and platform-dependent support issues).
Of course there are good reasons why Parallax may choose to go down the gcc path - but it would be primarily a commercial decision, and most of us here in these forums do what we do for the fun of it, and also for what we can learn along the way. How much fun would there be in simply porting gcc? How much would you learn? And would the final result be any better? There is no particular reason to think so, and in fact there is some concrete evidence to the contrary!
Ross.
Can you say what evidence there is to the contrary? I had thought that GCC contained a fairly capable target-independent optimizer that should generate decent code even without heavy target-dependent optimizations. I'm not that familiar with LCC though. Does it do any optimization itself? I know you've provded a Catalina optimizer but I assume that is a target-dependent optimizer. What level of optimizations is LCC capable of on its own?
Hi David,
Actually, lcc does do some fairly sophisticated optimization - but I'm sure gcc's is much better. I wasn't really thinking about optimization - the "evidence to the contrary" I mentioned is the only current port of a gcc toolchain we have - i.e. Zog. But I don't mean to belittle Zog here (who would! - he's one mean ice monster!). I meant instead that Zog illustrates how heavily the success of a gcc port is going to be dependent on the target you choose to port it to - you can't port gcc directly to PASM, so you have to port it to either a bytecode interpreter, or a variant of LMM PASM (have I missed any possibilities?).
So let's look at the options we currently have:
But the very sophistication of gcc means that getting this target right could be much harder than it was with lcc. Putting it another way, the back-end of lcc is fairly simple-minded and you don't have a huge amount of choice as to what primitive operations are needed - but the upside of this is that there's not too many ways you can go badly wrong. I suspect that with gcc there would be far more opportunities for things to turn out very poorly if you make a poor initial choice - and (like Zog!) you may not find out about it until you have virtually finished the port.
Ross.
An excellent summary of the compiler/VM landscape. As it's Easter I'm not going to disagree with anything you have written there
Just in case anyone is wondering. Zog only exists because:
1) The GCC compiler for the ZPU architecture already existed.
2) It was simple to do, a nice little challenge to get the interpreter into a COG.
3) It works, even compares favourably to the Spin interpreter and Catalina XMM in some cases.
The ZPU byte codes are probably far from optimal for a prop implementation but the the ZPU architecture was designed to be a implemented on FPGAs with a minimal use of logic blocks for a 32 bit CPU. As support to custom hardware on an FPGA performance was not a priority.
Moving on to things I know very little about, I think you might be right about the work involved with using GCC. There is this wonderful quote from wikipedia:
Today there is one more option that I don't recall seeing discussed here and that is LLVM and Clang. This combo is intended to be a replacement for GCC and by some accounts is easier to work with than GCC.
Hi Heater,
I looked at LLVM before deciding on LCC - it looked very promising, and I would agree with you that it is probably in between lcc and gcc in implementation complexity. What turned me off was the size and complexity of the VM it requires. I wasn't sure it could be done in a single cog. To get an idea of the size of the VM required, check out the list of instructions in the LLVM intermediate representation: http://llvm.org/docs/LangRef.html. In addition to the number of instructions, you also also have to consider their complexity (or their degree of abstraction from the hardware level) - for instance, the LLVM implements a "switch" statement as a single instruction. The LLVM is a very high level intermediate language that would take a lot of work to map efficiently to the architecture of the Propeller.
Given what Chip managed to accomplish with the SPIN VM in a single cog, I can't definitively say it wouldn't be possible to implement an LLVM in a single cog - but I can certainly say it was beyond my capabilites - so I chose LCC instead.
By the way, I also looked at CLR/CIL - especially is there is an LCC back end that generates CIL - but again decided that it was unlikely I could fit the required VM in a single cog.
Ross.
This does not sound quite right. Isn't LLVM supposed to be an "intermediate form". That is to say it is the common output of a number of different compilers of different languages which is then amenable to optimization and transformation that works for all languages. The LLVM finally being translated into the native instruction sets of whatever targets you have. It is not intended as a virtual machine to run on the targets Java style. Although I guess that is one approach.
As such using CLANG/LLVM for the Prop would be a case of creating the transformation from LLVM to Propeller LMM and defining that LMM kernel. Much like you have done for lcc.
Ok, you got me - I'm simplifying a bit. But it is true to say that you can either do a simplistic implementation, where each LLVM instruction is mapped directly to either a PASM instruction or an LMM primitive (and some of them do map fairly directly to PASM instructions) - or you can further process the intermediate representation into something more suitable for your architecture. The problem is that the number of decisions you need to make here - and the number of potential redefinitions of your LMM implementation every time you realize in hindsight that you had made a sub-optimal decision - is what makes this process time consuming, complex and not guaranteed to converge on an optimal solution.
On the other hand, the VM targeted by lcc maps nearly one-for-one to actual machine instructions on many architectures (including the propeller) with just a few exceptions that typically map to a small number of instructions each. For comparison with the LLVM, the entire LCC VM is summarized by the simple table on page 10 of this document.
So the upside is that lcc does more of the work for you, and makes more of the hard decisions. The downside is that lcc potentially generates slightly less optimal code - assmuming (and this is a big assumption!) that you could make better optimization decisions than it can.
But the real decider was that as soon as I saw that table, I knew I could easily fit the LCC VM into a single cog!
Ross.
Also, I don't if I agree that the ZPU was a poor choice for ZOG. The version of ZOG that I have been working with most recently works pretty well. I like the development environment that we've setup and I would be still using it except that I wanted a compiler that could generate code that runs from flash with all data in hub memory and I couldn't get that to work with ZOG. It mostly works but has trouble communicating with PASM code loaded into other COGs. It is my suspicion that this is due to the fact that the ZPU is big-endian but I have not proven that. To be honest, I was tired of working on tools. I got interested in the Propeller because I wanted to learn to use its unique architecture not so I could spend endless time working on a C compiler. I was glad to move over to Catalina C that you support so well.
There is a fairly new optimizing C compiler, that is not yet popular, that has proven it self to optimize (for speed, or for size, or a good balance of both) much better than gcc. And it is fully retargetable and self hosting. It is called OCC, was coded by a single person (the same that had previously coded cc386, and cc68k). And a link : http://ladsoft.tripod.com/orangec.htm .
Hi David,
It is not retargetable yet - the web page says it is "heading in the direction of of being fully retargetable" and the documents in the source package say it requires Win32. Also, it doesn't look like it is intended to be a cross-compiler.
Ross.