Re cutting and pasting, that sounds tedious! Every time I do something manually I'm also automating it in the IDE. I guess that is why it is taking so long to get some quite simple things working here. Hopefully only a few days away from talking to and from the registry.
You can also use BradC's command line compiler BSTC to extract PASM compiled into binary blobs from Spin files. With a couple of command line options which I don't remember off hand.
Great to hear there are other options - thanks heater. I'm already reading binary files as part of the xmodem transfer, so if these options had not existed it would have been possible to discard the first n bytes before doing the transfer. But these pre-written ones are going to be easier - just add one line to a batch file.
Running Spinc creates a .h file that looks like this:
/**
* @file COGJECT_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2010, John Doe
*/
unsigned long COGJECT_array[] =
{
0xa0fc0441, 0x5c7c0000, 0x00007530
};
This appears to be the core of the original binary with 18 bytes stripped off the beginning and 8 bytes removed from the end. I understand the 18 bytes at the beginning being removed but I do not understand the 8 bytes at the end. Is it always 8 bytes regardless of the program?
Next- a big picture question. We have a .h file that is written in C (as an aside, this appears to be the C equivalent of the BASIC Data statement, and I've been looking for this syntax for a long time!).
How do we use this?
The idea I had is to create a binary file, ending in the extension .cog, that resides on the sd card and can be loaded into a cog.
Now I am thinking that in addition to this, maybe there is another way?
Because including fopen and fclose and other stdio commands in a c program made it a lot bigger, so big that it only can be run in XMM, I believe that this ties future development to XMM only platforms. Maybe this is not correct, but assuming this is the development path for the moment, what it means is that is a much lower cost for a piece of 2k cog code. 2k out of 32k on the propeller is a significant percentage. 2k out of 512k (or 32Mb) is much less significant.
Therefore, it could be quite possible to include code for, say, three different types of VGA drivers, inside a C program - either as standard code, or as included .h files.
So even though sd card access of cog code is quite easy to do, maybe it is better to include the code in the program. I am presuming that loading a cog then becomes as simple as doing a 'coginit' and pointing it at the array in the .h file?
I need to think more about the options here, eg how many cogjects might one ever want to conceivably use? Even if you had 50, that is only 100k out of 512k. Though I might be using more like 10-15.
This appears to be the core of the original binary with 18 bytes stripped off the beginning and 8 bytes removed from the end. I understand the 18 bytes at the beginning being removed but I do not understand the 8 bytes at the end. Is it always 8 bytes regardless of the program?
It's neither always $18 nor 8. It all depends on how many methods and objects were used to create the binary (you may have included an object which only holds constants etc). SPIN code itself follows DAT sections so there is your post-DAT tail. Best to leave it to the extractor tools
Because including fopen and fclose and other stdio commands in a c program made it a lot bigger, so big that it only can be run in XMM, I believe that this ties future development to XMM only platforms. Maybe this is not correct, but assuming this is the development path for the moment, what it means is that is a much lower cost for a piece of 2k cog code. 2k out of 32k on the propeller is a significant percentage. 2k out of 512k (or 32Mb) is much less significant.
Note that not every cog binary is 2K in size. For example the 3 built-in cog binaries in my current project only amount to 1008 bytes. So 2K each may be a rather pessimistic view.
You can also use BradC's command line compiler BSTC to extract PASM compiled into binary blobs from Spin files. With a couple of command line options which I don't remember off hand.
Homespun will offer similar functionality real soon now.
Thanks for the hints. Yes, best to leave it to the extractor tools.
I've added spinc into the batch file (it could also be BSTC).
This is a technical question for RossH, but I presume the .h file needs to be moved to the "include" folder, not my working "demos" folder?
Next step with "one click" pasm object creator is to add in some code to the main C program - the #include statement and a new function with the reference to the array and which loads it. My test program is called cogject.spin, the array created by spinc is called
unsigned long COGJECT_array[] and now I need a function:
void load_cogject()
which I think just needs to do a coginit pointing to the cogject_array.
Almost there with one keypress cogject creation...
addit: The following is being created with a few keypresses. I put the .h program in comments to remind me what it looks like. Now I'm a little stuck with the exact syntax for _coginit. From the catalina manual
int _coginit(int par, int addr, int cogid);
This function starts a new cog. The par and addr parameters must be
given as long addresses (which can easily be done by dividing the
normal byte addresses by 4). The cogid parameter can be a specific
cog, or the special value ANY_COG.
but I'm not quite sure what 'par' is, and whether I need to pass addr as the name of the array storing the data, or whether it has a * for a pointer.
Skeleton code below
#include <stdio.h>
#include <cogject.h>
/*
unsigned long COGJECT_array[] =
{
0xa0fc0441, 0x5c7c0000, 0x00007530
};
*/
void load_cogject()
{
// _coginit(int par, int addr, int cogid); // not sure about the syntax here
// par = ?, addr = cogject_array, cogid = 1?
}
void clearscreen() // white text on dark blue background
{
int i;
for (i=0;i<40;i++)
{
t_setpos(0,0,i); // move cursor to next line
t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100
}
}
void main ()
{
clearscreen();
printf("Hello, World!\n");
while (1); // Prop reboots on exit from main()!
}
help with the coginit line would be most appreciated (assuming this idea will actually load a cog!).
Next step with "one click" pasm object creator is to add in some code to the main C program - the #include statement and a new function with the reference to the array and which loads it. My test program is called cogject.spin, the array created by spinc is called
unsigned long COGJECT_array[] and now I need a function:
void load_cogject()
which I think just needs to do a coginit pointing to the cogject_array.
Correct - see the program test_spinc.c in the Catalina\demos\spinc subdirectory.
Almost there with one keypress cogject creation...
addit: The following is being created with a few keypresses. I put the .h program in comments to remind me what it looks like. Now I'm a little stuck with the exact syntax for _coginit. From the catalina manual
int _coginit(int par, int addr, int cogid);
This function starts a new cog. The par and addr parameters must be
given as long addresses (which can easily be done by dividing the
normal byte addresses by 4). The cogid parameter can be a specific
cog, or the special value ANY_COG.
but I'm not quite sure what 'par' is, and whether I need to pass addr as the name of the array storing the data, or whether it has a * for a pointer.
As above - see the program test_spinc.c in the Catalina\demos\spinc subdirectory for a working example.
Found the test_spinc file. I also read the readme.txt file - I'm doing option 3. So - your spinc code
/*
* Include the cog program formatted into a C arrary. The include file can
* be generated using the following commands:
*
* homespun flash_led.spin -b
* spinc flash_led.binary > flash_led_array.h
*/
#include "flash_led_array.h"
/*
* Include the cog function definitions
*/
#include <catalina_cog.h>
/*
* This is an example of how to format data to be passed tothe cog program
* (via the usual PAR parameter). Note this is an example only - the flash_led
* cog program does not actually expect any data.
*/
unsigned long data[] = { 1, 2, 3 };
/*
* The main C program - loads the cog program, then loops forever
*/
void main() {
if (_coginit ((int)data>>2, (int)flash_led_array>>2, ANY_COG) == -1) {
// LED should start flashing on success - turn it
// on and leave it to indicate a coginit failure
_dira(1, 1);
_outa(1, 1);
}
while (1) {
// loop forever - flash led cog should continue running
}
}
1) I take it that PAR is a series of parameters being passed to the cog. For the moment, can I just include the data array as a dummy array with your values 1,2,3? Does it always have three longs?
2) Each cogject will have a different PAR data array - would I be better off renaming these from data[] to something like, mycogject_par[]
3) Re (int)data>>2. Can we break that down into components, for a C newbie like me? I think there might be three things going on there -
i) a shift, two bits to the right. Can you explain this more?
ii) a conversion to an integer type. I'm not sure what is being converted here - is it the pointer to the data[] array?
iii) an implicit conversion to a pointer to the data[] array. Forgive my confusion regarding pointers - I think I understand pointers where the * and & are used, but I still get confused about pointers that are implied, like pointers to arrays.
4) (int)flash_led_array>>2
i) again, what is the shift for?
ii) flash_led_array is the same name as the .h file. Presumably this is the same as the name of the array within the .h file. But just to clarify, does this have to be the same as the .h name or the same as the array within the .h file?
Many thanks in advance.
eg
#include <stdio.h>
#include <cogject.h>
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
void load_cogject()
{
_coginit((int)par_cogject>>2, (int)cogject_array>>2, 7); // load into cog 7
}
void clearscreen() // white text on dark blue background
{
int i;
for (i=0;i<40;i++)
{
t_setpos(0,0,i); // move cursor to next line
t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100
}
}
void main ()
{
clearscreen();
printf("Hello, World!\n");
while (1); // Prop reboots on exit from main()!
}
where cogject.h is this
/**
* @file COGJECT_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2010, John Doe
*/
unsigned long COGJECT_array[] =
{
0xa0fc0441, 0x5c7c0000, 0x00007530
};
ADDIT
worked out 4 ii) - it is the array name within the file, AND it is case sensitive. testing this out now - it is almost compiling
ADDIT again.
I have a program that compiles, but it is not quite working yet. The .h file is as above and the program is thus:
/* Cog test program - see bottom of code for the Main function */
#include <stdio.h>
#include <cogject.h>
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
void load_cogject()
{
_coginit((int)par_cogject>>2, (int)cogject_array>>2, 7); // load into cog 7
}
void clearscreen() // white text on dark blue background
{
int i;
for (i=0;i<40;i++)
{
t_setpos(0,0,i); // move cursor to next line */
t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100
}
}
void stopcog(int cognumber) // stop a cog
{
_cogstop(cognumber);
}
void sleep(int milliseconds) // sleep function
{
_waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
}
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void fill_memory(int start_address,int finish_address,char value) // fill memory with these bytes
{
int i;
for(i=start_address;i <= finish_address;i++)
{
poke(i,value);
}
}
void main ()
{
char c;
int i;
clearscreen();
printf("Hello, World!\n");
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
i=30000; // location to peek and poke
poke(i,66); // poke a B, the cog should change this to an A
printf("load cogject into cog 7\n");
load_cogject();
sleep(1000); // delay while cog gets started
c=peek(i); // peek byte at this location
printf("Peek value at %i = character %c Ascii value %d \n",i,c,c);
while (1); // Prop reboots on exit from main() so stop this
}
pasm part is
CON
_clkfreq = 80_000_000
_clkmode = xtal1 + pll16x
PUB Main
coginit(1,@cogstart,0) ' cog 1, cogstart, dummy value
DAT
org 0
cogstart
mov testvariable,#65 ' test value A
jmp #cogstart
testvariable long $7530 ' test memory location 30000
fit 496
1) I take it that PAR is a series of parameters being passed to the cog. For the moment, can I just include the data array as a dummy array with your values 1,2,3? Does it always have three longs?
It doesn't have to be anything - that was just an example.
3) Re (int)data>>2. Can we break that down into components, for a C newbie like me? I think there might be three things going on there -
i) a shift, two bits to the right. Can you explain this more?
Shift to the right by 2 is just divide by 4 - i.e. convert a byte address to a long address. I probably should have hidden this level of detail within the coginit function - or you could hide it in your own version of coginit. For more information, see the Parallax documentation (look at the PASM version of coginit, not the SPIN version).
ii) a conversion to an integer type. I'm not sure what is being converted here - is it the pointer to the data[] array?
Yes. My version coginit function expects ints. The parameters are usually addresses - but the par parameter in particular can just be any 14 bit value. Agian, you could hide this detail in your own version of coginit.
iii) an implicit conversion to a pointer to the data[] array. Forgive my confusion regarding pointers - I think I understand pointers where the * and & are used, but I still get confused about pointers that are implied, like pointers to arrays.
In C the name of an array is a pointer to the first element - so data is in fact a long *.
ii) flash_led_array is the same name as the .h file. Presumably this is the same as the name of the array within the .h file. But just to clarify, does this have to be the same as the .h name or the same as the array within the .h file?
Thanks for all that. Things like the >> are now one button copy and paste exercises, so I'll leave it as it is.
Code compiles but the cog is not doing what it is supposed to do. This is getting close now, and it probably is just one tiny little mistake. Can you take a look at this and see what you think:
pasm code
CON
_clkfreq = 80_000_000
_clkmode = xtal1 + pll16x
PUB Main
coginit(1,@cogstart,0) ' cog 1, cogstart, dummy value
DAT
org 0
cogstart
mov testvariable,#65 ' test value A
jmp #cogstart
testvariable long 5000 ' test memory location 5000
fit 496
.h code, filename is cogject.h
/**
* @file cogject_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2010, John Doe
*/
unsigned long cogject_array[] =
{
0xa0fc0441, 0x5c7c0000, 0x00001388
};
and the program to run this
/* Cog test program - see bottom of code for the Main function */
#include <stdio.h>
#include <cogject.h>
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
void load_cogject()
{
_coginit((int)par_cogject>>2, (int)cogject_array>>2, 7); // load into cog 7
}
void clearscreen() // white text on dark blue background
{
int i;
for (i=0;i<40;i++)
{
t_setpos(0,0,i); // move cursor to next line */
t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100
}
}
void stopcog(int cognumber) // stop a cog
{
_cogstop(cognumber);
}
void sleep(int milliseconds) // sleep function
{
_waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
}
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void fill_memory(int start_address,int finish_address,char value) // fill memory with these bytes
{
int i;
for(i=start_address;i <= finish_address;i++)
{
poke(i,value);
}
}
void main ()
{
char c;
int i;
clearscreen();
printf("Hello, World!\n");
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
i=5000; // location to peek and poke
poke(i,66); // poke a B, the cog should change this to an A
printf("load cogject into cog 7\n");
load_cogject();
sleep(1000); // delay while cog gets started
c=peek(i); // peek byte at this location
printf("Peek value at %i = character %c Ascii value %d \n",i,c,c);
while (1); // Prop reboots on exit from main() so stop this
}
It only does what you tell it to do. Namely fill cog register testvariable with #65 (thereby overwriting the 5000 stored in there). You want to affect hub RAM address 5000 so you should use hub access code.
DAT org 0
cogstart [B][COLOR="blue"]wrbyte[/COLOR][/B] testvalue, testvariable ' test value A
jmp #cogstart
testvariable long 5000 ' test memory location 5000
testvalue long 65
fit
DAT org 0
cogstart rdlong testvariable, par
cogloop wrbyte testvalue, testvariable ' test value A
jmp #cogloop
testvariable long 0 ' test memory location
testvalue long 65
@Ross - not quite ready for your suggestion as the code still isn't doing what it is supposed to do. Looking forward to sending a variable, but first:
CON
_clkfreq = 80_000_000
_clkmode = xtal1 + pll16x
PUB Main
coginit(1,@cogstart,0) ' cog 1, cogstart, dummy value
DAT
org 0
cogstart wrbyte testvalue, testvariable ' test value A
jmp #cogstart
testvariable long 5000 ' test memory location 5000
testvalue long 65 ' ascii A
fit 496
Just to double check, the .lst output is
0000: 00 b4 c4 04 ' Frequency: 80000000 Hz
0004: 6f ' XTAL mode
0005: 83 ' Checksum
0006: 10 00 ' Base of program
0008: 30 00 ' Base of variables
000a: 38 00 ' Base of stack
000c: 28 00 ' Initial program counter
000e: 3c 00 ' Initial stack pointer
'******************************************************************************
' cogject.spin
'******************************************************************************
'=================================== CONs =====================================
_clkfreq = 80000000
_clkmode = 1032
'=============================== Object Header ================================
0010: 20 00 02 00 ' 32 bytes, 2-1 methods, 0 object pointers
0014: 18 00 00 00 ' ptr #1 to $0028: PUB Main (locals size: 0)
'================================ DAT Section =================================
0018(0000): ' org 0
0018(0000): 02 06 3c 00 ' cogstart wrbyte testvalue, testvariable ' test value A
001c(0001): 00 00 7c 5c ' jmp #cogstart
0020(0002): 88 13 00 00 ' testvariable long 5000 ' test memory location 5000
0024(0003): 41 00 00 00 ' testvalue long 65 ' ascii A
'============================ Method #1: PUB Main =============================
'PUB Main
'------------------------------------------------------------------------------
coginit(1,@cogstart,0) ' cog 1, cogstart, dummy value
'------------------------------------------------------------------------------
0028: 36 PUSH#1
0029: c7 08 PUSH#.L OBJ+8
002b: 35 PUSH#0
002c: 2c COGISUB
002d: 32 RETURN
002e: 00 00
0030: ff ff f9 ff ff ff f9 ff
and the .h file is
/**
* @file cogject_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2010, John Doe
*/
unsigned long cogject_array[] =
{
0x003c0602, 0x5c7c0000, 0x00001388, 0x00000041
};
Your main problem is that you are using the XMM LARGE (-x5) memory model, where data is in XMM RAM, not Hub RAM. This means your cogject_array is in XMM RAM, and therefore the coginit function can't load it - instead, it is loading some random rubbish from hub RAM.
If you want to continue to use this memory model, you will need to copy the cogject_array to hub RAM before attempting to use it. The same is true of the par_cogject array. Since local variables are always in hub RAM, you should use code something like this:
void load_cogject()
{
unsigned long hub_array[512];
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
int i;
for (i = 0; i < 512; i++) {
hub_array[i] = cogject_array[i];
}
_coginit((int)par_cogject>>2, (int)hub_array>>2, 7); // load into cog 7
}
Also, you are quite likely to be running out of cogs. Since you don't need the mouse, or the keyboard, or the floating point plugin, or the SD card plugin (all of which are being loaded for you) you should use a compile command more like the following:
Ah - that would make sense. So I'll need to set aside a little bit of buffer space in hub for loading cogs? The same buffer space could be reused each time a cog is loaded.
Looks like I need a generic routine based on your example to move data from array x to hub. And if we pass the location of an array, it could 'hide' the >> for the coginit like you mentioned earlier.
In a general sense, all the catalina comms are in high memory, so maybe I could use a buffer from 0-512, (0-2 kilobytes) and then maybe 20k for video buffer, and that still should leave room for stack space (?10k)
OR - I just noticed
Since local variables are always in hub RAM
. Where are they exactly - up the top of hub ram?
Re the options without the mouse, the next thing I am going to do is try wiping out the cogs one at a time and then reloading them from the software. So I'll probably leave the default there for debugging. That might be a bit of a challenge, but it is a step along the way towards doing clever things like changing the VGA from text to color to grayscale from within software.
Ah - that would make sense. So I'll need to set aside a little bit of buffer space in hub for loading cogs? The same buffer space could be reused each time a cog is loaded.
Looks like I need a generic routine based on your example to move data from array x to hub. And if we pass the location of an array, it could 'hide' the >> for the coginit like you mentioned earlier.
In a general sense, all the catalina comms are in high memory, so maybe I could use a buffer from 0-512, (0-2 kilobytes) and then maybe 20k for video buffer, and that still should leave room for stack space (?10k)
.
Just use a local array, as in my example. When you need it, it gets allocated automatically, and when you don't it doesn't consume any valuable hub space.
Crossed post there - I just reread your post and realised what you said. So - when I call a function, if I define an array in that function for say, 1000 bytes, does that array get stored in the hub (in the stack?) and then is deallocated when the function finishes?
If so - does this mean the biggest array one could have in a local function is about 30k?
Crossed post there - I just reread your post and realised what you said. So - when I call a function, if I define an array in that function for say, 1000 bytes, does that array get stored in the hub (in the stack?) and then is deallocated when the function finishes?
If so - does this mean the biggest array one could have in a local function is about 30k?
Yes, 32kb is the biggest you can have as a local variable under any Catalina memory model.
If you need anything bigger than that you must use the XMM LARGE memory model and allocate the space you need on the heap (using malloc) - you can have megabytes of storage that way.
This is one of the main differences between the XMM LARGE and XMM SMALL memory models - i.e. in the XMM LARGE (-x5) memory model the heap is in XMM RAM. In the XMM SMALL (-x2) memory model the heap is in HUB RAM.
Isn't C just full of surprises. malloc for instance.
But now I know how to load and reload cogs from within a program, I think this is going to open up a whole lot of new possibilities. First, the obvious one is that it frees up hub ram to do the one thing that I think hub ram does better than anything else, and that is to act as a video buffer. The second thing is to help solve the problem of never having quite enough cogs. For instance, you can load an SD driver cog, read the sd card with 100k of data, replace that driver with a serial port driver cog, and output the data. Or load some vga driver cogs and play a little movie with that 100k of data. Or play a wav file.
Ok, next little task is to pass a variable and get it back. I need to go back and study the registry in more detail.
Then I need to take a look at a typical Obex spin/pasm piece of code and think about how to turn this into a C/pasm piece of code. It probably will involve keeping the pasm largely as is, and turning the Spin into C. But it is the binding of the two that is the complicated part. From a vague top-down point of view, I think the idea is to take each Spin PUB function and translate to a C function, but keep links to the associated pasm code. So I think I'll be using the vb.net IDE and frequently swapping between the C and PASM tabs.
BTW - if there is anyone out there who wants to be part of this, let us know as I can post the IDE code. I had a crazy idea on the way home from work that vb.net does lend itself to collaborative input on a program, in that you can create a program and create a tab frame and allow contributors to add a tab item to the frame, then they can add code and visual objects as much as they like and it would stay separate from the other code. eg I have some skeleton code for image manipulation as a separate tab, so if, hypothetically, someone wanted to add a tab for sound manipulation, they could contribute to the IDE without affecting other code.
Just a thought, anyway. My little task here is to make things easier to use. eg - you can run the following code on a propeller by pressing F12
/* Cog test program - see bottom of code for the Main function */
#include <stdio.h>
#include <cogject.h>
void load_cogject()
{
unsigned long hub_array[512];
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
int i;
for (i = 0; i < 512; i++)
{
hub_array[i] = cogject_array[i];
}
_coginit((int)par_cogject>>2, (int)hub_array>>2, 7); // load into cog 7
}
void clearscreen() // white text on dark blue background
{
int i;
for (i=0;i<40;i++)
{
t_setpos(0,0,i); // move cursor to next line */
t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100
}
}
void stopcog(int cognumber) // stop a cog
{
_cogstop(cognumber);
}
void sleep(int milliseconds) // sleep function
{
_waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
}
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void fill_memory(int start_address,int finish_address,char value) // fill memory with these bytes
{
int i;
for(i=start_address;i <= finish_address;i++)
{
poke(i,value);
}
}
void main ()
{
char c;
int i;
clearscreen();
printf("Hello, World!\n");
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
i=5000; // location to peek and poke
poke(i,66); // poke a B, the cog should change this to an A
printf("load cogject into cog 7\n");
load_cogject();
c=peek(i); // peek byte at this location
printf("Peek value at %i = character %c Ascii value %d \n",i,c,c);
while (1); // Prop reboots on exit from main() so stop this
}
I've had a little break away from this code and it has been good because I've been able to reflect on some cunning ways to write code. (I have updated my website - see link, for the latest board plus inbuilt links to Youtube and Paypal. Thanks ++ to http://viviti.com/ for making it easy to build an online store).
Ok, this is the design challenge. I want to be able to write inline pasm code within a C program in the same way as the proptool. Now, Catalina in external memory can have inline pasm because it is running in LMM, but this isn't quite as useful as I'd like because the timing is not precise, it is not as fast as real cog code, and you can't set things running in the background.
So I want some pasm code that I can load into a cog on the fly from within code. All fine, but for me personally, I want the entire program to be a single text file. RossH on the other hand has said he does not want me to create code that only my IDE can compile, and it is great he has said this because it makes a lot of sense.
So - how do you put pasm code in a C program that will compile with Catalina and also with my IDE?
The answer I have come up with is to put the pasm code within a comment block. So C treats it as a comment. But my IDE is able to find that code, preprocess it, shell out to SpinC to produce a .h file, then reinsert the compiled binary data into the program as a data statement for an array.
So what you can do is use the IDE and compile/download/run the included cog code with a single press of F12.
But you could also do a copy/paste of the pasm code, run it through spinc manually and then manually paste it back into the program, and so this would also run fine with any catalina compiler.
I've also added some automatic color highlighting of the pasm code.
There is also the ability to rebuild all the indents so they look neat (the code to do this is easier to write if { sits on a line by itself, so that is the reason I'm using the slightly more unusual C formatting of putting { on its own line.)
Screenshot of what the code looks like is below. I'm adding various buttons so you can pre-write this code easily, eg this was written with one click of a button.
I've had a little break away from this code and it has been good because I've been able to reflect on some cunning ways to write code. (I have updated my website - see link, for the latest board plus inbuilt links to Youtube and Paypal. Thanks ++ to http://viviti.com/ for making it easy to build an online store).
Ok, this is the design challenge. I want to be able to write inline pasm code within a C program in the same way as the proptool. Now, Catalina in external memory can have inline pasm because it is running in LMM, but this isn't quite as useful as I'd like because the timing is not precise, it is not as fast as real cog code, and you can't set things running in the background.
So I want some pasm code that I can load into a cog on the fly from within code. All fine, but for me personally, I want the entire program to be a single text file. RossH on the other hand has said he does not want me to create code that only my IDE can compile, and it is great he has said this because it makes a lot of sense.
So - how do you put pasm code in a C program that will compile with Catalina and also with my IDE?
The answer I have come up with is to put the pasm code within a comment block. So C treats it as a comment. But my IDE is able to find that code, preprocess it, shell out to SpinC to produce a .h file, then reinsert the compiled binary data into the program as a data statement for an array.
So what you can do is use the IDE and compile/download/run the included cog code with a single press of F12.
But you could also do a copy/paste of the pasm code, run it through spinc manually and then manually paste it back into the program, and so this would also run fine with any catalina compiler.
I've also added some automatic color highlighting of the pasm code.
There is also the ability to rebuild all the indents so they look neat (the code to do this is easier to write if { sits on a line by itself, so that is the reason I'm using the slightly more unusual C formatting of putting { on its own line.)
Screenshot of what the code looks like is below. I'm adding various buttons so you can pre-write this code easily, eg this was written with one click of a button.
Hi Dr_Acula,
This seems like a good solution - incluing the PASM that the C code relies on as a comment block makes it self-documenting. And being able to compile it with other tools means it is not tied to any specific toolset.
Great. I got the vb code working tonight. This can handle any number of included pasm code - just name each one a different name in the comment block eg
PASM Start mycode.spin
and 'mycode' becomes the name of the .h file as well as the unique name of the array in the C function.
I've got a 'precompile' button on the IDE that rebuilds the code. Take the pasm code, create a .h file, remove the previous C function below the pasm code block and completely rebuild it with the new code.
So easy to use - I changed the test letter from A to D, clicked the precompile button, then F12 to compile and download and it works.
Private Sub CompileWithPASMCodeToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CompileWithPASMCodeToolStripMenuItem.Click
' convert all pasm code into data listings underneath the pasm code as a C function
Dim i As Integer
Dim NumberLines As Integer
Dim PasmCode As Boolean
Dim LineOfText As String
Dim PasmString As String
Dim PASMFilename As String
Dim RemoveFlag As Boolean
Dim TempString As String
PasmCode = False ' not reading some pasm code
NumberLines = RichTextBox1.Lines.Length
'create all the cogs as .h files
For i = 0 To NumberLines - 1
LineOfText = RichTextBox1.Lines(i)
' order here is important - end first, then check, then start
If Strings.Left(LineOfText, 8) = "PASM End" Then
TextBox16.Text = PASMFilename ' move over to the pasm tab
RichTextBox3.Text = PasmString ' move to the pasm richtextbox
CompilePause = False ' stop at end of shell - not writing file properly with no pause?!
System.Windows.Forms.Application.DoEvents() ' update the richtextbox
CompilePasm() ' compile it and save it
PasmCode = False ' finish logging
System.Windows.Forms.Application.DoEvents() ' update the richtextbox
End If
If PasmCode = True Then PasmString += LineOfText + vbCrLf
If Strings.Left(LineOfText, 10) = "PASM Start" Then
TabControl1.SelectedIndex = 4 ' display the pasm code
System.Windows.Forms.Application.DoEvents() ' update the richtextbox
PasmCode = True ' start logging the data
PasmString = "" ' clear the string
PASMFilename = Strings.Mid(LineOfText, 12) ' filename stored after PASM Start
End If
Next i
' remove all the associated cog code underneath the pasm ie the next function
' use tempstring as a temp store
RemoveFlag = False
TempString = ""
For i = 0 To NumberLines - 1
LineOfText = RichTextBox1.Lines(i)
If RemoveFlag = False Then TempString += LineOfText + vbCrLf
If RemoveFlag = True And LineOfText = "}" Then RemoveFlag = False
If Strings.Left(LineOfText, 8) = "PASM End" Then RemoveFlag = True
Next
RichTextBox1.Text = TempString ' put back the modified file
NumberLines = RichTextBox1.Lines.Length ' shorter now so get new length
TempString = ""
For i = 0 To NumberLines - 1
LineOfText = RichTextBox1.Lines(i)
If Strings.Left(LineOfText, 10) = "PASM Start" Then
PASMFilename = Strings.Mid(LineOfText, 12) ' filename stored after PASM Start
PASMFilename = Strings.Left(PASMFilename, Strings.Len(PASMFilename) - 5) ' strip off the spin bit
End If
If Strings.Left(LineOfText, 8) = "PASM End" Then
' add in the new code
TempString += "PASM End" + vbCrLf
TempString += "*/" + vbCrLf
TempString += vbCrLf
TempString += "void " + PASMFilename + "() // unique name for each loader" + vbCrLf
TempString += "{" + vbCrLf
' add the .h file
FileOpen(1, "C:\Program Files\Catalina\Demos\" + PASMFilename + ".h", OpenMode.Input) ' open a file
Do
LineOfText = LineInput(1)
TempString += LineOfText + vbCrLf
Loop Until LineOfText = "};"
FileClose(1) ' close the .h file
TempString += "unsigned long hub_array[512];" + vbCrLf
TempString += "unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used" + vbCrLf
TempString += "int i;" + vbCrLf
TempString += "for (i = 0; i < 512; i++)" + vbCrLf
TempString += "{" + vbCrLf
TempString += "hub_array[i] = " + PASMFilename + "_array[i];" + vbCrLf
TempString += "}" + vbCrLf
TempString += "_coginit((int)par_cogject>>2, (int)hub_array>>2, 7); // load into cog 7" + vbCrLf
TempString += "}" + vbCrLf
Else
TempString += LineOfText + vbCrLf ' add line to tempstring
End If
Next i
RichTextBox1.Text = TempString ' put back modified file
Indents() ' add the proper indentation
ColorCatalina() ' display in color
TabControl1.SelectedIndex = 0 ' display the c code
End Sub
Ok, now on to something more complicated. The whole point of all this is to make it much easier to translate Spin objects into Catalina objects. Using the above it is now possible to write lots of C functions that copy the Spin functions, and then hopefully copy and paste the PASM in directly.
The hard part is binding the two together. I'm going to try this with some real code and see what happens. It may be that the registry is enough and you can pass the location of a block of hub ram.
The hard sort of code is where the Pasm part of an existing spin object is talking to a block of hub ram. It might be an array, or worse, it might be a number of variables that on the spin compiler end up scattered all over hub ram. The spin compiler does not care, as the pasm object code knows where the variables ended up.
I'm thinking of how this fits with the registry model. Say existing spin code has pasm that is talking to 100 bytes of hub ram. At the very least, if those are not contiguous you would rewrite the code so that it is. But even after doing this, we need a way to say that these 100 bytes of ram are reserved and I am not sure how to do that with the registry.
Say object 1 reserves 100 bytes and object 2 reserves 10 and object 3 reserves 4. We don't want each one to overwrite the other. And it would help to have the flexibility to add more code.
I am wondering about using constants in the C code and linking the C functions using those constants. And also passing those constants to the pasm comment blocks.
I guess there will never be a perfect solution if many cogs are being loaded and reloaded. As an example, say one is loading cogs to drive text graphics, then to drive color graphics. Color might reserve 19200 bytes of hub ram. Text would be 3200. So one might say that color graphics are bigger than text, so then put the keyboard circular buffer above 19200. One could be loading in a variety of display drivers/tile drivers etc, but as long as they are never more than 19200 then things would be ok.
Mostly this is something that C contains within itself, but sometimes changing something might need to get passed to pasm as well. So any constant definitions in C might be at the beginning, and maybe the pasm preprocessor looks for these and modifies pasm code accordingly?
To put it another way, maybe the whole hub memory map is defined with constants at the beginning of the C program?
I guess I'll need to have a go at translating an existing object to get a feel for how this might work.
Addit: Brainwave this morning in the shower. At the beginning of the C program, define a list of constants that show what the memory map should be eg
2048 for cog loading
19200 for graphics buffer
50 for keyboard buffer
128 for serial port buffer
C knows about this as these are const definitions
PASM can be told about it as the preprocessor can copy this list of constants into every bit of PASM code into the CON section, and translate from C syntax to pasm syntax.
This becomes the glue between pasm and C
This will make translating existing object code to C a lot easier.
In the code below I'm starting to define constants at the beginning of the C program that define how hub memory is to be used.
I am still a little confused about loading cogs. I'd like to reserve hub memory locations 0 to 2047 for loading cogs.
In this code, how does unsigned long hub_array[512]; know where it should end up?
Does it end up at location 0 or some other location in hub?
Help here would be most appreciated!
Below is some code that glues together C and PASM. Hit Shift F12 and it compiles/downloads/runs on a propeller. But there are some subtleties here:
1) constants defined at the beginning of the C program are translated to CON and copied to every example of PASM code.
2) The precompiler takes all PASM code and converts it to C and copies it back into the C program by reading back the .h program that has been created.
3) at the bottom of the screenshot is a line "testvariable long graphicstart". This shows an example of gluing C and PASM together. At the bottom of the code text is how this is used in C.
i=graphicstart; // location to peek and poke
So - this is still a standard C program. It will compile with catalina.
But- change a constant at the beginning of the C program and recompile and all the PASM gets updated too.
If you want to share variables, use a constant hub location as both C and PASM will know where it is.
And yes, I know this bypasses the registry system. Why am I doing that? Well, I am worried about existing PASM objects that have no code space free at all. This system means that such code should drop into Catalina objects with no need to add any registry interface code.
I think I am now getting close to attempting to port spin objects over to C.
/* PASM demo for use with Catalina Precompiler/Compiler */
#include <stdio.h>
/* Hub memory map and other constants used by PASM routines */
const unsigned long int cogload = 0; // start of space for loading cogs 2048 bytes= 496 longs
const unsigned long int cogloadsize = 2048; // size of cogload space
const unsigned long int graphicstart = 2048; // start of space for video graphics buffer
const unsigned long int graphicsize = 19200; // size of graphics buffer = 19200 bytes
const unsigned long int keyboardstart = 21248; // start of space for keyboard
const unsigned long int keyboardsize = 100; // size of space for keyboard
const unsigned long int cogjectstart = 21348; // start of space for mycogject
const unsigned long int cogjectsize = 1; // size of space for mycogject
/* End hub memory map */
/* PASM Code for compilation using SpinC and inclusion as a data file
PASM Start mycogject.spin
CON
_clkfreq = 80_000_000 ' 5Mhz Crystal
_clkmode = xtal1 + pll16x ' x 16
' Start of C hub memory map constants
cogload = 0
cogloadsize = 2048
graphicstart = 2048
graphicsize = 19200
keyboardstart = 21248
keyboardsize = 100
cogjectstart = 21348
cogjectsize = 1
' End of C constants
PUB Main
coginit(1,@cogstart,0) ' cog 1, cogstart, dummy value
DAT
org 0
cogstart wrbyte testvalue, testvariable ' test value A
jmp #cogstart
testvariable long graphicstart ' test memory location at graphicstart
testvalue long 65 ' ascii A
fit 496
PASM End
*/
void mycogject() // unique name for each loader
{
/**
* @file mycogject_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2010, John Doe
*/
unsigned long mycogject_array[] =
{
0x003c0602, 0x5c7c0000, 0x00000800, 0x00000041
};
unsigned long hub_array[512];
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
int i;
for (i = 0; i < 512; i++)
{
hub_array[i] = mycogject_array[i];
}
_coginit((int)par_cogject>>2, (int)hub_array>>2, 7); // load into cog 7
}
void clearscreen() // white text on dark blue background
{
int i;
for (i=0;i<40;i++)
{
t_setpos(0,0,i); // move cursor to next line
t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100
}
}
void sleep(int milliseconds) // sleep function
{
_waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
}
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void main ()
{
char c;
int i;
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
i=graphicstart; // location to peek and poke
poke(i,66); // poke a B, the cog should change this to an A
printf("load cogject into cog 7\n");
mycogject();
c=peek(i); // peek byte at this location
printf("Peek value at %i = character %c Ascii value %d \n",i,c,c);
while (1); // Prop reboots on exit from main()
}
Learning a lot as we go. Re the question above, I think the cogs are loaded from within the stack space at the top of hub ram, so I think I can start using hub ram from location 0 instead of location 2048.
These are some notes to myself as I port Kye's video driver over to C. Some of these will be obvious to those experienced with C and Spin.
1) in the dummy PUB that has coginit, the value in cogint that starts with an @ should match with the first line in the DAT section that comes after the org 0.
2) Kye's code uses some calculations to work out values based on pins and frequency, including some spin code that does a 64 by 32 bit divide. For pin group 2, the constants work out as
directionstate_const = $00FF0000 ' for pin group 2
videostate_const = $30_00_04_FF ' for pin group 2
frequencystate_const = 337914606 ' calculated for 80Mhz
3) For constants just used locally in PASM, define them in the CON section above the _clkfreq line. The precompiler uses the line _clkmode as a flag and copies the C constants after this line, so this is where common constants go that are used by both C and PASM.
4) For passing longs between C and PASM, define a memory location in the global constants part of C. Then use wrlong and rdlong in pasm, and peek and poke in C to pass data back and forth.
5) I keep forgetting what I learned about PASM and again I've found out through various syntax errors that PASM is a hybrid 32 bit/ 9 bit language. So to help my flagging neurons fix this in memory, you can't use constants in pasm code. mov myvariable,myconstant will not work. Instead, near the bottom of the code, define a new value with myvalue long myconstant, then in the code, mov myvariable,myvalue
6) Passing values between Spin and PASM is not the same as between C and PASM. A typical example of some spin code is to do some maths on a long in a supporting PUB. Where is this long first defined? Not as a | in the PUB and not as a global variable in the Spin code. This long is defined usually at the bottom of the PASM code as 'myvariable long 0'. This is a special sort of long and the only way to find these is to do ^F on the name of the longs, scroll through the PASM code where they will usually be mentioned several times, then find that they are defined in PASM code. The proptool compiles this code and 'myvariable' ends up in a hub location that happens to be in the middle of some PASM code that is going to be loaded into a cog. When the PUB Start is run to start this cog, a value might be calculated for myvariable, the value gets stored in hub ram at the location that ends up part of cog code, and then when the cog is started, this value is ready for the cog to use.
We can't do this in C!
The precompiler can glue together constants easily but these 'PASM defined' variables are more complex to handle, especially with self contained cogjects that are going to be loaded in and out multiple times. We can cheat and turn some into constants and then define them in the PASM code as "myvariable long myconstant". But for others, I can't see any solution apart from calculating them in C, storing them to fixed hub locations that are defined in the global constants section using poke, then using a wrlong in PASM to read them back.
Or use the registry?
So - in general, when translating to C, look in the PUB start of the existing object. Any variables that seem to be passed with @ symbols will need known global locations defined in hub ram. Any variables that seem to end up in the PASM code as well - either try to convert these to constants, or if not, then also define a hub ram location for each, and use a rdlong in PASM.
I have yet to look at code in other supporting PUBs but I suspect there will be more of these 'longs that are defined in pasm code and used in Spin'. I guess all the variables will end up as a group which might make the code easier to read. It also would be a step towards reloadable spin objects as these are currently not possible for most objects due to the way code is glued together.
{{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// VGA64 6 Bits Per Pixel Engine
//
// Author: Kwabena W. Agyeman
// Updated: 11/17/2010
// Designed For: P8X32A
// Version: 1.0
//
// Copyright (c) 2010 Kwabena W. Agyeman
// See end of file for terms of use.
//
// Update History:
//
// v1.0 - Original release - 11/17/2009.
//
// For each included copy of this object only one spin interpreter should access it at a time.
//
// Nyamekye,
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Video Circuit:
//
// 0 1 2 3 Pin Group
//
// 240OHM
// Pin 0, 8, 16, 24 ----R-------- Vertical Sync
//
// 240OHM
// Pin 1, 9, 17, 25 ----R-------- Horizontal Sync
//
// 470OHM
// Pin 2, 10, 18, 26 ----R-------- Blue Video
// |
// 240OHM |
// Pin 3, 11, 19, 27 ----R-----
//
// 470OHM
// Pin 4, 12, 20, 28 ----R-------- Green Video
// |
// 240OHM |
// Pin 5, 13, 21, 29 ----R-----
//
// 470OHM
// Pin 6, 14, 22, 30 ----R-------- Red Video
// |
// 240OHM |
// Pin 7, 15, 23, 31 ----R-----
//
// 5V
// |
// --- 5V
//
// --- Vertical Sync Ground
// |
// GND
//
// --- Hoirzontal Sync Ground
// |
// GND
//
// --- Blue Return
// |
// GND
//
// --- Green Return
// |
// GND
//
// --- Red Return
// |
// GND
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}}
CON
#$FC, Light_Grey, #$A8, Grey, #$54, Dark_Grey
#$C0, Light_Red, #$80, Red, #$40, Dark_Red
#$30, Light_Green, #$20, Green, #$10, Dark_Green
#$0C, Light_Blue, #$08, Blue, #$04, Dark_Blue
#$F0, Light_Orange, #$A0, Orange, #$50, Dark_Orange
#$CC, Light_Purple, #$88, Purple, #$44, Dark_Purple
#$3C, Light_Teal, #$28, Teal, #$14, Dark_Teal
#$FF, White, #$00, Black
PUB plotBox(color, xPixelStart, yPixelStart, xPixelEnd, yPixelEnd) '' 8 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Plots a one color box of pixels on screen.
'' //
'' // Color - The color of the box of pixels to display on screen. A color byte (%RR_GG_BB_xx).
'' // XPixelStart - The X cartesian pixel start coordinate. X between 0 and 159. Y between 0 and 119.
'' // YPixelStart - The Y cartesian pixel start coordinate. Note that this axis is inverted like on all other graphics drivers.
'' // XPixelEnd - The X cartesian pixel end coordinate. X between 0 and 159. Y between 0 and 119.
'' // YPixelEnd - The Y cartesian pixel end coordinate. Note that this axis is inverted like on all other graphics drivers.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
xPixelEnd := ((xPixelEnd <# 159) #> 0)
yPixelEnd := (((yPixelEnd <# 119) #> 0) * 160)
xPixelStart := ((xPixelStart <# xPixelEnd) #> 0)
yPixelStart := (((yPixelStart * 160) <# yPixelEnd) #> 0)
yPixelEnd += xPixelStart
yPixelStart += xPixelStart
xPixelEnd -= --xPixelStart
repeat result from yPixelStart to yPixelEnd step 160
bytefill(@displayBuffer + result, (color | $3), xPixelEnd)
PUB plotPixel(color, xPixel, yPixel) '' 6 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Plots a one color pixel on screen.
'' //
'' // Color - The color of the pixel to display on screen. A color byte (%RR_GG_BB_xx).
'' // XPixel - The X cartesian pixel coordinate. X between 0 and 159. Y between 0 and 119.
'' // YPixel - The Y cartesian pixel coordinate. Note that this axis is inverted like on all other graphics drivers.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
displayBuffer.byte[((xPixel <# 159) #> 0) + (160 * ((yPixel <# 119) #> 0))] := (color | $3)
PUB displayClear '' 3 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Clears the screen to black.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
longfill(@displayBuffer, 0, constant((160 * 120) / 4))
PUB displayPointer '' 3 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Returns a pointer to the display buffer.
'' //
'' // The display buffer is an array of 160 by 120 bytes. Each byte represents a pixel on the screen.
'' //
'' // Each pixel is a color byte (%RR_GG_BB_xx). Where RR, GG, and BB are the two bit values of red, green, blue respectively.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return @displayBuffer
PUB displayState(state) '' 4 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Enables or disables the PIX Driver's video output - turning the monitor off or putting it into standby mode.
'' //
'' // State - True for active and false for inactive.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
displayIndicator := state
PUB displayRate(rate) '' 4 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Returns true or false depending on the time elasped according to a specified rate.
'' //
'' // Rate - A display rate to return at. 0=0.234375Hz, 1=0.46875Hz, 2=0.9375Hz, 3=1.875Hz, 4=3.75Hz, 5=7.5Hz, 6=15Hz, 7=30Hz.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
result or= (($80 >> ((rate <# 7) #> 0)) & syncIndicator)
PUB displayWait(frames) '' 4 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Waits for the display vertical refresh.
'' //
'' // The best time to draw on screen for flicker free operation is right after this function returns.
'' //
'' // Frames - Number of vertical refresh frames to wait for.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
repeat (frames #> 0)
result := syncIndicator
repeat until(result <> syncIndicator)
PUB displayColor(redAmount, greenAmount, blueAmount) '' 6 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Builds a color byte (%RR_GG_BB_xx) from red, green, and blue componets.
'' //
'' // RedAmount - The amount of red to add to the color byte. Between 0 and 3.
'' // GreenAmount - The amount of green to add to the color byte. Between 0 and 3.
'' // BlueAmount - The amount of blue to add to the color byte. Between 0 and 3.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return ((((redAmount <# 3) #> 0) << 6) | (((greenAmount <# 3) #> 0) << 4) | (((blueAmount <# 3) #> 0) << 2) | $3)
PUB PIXEngineStart(pinGroup) '' 7 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Starts up the PIX driver running on a cog.
'' //
'' // Returns true on success and false on failure.
'' //
'' // PinGroup - Pin group to use to drive the video circuit. Between 0 and 3.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PIXEngineStop
if(chipver == 1)
pinGroup := ((pinGroup <# 3) #> 0)
directionState := ($FF << (8 * pinGroup))
videoState := ($30_00_00_FF | (pinGroup << 9))
pinGroup := constant((25_175_000 + 1_600) / 4)
frequencyState := 1
repeat 32
pinGroup <<= 1
frequencyState <-= 1
if(pinGroup => clkfreq)
pinGroup -= clkfreq
frequencyState += 1
displayIndicatorAddress := @displayIndicator
syncIndicatorAddress := @syncIndicator
cogNumber := cognew(@initialization, @displayBuffer)
result or= ++cogNumber
PUB PIXEngineStop '' 3 Stack Longs
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'' // Shuts down the PIX driver running on a cog.
'' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(cogNumber)
cogstop(-1 + cogNumber~)
DAT
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' PIX Driver
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
org 0
' //////////////////////Initialization/////////////////////////////////////////////////////////////////////////////////////////
initialization mov vcfg, videoState ' Setup video hardware.
mov frqa, frequencyState '
movi ctra, #%0_00001_101 '
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Active Video
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
loop mov displayCounter, par ' Set/Reset tiles fill counter.
mov tilesCounter, #120 '
tilesDisplay mov tileCounter, #4 ' Set/Reset tile fill counter.
tileDisplay mov vscl, visibleScale ' Set/Reset the video scale.
mov counter, #40 '
' //////////////////////Visible Video//////////////////////////////////////////////////////////////////////////////////////////
videoLoop rdlong buffer, displayCounter ' Download new pixels.
add displayCounter, #4 '
or buffer, HVSyncColors ' Update display scanline.
waitvid buffer, #%%3210 '
djnz counter, #videoLoop ' Repeat.
' //////////////////////Invisible Video////////////////////////////////////////////////////////////////////////////////////////
mov vscl, invisibleScale ' Set/Reset the video scale.
waitvid HSyncColors, syncPixels ' Horizontal Sync.
' //////////////////////Repeat/////////////////////////////////////////////////////////////////////////////////////////////////
sub displayCounter, #160 ' Repeat.
djnz tileCounter, #tileDisplay '
add displayCounter, #160 ' Repeat.
djnz tilesCounter, #tilesDisplay '
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Inactive Video
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
add refreshCounter, #1 ' Update sync indicator.
wrbyte refreshCounter, syncIndicatorAddress '
' //////////////////////Front Porch////////////////////////////////////////////////////////////////////////////////////////////
mov counter, #11 ' Set loop counter.
frontPorch mov vscl, blankPixels ' Invisible lines.
waitvid HSyncColors, #0 '
mov vscl, invisibleScale ' Horizontal Sync.
waitvid HSyncColors, syncPixels '
djnz counter, #frontPorch ' Repeat # times.
' //////////////////////Vertical Sync//////////////////////////////////////////////////////////////////////////////////////////
mov counter, #(2 + 2) ' Set loop counter.
verticalSync mov vscl, blankPixels ' Invisible lines.
waitvid VSyncColors, #0 '
mov vscl, invisibleScale ' Vertical Sync.
waitvid VSyncColors, syncPixels '
djnz counter, #verticalSync ' Repeat # times.
' //////////////////////Back Porch/////////////////////////////////////////////////////////////////////////////////////////////
mov counter, #31 ' Set loop counter.
backPorch mov vscl, blankPixels ' Invisible lines.
waitvid HSyncColors, #0 '
mov vscl, invisibleScale ' Horizontal Sync.
waitvid HSyncColors, syncPixels '
djnz counter, #backPorch ' Repeat # times.
' //////////////////////Update Display Settings////////////////////////////////////////////////////////////////////////////////
rdbyte buffer, displayIndicatorAddress wz ' Update display settings.
muxnz dira, directionState '
' //////////////////////Loop///////////////////////////////////////////////////////////////////////////////////////////////////
jmp #loop ' Loop.
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Data
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
invisibleScale long (16 << 12) + 160 ' Scaling for inactive video.
visibleScale long (4 << 12) + 16 ' Scaling for active video.
blankPixels long 640 ' Blank scanline pixel length.
syncPixels long $00_00_3F_FC ' F-porch, h-sync, and b-porch.
HSyncColors long $01_03_01_03 ' Horizontal sync color mask.
VSyncColors long $00_02_00_02 ' Vertical sync color mask.
HVSyncColors long $03_03_03_03 ' Horizontal and vertical sync colors.
' //////////////////////Configuration Settings/////////////////////////////////////////////////////////////////////////////////
directionState long 0
videoState long 0
frequencyState long 0
' //////////////////////Addresses//////////////////////////////////////////////////////////////////////////////////////////////
displayIndicatorAddress long 0
syncIndicatorAddress long 0
' //////////////////////Run Time Variables/////////////////////////////////////////////////////////////////////////////////////
counter res 1
buffer res 1
tileCounter res 1
tilesCounter res 1
refreshCounter res 1
displayCounter res 1
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
fit 496
DAT
' //////////////////////Variable Arrary////////////////////////////////////////////////////////////////////////////////////////
displayBuffer long 0[(160 * 120) / 4] ' Display buffer.
displayIndicator byte 1 ' Video output control.
syncIndicator byte 0 ' Video update control.
cogNumber byte 0 ' Cog ID.
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
{{
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}}
Load up a catalina program, then load a video driver into a cog and change from a white on blue text screen to a graphics screen using Kye's 64 color 160x120 video driver.
Compilation and download is simply Shift F12.
If we can port one object into C, then other objects should be possible too. Next I need to see if I can load a text driver back in. Then a different SD card driver. Serial port drivers etc etc.
This is a new way of using the propeller. 512k of memory. And never run out of cogs - just keep recycling them. You could put the whole Obex on an SD card and load them in and out as needed.
I've attached the vb.net IDE. This is very messy code and some things like the auto indenting and color text are much slower than I would like. You can see some of the complexities of the precompiler where it goes through and removes old code, then creates new code and rebuilds the program prior to sending it off to spinc and catalina and homespun. The aim here is to make it easy for the coder - change one line of text and hit Shift F12 to test it.
/* PASM demo for use with Catalina IDE Precompiler and Compiler */
#include <stdio.h>
/* Global constants for both C and PASM */
const unsigned long int graphicstart = 0; // start of space for video graphics buffer
const unsigned long int displayindicator_const = 19200; // used by the graphics code change to 19200 when works
const unsigned long int syncindicator_const = 19201; // used by the graphics code
/* End global constants */
/* PASM Code for compilation using SpinC and inclusion as a data file
PASM Start vga160.spin
CON
directionstate_const = $00FF0000 ' for pin group 2
videostate_const = $30_00_04_FF ' for pin group 2
frequencystate_const = 337914606 ' calculated for 80Mhz
_clkfreq = 80_000_000 ' 5Mhz Crystal
_clkmode = xtal1 + pll16x ' x 16
' Start of C hub constants
graphicstart = 0
displayindicator_const = 19200
syncindicator_const = 19201
' End of C constants
PUB Main
coginit(1,@initialization,0) ' cog 1 dummy, cogstart, dummy value see Loop replaced par with #0
DAT
org 0
initialization mov vcfg, videoState ' Setup video hardware.
mov frqa, frequencyState '
movi ctra, #%0_00001_101 '
mov displayindicatoraddress, #1 ' startup value
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Active Video
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
loop mov displayCounter, #graphicstart ' buffer starts at hub ram zero must be < 9 bits value ie <512 so 0 is the logical choice
mov tilesCounter, #120 '
tilesDisplay mov tileCounter, #4 ' Set/Reset tile fill counter.
tileDisplay mov vscl, visibleScale ' Set/Reset the video scale.
mov counter, #40 '
' //////////////////////Visible Video//////////////////////////////////////////////////////////////////////////////////////////
videoLoop rdlong buffer, displayCounter ' Download new pixels.
add displayCounter, #4 '
or buffer, HVSyncColors ' Update display scanline.
waitvid buffer, #%%3210 '
djnz counter, #videoLoop ' Repeat.
' //////////////////////Invisible Video////////////////////////////////////////////////////////////////////////////////////////
mov vscl, invisibleScale ' Set/Reset the video scale.
waitvid HSyncColors, syncPixels ' Horizontal Sync.
' //////////////////////Repeat/////////////////////////////////////////////////////////////////////////////////////////////////
sub displayCounter, #160 ' Repeat.
djnz tileCounter, #tileDisplay '
add displayCounter, #160 ' Repeat.
djnz tilesCounter, #tilesDisplay '
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Inactive Video
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
add refreshCounter, #1 ' Update sync indicator.
wrbyte refreshCounter, syncIndicatorAddress '
' //////////////////////Front Porch////////////////////////////////////////////////////////////////////////////////////////////
mov counter, #11 ' Set loop counter.
frontPorch mov vscl, blankPixels ' Invisible lines.
waitvid HSyncColors, #0 '
mov vscl, invisibleScale ' Horizontal Sync.
waitvid HSyncColors, syncPixels '
djnz counter, #frontPorch ' Repeat # times.
' //////////////////////Vertical Sync//////////////////////////////////////////////////////////////////////////////////////////
mov counter, #(2 + 2) ' Set loop counter.
verticalSync mov vscl, blankPixels ' Invisible lines.
waitvid VSyncColors, #0 '
mov vscl, invisibleScale ' Vertical Sync.
waitvid VSyncColors, syncPixels '
djnz counter, #verticalSync ' Repeat # times.
' //////////////////////Back Porch/////////////////////////////////////////////////////////////////////////////////////////////
mov counter, #31 ' Set loop counter.
backPorch mov vscl, blankPixels ' Invisible lines.
waitvid HSyncColors, #0 '
mov vscl, invisibleScale ' Horizontal Sync.
waitvid HSyncColors, syncPixels '
djnz counter, #backPorch ' Repeat # times.
' //////////////////////Update Display Settings////////////////////////////////////////////////////////////////////////////////
rdbyte buffer, displayIndicatorAddress wz ' Update display settings.
muxnz dira, directionState '
' //////////////////////Loop///////////////////////////////////////////////////////////////////////////////////////////////////
jmp #loop ' Loop.
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Data
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
invisibleScale long (16 << 12) + 160 ' Scaling for inactive video.
visibleScale long (4 << 12) + 16 ' Scaling for active video.
blankPixels long 640 ' Blank scanline pixel length.
syncPixels long $00_00_3F_FC ' F-porch, h-sync, and b-porch.
HSyncColors long $01_03_01_03 ' Horizontal sync color mask.
VSyncColors long $00_02_00_02 ' Vertical sync color mask.
HVSyncColors long $03_03_03_03 ' Horizontal and vertical sync colors.
' //////////////////////Configuration Settings//////////////////////////////////////////
directionState long directionstate_const
videoState long videostate_const
frequencyState long frequencystate_const
' //////////////////////Addresses//////////////////////////////////////////////////////////////////////////////////////////////
displayIndicatorAddress long displayindicator_const
syncIndicatorAddress long syncindicator_const ' global constant
' //////////////////////Run Time Variables/////////////////////////////////////////////////////////////////////////////////////
counter res 1
buffer res 1
tileCounter res 1
tilesCounter res 1
refreshCounter res 1
displayCounter res 1
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
fit 496
PASM End
*/
void vga160() // unique name for each loader
{
/**
* @file vga160_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2010, John Doe
*/
unsigned long vga160_array[] =
{
0xa0bffc33, 0xa0bff434, 0x58fff00d, 0xa0fc6a01,
0xa0fc7800, 0xa0fc7478, 0xa0fc7204, 0xa0bffe2c,
0xa0fc6e28, 0x08bc703c, 0x80fc7804, 0x68bc7031,
0xfc7c70e4, 0xe4fc6e09, 0xa0bffe2b, 0xfc3c5e2e,
0x84fc78a0, 0xe4fc7207, 0x80fc78a0, 0xe4fc7406,
0x80fc7601, 0x003c7636, 0xa0fc6e0b, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e17,
0xa0fc6e04, 0xa0bffe2d, 0xfc7c6000, 0xa0bffe2b,
0xfc3c602e, 0xe4fc6e1d, 0xa0fc6e1f, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e23,
0x02bc7035, 0x7cbfec32, 0x5c7c0004, 0x000100a0,
0x00004010, 0x00000280, 0x00003ffc, 0x01030103,
0x00020002, 0x03030303, 0x00ff0000, 0x300004ff,
0x14242aee, 0x00004b00, 0x00004b01
};
unsigned long hub_array[512];
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
int i;
for (i = 0; i < 512; i++)
{
hub_array[i] = vga160_array[i];
}
_coginit((int)par_cogject>>2, (int)hub_array>>2, 7); // load into cog 7
}
void clearscreen() // white text on dark blue background
{
int i;
for (i=0;i<40;i++)
{
t_setpos(0,0,i); // move cursor to next line
t_color(0,0x08FC); // RRGGBBxx eg dark blue background 00001000 white text 11111100
}
}
void sleep(int milliseconds) // sleep function
{
_waitcnt(_cnt()+(milliseconds*(_clockfreq()/1000))-4296);
}
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void main ()
{
int i;
clearscreen();
printf("Clock speed %u \n",_clockfreq()); // see page 28 of the propeller manual for other useful commands
printf("load cogject into cog 7\n");
printf("Catalina running in cog number %i \n",_cogid()); // integer
sleep(3000); // for display to warm up
for(i=0;i<500;i++)
{
poke(i,0x53); // fill some bytes with gold 0x53
}
vga160(); // start graphic driver in cog 7
_cogstop(6); // shut down other cogs but not the one in _cogid above ie the C one
_cogstop(5);
_cogstop(4);
_cogstop(3);
_cogstop(0);
_cogstop(1);
// _cogstop(2); // this cog is running C
poke(0,0xff); // white square
while (1); // Prop reboots on exit from main()
}
I'm making a lot of progress with translating Spin objects into C. I was wondering if I could ask your sage advice on where things get stored?
/* PASM demo for use with Catalina IDE Precompiler and Compiler using XMM external memory, board http://smarthome.viviti.com/propeller
C code by Dr_Acula (James Moxham)
Catalina C compiler by RossH
Individual cog code authors acknowledged in the code below.
see post #493 http://forums.parallax.com/showthread.php?116370-Catalina-2.6-a-FREE-C-compiler-for-the-Propeller-The-Final-Frontier!/page25
for some discussion about hub vs xmm memory. I need to 1) make sure the stack is not being filled up with all this cog code and
2) need a way of passing parameters via par. (possibly via a common entry point in hub and a pointer passed to the load function)
and 3) check where the Tweety array is ending up being stored.
Code:
int myarray_1[5000]; <-- in the LARGE XMM model, this will be in XMM RAM (global, file scope)
int main() {
int myarray_2[5000]; <-- this will always be in Hub RAM (on the stack, local scope)
int *my_array_3 = malloc(5000); <-- in the LARGE XMM model, this will be in XMM RAM
}
Here is a reference you can look up on file scope. This is important in Catalina, since plugins can generally only access Hub RAM (i.e. they do not have access to XMM RAM) - this means that all variables used to communicate with plugins must be in Hub RAM. When using XMM, this can be done by making such variables "local" to the main function (and passing a pointer to them to anything outside that scope - when you see the new graphics plugin you'll see a working example of this
*/
#include <stdio.h>
/* Global constants for both C and PASM */
const unsigned long int graphicstart = 100; // start of space for video graphics buffer - problems if less than 100 but must be under 512
const unsigned long int displayindicator_const = 19998; // 19200 plus a buffer (tile drivers might use more)
const unsigned long int syncindicator_const = 19999; // used by the graphics code
/* End global constants */
/* PASM Code for compilation using SpinC and inclusion as a data file
VGA64 6 Bits Per Pixel Engine
Author: Kwabena W. Agyeman
Updated: 11/17/2010
Designed For: P8X32A
Version: 1.0
Copyright (c) 2010 Kwabena W. Agyeman
See end of file for terms of use.
Update History:
v1.0 - Original release - 11/17/2009.
PASM Start vga160.spin
CON
directionstate_const = $00FF0000 ' for pin group 2
videostate_const = $30_00_04_FF ' for pin group 2
frequencystate_const = 337914606 ' calculated for 80Mhz
_clkfreq = 80_000_000 ' 5Mhz Crystal
_clkmode = xtal1 + pll16x ' x 16
' Start of C hub constants
graphicstart = 100
displayindicator_const = 19998
syncindicator_const = 19999
' End of C constants
PUB Main
coginit(1,@initialization,0) ' cog 1 dummy, cogstart, dummy value see Loop replaced par with #0
DAT
org 0
initialization mov vcfg, videoState ' Setup video hardware.
mov frqa, frequencyState '
movi ctra, #%0_00001_101 '
mov displayindicatoraddress, #1 ' startup value
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Active Video
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
loop mov displayCounter, #graphicstart ' buffer starts at graphicstart must be < 9 bits value ie <512 so 0 is the logical choice
mov tilesCounter, #120 '
tilesDisplay mov tileCounter, #4 ' Set/Reset tile fill counter.
tileDisplay mov vscl, visibleScale ' Set/Reset the video scale.
mov counter, #40 '
' //////////////////////Visible Video//////////////////////////////////////////////////////////////////////////////////////////
videoLoop rdlong buffer, displayCounter ' Download new pixels.
add displayCounter, #4 '
or buffer, HVSyncColors ' Update display scanline.
waitvid buffer, #%%3210 '
djnz counter, #videoLoop ' Repeat.
' //////////////////////Invisible Video////////////////////////////////////////////////////////////////////////////////////////
mov vscl, invisibleScale ' Set/Reset the video scale.
waitvid HSyncColors, syncPixels ' Horizontal Sync.
' //////////////////////Repeat/////////////////////////////////////////////////////////////////////////////////////////////////
sub displayCounter, #160 ' Repeat.
djnz tileCounter, #tileDisplay '
add displayCounter, #160 ' Repeat.
djnz tilesCounter, #tilesDisplay '
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Inactive Video
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
add refreshCounter, #1 ' Update sync indicator.
wrbyte refreshCounter, syncIndicatorAddress '
' //////////////////////Front Porch////////////////////////////////////////////////////////////////////////////////////////////
mov counter, #11 ' Set loop counter.
frontPorch mov vscl, blankPixels ' Invisible lines.
waitvid HSyncColors, #0 '
mov vscl, invisibleScale ' Horizontal Sync.
waitvid HSyncColors, syncPixels '
djnz counter, #frontPorch ' Repeat # times.
' //////////////////////Vertical Sync//////////////////////////////////////////////////////////////////////////////////////////
mov counter, #(2 + 2) ' Set loop counter.
verticalSync mov vscl, blankPixels ' Invisible lines.
waitvid VSyncColors, #0 '
mov vscl, invisibleScale ' Vertical Sync.
waitvid VSyncColors, syncPixels '
djnz counter, #verticalSync ' Repeat # times.
' //////////////////////Back Porch/////////////////////////////////////////////////////////////////////////////////////////////
mov counter, #31 ' Set loop counter.
backPorch mov vscl, blankPixels ' Invisible lines.
waitvid HSyncColors, #0 '
mov vscl, invisibleScale ' Horizontal Sync.
waitvid HSyncColors, syncPixels '
djnz counter, #backPorch ' Repeat # times.
' //////////////////////Update Display Settings////////////////////////////////////////////////////////////////////////////////
rdbyte buffer, displayIndicatorAddress wz ' Update display settings.
muxnz dira, directionState '
' //////////////////////Loop///////////////////////////////////////////////////////////////////////////////////////////////////
jmp #loop ' Loop.
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
' Data
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
invisibleScale long (16 << 12) + 160 ' Scaling for inactive video.
visibleScale long (4 << 12) + 16 ' Scaling for active video.
blankPixels long 640 ' Blank scanline pixel length.
syncPixels long $00_00_3F_FC ' F-porch, h-sync, and b-porch.
HSyncColors long $01_03_01_03 ' Horizontal sync color mask.
VSyncColors long $00_02_00_02 ' Vertical sync color mask.
HVSyncColors long $03_03_03_03 ' Horizontal and vertical sync colors.
' //////////////////////Configuration Settings//////////////////////////////////////////
directionState long directionstate_const
videoState long videostate_const
frequencyState long frequencystate_const
' //////////////////////Addresses//////////////////////////////////////////////////////////////////////////////////////////////
displayIndicatorAddress long displayindicator_const
syncIndicatorAddress long syncindicator_const ' global constant
' //////////////////////Run Time Variables/////////////////////////////////////////////////////////////////////////////////////
counter res 1
buffer res 1
tileCounter res 1
tilesCounter res 1
refreshCounter res 1
displayCounter res 1
' /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
fit 496
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'// TERMS OF USE: MIT License
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
'// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
'// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
'// Software is furnished to do so, subject to the following conditions:
'//
'// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
'// Software.
'//
'// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
'// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
'// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
'// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PASM End
*/
void vga160(char cognumber) // function name copied from .spin name above CON section
{
/**
* @file vga160_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2011, John Doe
*/
unsigned long vga160_array[] =
{
0xa0bffc33, 0xa0bff434, 0x58fff00d, 0xa0fc6a01,
0xa0fc7864, 0xa0fc7478, 0xa0fc7204, 0xa0bffe2c,
0xa0fc6e28, 0x08bc703c, 0x80fc7804, 0x68bc7031,
0xfc7c70e4, 0xe4fc6e09, 0xa0bffe2b, 0xfc3c5e2e,
0x84fc78a0, 0xe4fc7207, 0x80fc78a0, 0xe4fc7406,
0x80fc7601, 0x003c7636, 0xa0fc6e0b, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e17,
0xa0fc6e04, 0xa0bffe2d, 0xfc7c6000, 0xa0bffe2b,
0xfc3c602e, 0xe4fc6e1d, 0xa0fc6e1f, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e23,
0x02bc7035, 0x7cbfec32, 0x5c7c0004, 0x000100a0,
0x00004010, 0x00000280, 0x00003ffc, 0x01030103,
0x00020002, 0x03030303, 0x00ff0000, 0x300004ff,
0x14242aee, 0x00004e1e, 0x00004e1f
};
unsigned long hub_array[512];
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
int i;
for (i = 0; i < 512; i++)
{
hub_array[i] = vga160_array[i];
}
_coginit((int)par_cogject>>2, (int)hub_array>>2, cognumber); // load into cog
}
void pset(int x, int y, char pixelcolor) // set pixel at x,y to color %RRGGBBxx x=0 to 159, y=0 to 119
{
int address = graphicstart + x + (y * 160); // calculate the address - no boundary checks as slows down code
*((char *)address) = pixelcolor; // poke to this location in hub memory
}
char rgb(char red, char green, char blue) // pass red etc 0-255, reduces to 0-3, combines to pixelcolor
{
char pixelcolor;
red = red >> 6; // divide by 64
green = green >> 6;
blue = blue >> 6;
pixelcolor = (red << 6) | (green << 4) | (blue << 2); // combine together using shifts then or bits
return pixelcolor;
}
void plotbox(int x1, int y1, int x2, int y2, char pixelcolor) // plot a box, or a line eg horizontal line y1=y2
{
int x;
int y;
for (x=x1; x<=x2 ;x++)
{
pset(x,y1,pixelcolor); // horizontal lines
pset(x,y2,pixelcolor);
}
for (y=y1+1; y < y2; y++)
{
pset(x1,y,pixelcolor); // vertical lines
pset(x2,y,pixelcolor);
}
}
void displayclear() // clears to black
{
int address;
char pixelcolor = 0; // black
for (address=graphicstart; address < graphicstart+19200; address++)
{
*((char *)address) = pixelcolor; // set to pixelcolor
}
}
// ---------------------------- end vga 160x120 driver methods ------------------------------------------------
// ---------------------------- begin 1280 vga driver ---------------------------------------------------------
/* PASM Code for compilation using SpinC and inclusion as a data file
'
' VGA 1280x1024 Tile Driver v0.9 by Chip Gracey Copyright (c) 2006 Parallax, Inc. 22 November 2006
'
'
' This object generates a 1280x1024 VGA display from a 80x64 array of 16x16-pixel 4-color tiles. It requires
' three cogs (or four with optional cursor enabled) and at least 80 MHz.
PASM Start vga1280.spin
CON
hp = 1280 'horizontal pixels 1280 x1024
vp = 1024 'vertical pixels
hf = 48 'horizontal front porch pixels
hs = 112 'horizontal sync pixels
hb = 248 'horizontal back porch pixels
vf = 1 'vertical front porch lines
vs = 3 'vertical sync lines
vb = 38 'vertical back porch lines
pr = 100 'pixel rate in MHz at 80MHz system clock (5MHz granularity)
ht = hp + hf + hs + hb 'total scan line pixels
xtiles = hp / 16 ' Tile array
ytiles = vp / 16
_clkfreq = 80_000_000 ' 5Mhz Crystal
_clkmode = xtal1 + pll16x ' x 16
' Start of C hub constants
graphicstart = 100
displayindicator_const = 19998
syncindicator_const = 19999
' End of C constants
PUB Main
coginit(1,@entry,0) ' cog 1, cogstart, dummy value
DAT
' ???????????????????????????????
' ? Initialization - all cogs ?
' ???????????????????????????????
org
' Move field loop into position
entry mov $1EF,$1EF - field + field_begin
sub entry,d0s0_ '(reverse move to avoid overwrite)
djnz regs,#entry
' Build line display code
mov par,#xtiles
:wv mov linecode+0,linecode_wv
add :wv,d1
add linecode_wv,#1
cmp par,#1 wz
:sc if_nz mov linecode+1,linecode_sc
if_nz add :sc,d1
if_nz add linecode_sc,d1
djnz par,#:wv
' Acquire settings
'dira_ ?» dira
mov regs,par 'dirb_ ?» dirb
:next movd :read,sprs 'vcfg_ ?» vcfg
or :read,d8_d4 'cnt_ ?» cnt
shr sprs,#4 'array_ptr_ ?» ctrb
:read rdlong dira,regs 'color_ptr_ ?» frqb
add regs,#4 'cursor_ptr_ ?» vscl
tjnz sprs,#:next 'sync_ptr_ ?» phsb
rdlong regs,regs 'mode_ ?» regs
test regs,#%100 wc 'if mode 1, set tile size to 16 x 32 pixels
if_c movs tile_bytes,#32 * 4
if_c shr array_bytes,#1
'adjust cog index
test regs,#%10 wc 'settings 0 1 2
test regs,#%01 wz '---------------------
if_nc add build,#1 'build +1 +1 +0
if_nc_and_z add vf_lines,#2
if_c_and_z add vf_lines,#4 'vf_lines +2 +0 +4
if_nc_and_z sub vb_lines,#4
if_nc_and_nz sub vb_lines,#2 'vb_lines -4 -2 -0
if_nc_and_nz movs start_line,#2 * 4
if_c_and_z movs start_line,#4 * 4 'start_line +0 +2 +4
if_c_or_z mov snop,#0 'sync N Y N
mov regs,vscl 'save cursor pointer
' Synchronize all cogs' video circuits so that waitvid's will be pixel-locked
movi frqa,#(pr / 5) << 1 'set pixel rate (VCO runs at 1x)
mov vscl,#1 'set video shifter to reload on every pixel
waitcnt cnt,d8_d4 'wait for sync count, add ~3ms - cogs locked!
movi ctra,#%00001_111 'enable PLLs now - NCOs locked!
waitcnt cnt,#0 'wait ~3ms for PLLs to stabilize - PLLs locked!
mov vscl,#100 'subsequent WAITVIDs will now be pixel-locked!
' Determine if this cog is to perform one of two field functions or the cursor function
if_nc_or_z jmp #vsync 'if cog index 0..2, jump to field function
'else, cursor function follows
' ???????????????????????????
' ? Cursor Loop - one cog ?
' ???????????????????????????
' Do vertical sync lines minus two
cursor mov par,#vf + vs + vb - 5
:loop mov vscl,vscl_line
:vsync waitvid ccolor,#0
djnz par,#:vsync
' Do two lines minus horizontal back porch pixels to buy a big block of time
mov vscl,vscl_two_lines_mhb
waitvid ccolor,#0
' Get cursor data
rdlong cx,regs 'get cursor x
add regs,#4
rdlong cy,regs 'get cursor y
add regs,#4
rdlong ccolor,regs 'get cursor color
add regs,#4
rdlong cshape,regs 'get cursor shape
sub regs,#3 * 4
and ccolor,#$FC 'trim and justify cursor color
shl ccolor,#8
' Build cursor pixels
mov par,#32 'ready for 32 cursor segments
movd :pix,#cpix
mov cnt,cshape
:pixloop cmp cnt,#1 wc, wz 'arrow, crosshair, or custom cursor?
if_a jmp #:custom
if_e jmp #:crosshair
cmp par,#32 wz 'arrow
cmp par,#32-21 wc
if_z mov cseg,h80000000
if_nz_and_nc sar cseg,#1
if_nz_and_c shl cseg,#2
mov coff,#0
jmp #:pix
:crosshair cmp par,#32-15 wz 'crosshair
if_ne mov cseg,h00010000
if_e neg cseg,#2
cmp par,#1 wz
if_e mov cseg,#0
mov coff,h00000F0F
jmp #:pix
:custom rdlong cseg,cshape 'custom
add cshape,#4
rdlong coff,cshape
:pix mov cpix,cseg 'save segment into pixels
add :pix,d0
djnz par,#:pixloop 'another segment?
' Compute cursor position
mov cseg,coff 'apply cursor center-pixel offsets
and cseg,#$FF
sub cx,cseg
shr coff,#8
and coff,#$FF
add cy,coff
cmps cx,neg31 wc 'if x out of range, hide cursor via y
if_nc cmps pixels_m1,cx wc
if_c neg cy,#1
mov cshr,#0 'adjust for left-edge clipping
cmps cx,#0 wc
if_c neg cshr,cx
if_c mov cx,#0
mov cshl,#0 'adjust for right-edge clipping
cmpsub cx,pixels_m32 wc
if_c mov cshl,cx
if_c mov cx,pixels_m32
add cx,#hb 'bias x and y for display
sub cy,lines_m1
' Do visible lines with cursor
mov par,lines 'ready for visible scan lines
:line andn cy,#$1F wz, nr 'check if scan line in cursor range
if_z movs :seg,cy 'if in range, get cursor pixels
if_z add :seg,#cpix
if_nz mov cseg,#0 'if out of range, use blank pixels
:seg if_z mov cseg,cpix
if_z rev cseg,#0 'reverse pixels so they map sensibly
if_z shr cseg,cshr 'perform any edge clipping on pixels
if_z shl cseg,cshl
mov vscl,cx 'do left blank pixels (hb+cx)
waitvid ccolor,#0
mov vscl,vscl_cursor 'do cursor pixels (32)
waitvid ccolor,cseg
mov vscl,vscl_line_m32 'do right blank pixels (hp+hf+hs-32-cx)
sub vscl,cx
waitvid ccolor,#0
add cy,#1 'another scan line?
djnz par,#:line
' Do horizontal back porch pixels and loop
mov vscl,#hb
waitvid ccolor,#0
mov par,#vf + vs + vb - 2 'ready to do vertical sync lines
jmp #:loop
' Cursor data
vscl_line long ht 'total pixels per scan line
vscl_two_lines_mhb long ht * 2 - hb 'total pixels per two scan lines minus hb
vscl_line_m32 long ht - 32 'total pixels per scan line minus 32
vscl_cursor long 1 << 12 + 32 '32 pixels per cursor with 1 clock per pixel
lines long vp 'visible scan lines
lines_m1 long vp - 1 'visible scan lines minus 1
pixels_m1 long hp - 1 'visible pixels minus 1
pixels_m32 long hp - 32 'visible pixels minus 32
neg31 long -31
h80000000 long $80000000 'arrow/crosshair cursor data
h00010000 long $00010000
h00000F0F long $00000F0F
' Initialization data
d0s0_ long 1 << 9 + 1 'd and s field increments
regs long $1F0 - field 'number of registers in field loop space
sprs long $DFB91E76 'phsb/vscl/frqb/ctrb/cnt/vcfg/dirb/dira nibbles
bit15 long $8000 'bit15 mask used to differentiate cogs in par
d8_d4 long $0003E000 'bit8..bit4 mask for d field
field_begin 'field code begins at this offset
' Undefined cursor data
cx res 1
cy res 1
ccolor res 1
cshape res 1
coff res 1
cseg res 1
cshr res 1
cshl res 1
cpix res 32
' ?????????????????????????????
' ? Field Loop - three cogs ?
' ?????????????????????????????
org
' Allocate buffers
palettes res 64 'palettes of colors
pixels0 res xtiles 'pixels for tile row line +0
pixels1 res xtiles 'pixels for tile row line +1
linecode res xtiles * 2 - 1 'line display code
field mov vscl,#hf
waitvid hv_sync,#0
linecode_sc sumc linecode,#xtiles '(winds up as 'sumc field-1,#xtiles')
mov ina,#1
jmp #hsync
linecode_wv waitvid palettes,pixels0
' Each cog alternately builds and displays two scan lines
build mov cnt,#vp / 6 'ready number of two-scan-line builds/displays
start_line mov tile_line,#0 'ready starting tile line (adjusted)
' Build two scan lines during four scan lines
build_2y movd col,#linecode 'reset pointers for scan line buffers
movd pix0,#pixels0
movd pix1,#pixels1
mov ina,#2 'two scan lines require two waitvid's
build_40x mov vscl,vscl_two_lines 'output lows for two scan lines so other cog
:zero waitvid :zero,#0 '..can display while this cog builds (twice)
mov inb,#xtiles / 2 'build four scan lines for half a row
build_1x rdword vscl,ctrb 'get word from the tile array
add ctrb,#2
ror vscl,#10 'get color bits
col movd linecode,vscl
add col,d1
shr vscl,#16 'get tile line address
add vscl,tile_line
pix0 rdlong pixels0,vscl 'get line +0 tile pixels
add pix0,d0
add vscl,#4
pix1 rdlong pixels1,vscl 'get line +1 tile pixels
add pix1,d0
djnz inb,#build_1x 'loop for next tile (16 inst/loop)
djnz ina,#build_40x 'if first half done, loop for 2nd waitvid
sub ctrb,#xtiles * 2 'back up to start of same row
' Display two scan lines
mov inb,#2 'ready for two scan lines
movs hsync_ret,#linecode_ret
display test inb,#1 wc
mov vscl,vscl_tile 'set pixel rate for tiles
jmp #linecode
linecode_ret djnz inb,#display 'another scan line?
' Another two scan lines?
add tile_line,#6 * 4 'advance six scan lines within tile row
tile_bytes cmpsub tile_line,#16 * 4 wc 'tile row done? (# doubled for mode 1)
if_c add ctrb,#xtiles * 2 'if done, advance array pointer to next row
djnz cnt,#build_2y 'another two scan lines?
sub ctrb,array_bytes 'display done, reset array pointer to top row
' Visible section done, handle sync indicator
cmp cnt,phsb wz 'sync enabled? (cnt=0)
snop if_nz wrlong neg1,phsb 'if so, write -1 to sync indicator (change to nop unless #3)
' Do vertical sync lines and loop
vf_lines mov ina,#vf 'do vertical front porch lines (adjusted +4|+2|+0)
call #blank
vsync mov ina,#vs 'do vertical sync lines
call #blank_vsync
vb_lines mov ina,#vb 'do vertical back porch lines (adjusted -4|-2|-0)
movs blank_vsync_ret,#build '(loop to build, blank_vsync follows)
' Subroutine - do blank lines
blank_vsync xor hv_sync,#$0101 'flip vertical sync bits
blank mov vscl,vscl_blank 'do horizontal blank pixels
waitvid hv_sync,#0
mov vscl,#hf 'do horizontal front porch pixels
waitvid hv_sync,#0
hsync mov vscl,#hs 'do horizontal sync pixels
waitvid hv_sync,#1
rdlong vscl,frqb 'update another palette
and vscl,color_mask
:palette mov palettes,vscl
add :palette,d0
add frqb,#4
add par,count_64 wc
if_c movd :palette,#palettes
if_c sub frqb,#64 * 4
mov vscl,#hb 'do horizontal back porch pixels
waitvid hv_sync,#0
djnz ina,#blank 'another blank line?
hsync_ret
blank_ret
blank_vsync_ret ret
' Field data
d0s0 long 1 << 9 + 1 'd and s field increments
d0 long 1 << 9 'd field increment
d1 long 1 << 10 'd field double increment
array_bytes long xtiles * ytiles * 2 'number of bytes in tile array
vscl_two_lines long ht * 2 'total pixels per two scan lines
vscl_tile long 1 << 12 + 16 '16 pixels per tile with 1 clock per pixel
vscl_blank long hp 'visible pixels per scan line
hv_sync long $0200 '+/-H,-V states
count_64 long $04000000 'addend that sets carry every 64th addition
color_mask long $FCFCFCFC 'mask to isolate R,G,B bits from H,V
neg1 long $FFFFFFFF 'negative 1 to be written to sync indicator
' Undefined field data
tile_line res 1
fit 496
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'// TERMS OF USE: MIT License
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
'// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
'// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
'// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
'// Software is furnished to do so, subject to the following conditions:
'//
'// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
'// Software.
'//
'// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
'// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
'// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
'// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PASM End
*/
void vga1280(char cognumber) // function name copied from .spin name above CON section
{
/**
* @file vga1280_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2011, John Doe
*/
unsigned long vga1280_array[] =
{
0xa0bfdefd, 0x84bc0088, 0xe4fd1200, 0xa0ffe050,
0xa0bdc184, 0x80bc09c5, 0x80ff0801, 0x867fe001,
0xa095c381, 0x809411c5, 0x809703c5, 0xe4ffe004,
0xa0bd13f0, 0x54bc208a, 0x68bc208c, 0x28fd1404,
0x08bfec89, 0x80fd1204, 0xe87d140d, 0x08bd1289,
0x617d1204, 0x50f34880, 0x28f38c01, 0x617d1202,
0x627d1201, 0x80cf0a01, 0x80cb5402, 0x80e35404,
0x84cb5c04, 0x84c75c02, 0x50c70c08, 0x50e30c10,
0xa0fb5200, 0xa0bd13ff, 0x58fff428, 0xa0fffe01,
0xf8bfe28c, 0x58fff00f, 0xf8ffe200, 0xa0fffe64,
0x5c6c01ac, 0xa0ffe025, 0xa0bffe7c, 0xfc7d1e00,
0xe4ffe02b, 0xa0bffe7d, 0xfc7d1e00, 0x08bd1a89,
0x80fd1204, 0x08bd1c89, 0x80fd1204, 0x08bd1e89,
0x80fd1204, 0x08bd2089, 0x84fd120c, 0x60fd1efc,
0x2cfd1e08, 0xa0ffe020, 0x54fca095, 0xa0bfe290,
0x877fe201, 0x5c44004d, 0x5c680046, 0x867fe020,
0x857fe00b, 0xa0a92485, 0x38c52401, 0x2cd12402,
0xa0fd2200, 0x5c7c0050, 0x867fe011, 0xa0952486,
0xa4e92402, 0x867fe001, 0xa0e92400, 0xa0bd2287,
0x5c7c0050, 0x08bd2490, 0x80fd2004, 0x08bd2290,
0xa0bd2a92, 0x80bca1c4, 0xe4ffe03c, 0xa0bd2491,
0x60fd24ff, 0x84bd1a92, 0x28fd2208, 0x60fd22ff,
0x80bd1c91, 0xc13d1a84, 0xc10d048d, 0xa4f11c01,
0xa0fd2600, 0xc17d1a00, 0xa4b1268d, 0xa0f11a00,
0xa0fd2800, 0xe1bd1a83, 0xa0b1288d, 0xa0b11a83,
0x80fd1af8, 0x84bd1c81, 0xa0bfe080, 0x667d1c1f,
0x50a8d68e, 0x80e8d695, 0xa0d52400, 0xa0a92495,
0x3ce92400, 0x28a92493, 0x2ca92494, 0xa0bffe8d,
0xfc7d1e00, 0xa0bffe7f, 0xfc3d1e92, 0xa0bffe7e,
0x84bffe8d, 0xfc7d1e00, 0x80fd1c01, 0xe4ffe067,
0xa0fffef8, 0xfc7d1e00, 0xa0ffe028, 0x5c7c002a,
0x00000698, 0x00000c38, 0x00000678, 0x00001020,
0x00000400, 0x000003ff, 0x000004ff, 0x000004e0,
0xffffffe1, 0x80000000, 0x00010000, 0x00000f0f,
0x00000201, 0x00000071, 0xdfb91e76, 0x00008000,
0x0003e000, 0xa0fffe30, 0xfc7f9400, 0x90fdc050,
0xa0ffe401, 0x5c7c01b5, 0xfc3c0040, 0xa0ffe2aa,
0xa0ff9c00, 0x54ff22e0, 0x54ff2a40, 0x54ff3090,
0xa0ffe402, 0xa0bfffc7, 0xfc7f1800, 0xa0ffe628,
0x04bffff9, 0x80fff202, 0x20fffe0a, 0x54bdc1ff,
0x80bf23c5, 0x28fffe10, 0x80bfffce, 0x08bc81ff,
0x80bf2bc4, 0x80fffe04, 0x08bd21ff, 0x80bf31c4,
0xe4ffe78e, 0xe4ffe58b, 0x84fff2a0, 0xa0ffe602,
0x50ff85a2, 0x617fe601, 0xa0bfffc8, 0x5c7c00e0,
0xe4ffe79f, 0x80ff9c18, 0xe1ff9c40, 0x80f3f2a0,
0xe4ffe387, 0x84bff3c6, 0x863fe3fd, 0x08179bfd,
0xa0ffe401, 0x5cff85b1, 0xa0ffe403, 0x5cff85b0,
0xa0ffe426, 0x50ff8585, 0x6cff9501, 0xa0bfffc9,
0xfc7f9400, 0xa0fffe30, 0xfc7f9400, 0xa0fffe70,
0xfc7f9401, 0x08bffffb, 0x60bfffcc, 0xa0bc01ff,
0x80bf73c4, 0x80fff604, 0x81bfe1cb, 0x54f37200,
0x84f3f700, 0xa0fffef8, 0xfc7f9400, 0xe4ffe5b1,
0x5c7c0000, 0x00000201, 0x00000200, 0x00000400,
0x00002800, 0x00000d30, 0x00001010, 0x00000500,
0x00000200, 0x04000000, 0xfcfcfcfc, 0xffffffff
};
unsigned long hub_array[512];
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
int i;
for (i = 0; i < 512; i++)
{
hub_array[i] = vga1280_array[i];
}
_coginit((int)par_cogject>>2, (int)hub_array>>2, cognumber); // load into cog
}
// ---------------------------- end 1280 vga driver ---------------------------------------------------------
void displayicon(int x,int y) // 32x32 bit icon created from the Visual IDE tab and copied here. Display at x,y
{
int r;
int c;
int i;
// File c:\program files\catalina\demos\tweety32.h
char tweety[] =
{
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, 0xf8, 0xac, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf4, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf4, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf4, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xa0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xe4, 0xa8, 0xa4, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xa8, 0xfc, 0xb8, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf4, 0xf0, 0xf0, 0xf0, 0xf0, 0x68, 0xac, 0xb8, 0xf0, 0xf0, 0xf0, 0xa4, 0xa4, 0xa4, 0xf4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0x18, 0x68, 0xa8, 0xf0, 0xf0, 0xa8, 0xfc, 0xa8, 0xe0, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf4, 0xf0, 0xf0, 0xf0, 0x18, 0x68, 0xf4, 0xf0, 0xa4, 0x6c, 0xbc, 0xe4, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xf0, 0xa4, 0xa8, 0xf0, 0xf0, 0x14, 0x68, 0xe4, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0x58, 0xe4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xf0, 0xe0, 0xe4, 0xe0, 0xf0, 0xf4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf4, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf0, 0xf4, 0xf8, 0xf8, 0xf8, 0xf4, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xfc, 0xfc, 0xf4, 0xf0, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xe4, 0xf0, 0xf0, 0xf4, 0xfc, 0xf4, 0xf0, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xe0, 0xa0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf4, 0xf0, 0xf8, 0xf0, 0xf0, 0xf0, 0xf4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xe4, 0xe4, 0xf0, 0xf4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xe4, 0xe4, 0xe4, 0xf8, 0xfc, 0xfc, 0xfc, 0xe4, 0xe8, 0xe4, 0xfc, 0xfc, 0xf8, 0xe8, 0xe4, 0xe4, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xe8, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xfc, 0xe8, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xe8, 0xe4, 0xe4, 0xe4, 0xe8, 0xf8, 0xfc, 0xfc, 0xfc, 0xfc, 0xe8, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
};
i=0; // counter
for(r=0; r<32; r++) // count rows
{
for(c=0; c<32; c++) // column counter
{
pset(x+c,r+y,tweety[i]);
i++; // increment counter
}
}
}
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void 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);
}
void stopallcogs() // stop all cogs except the one running C
{
int i;
int c; // cog running C
c=_cogid();
for(i=0;i<8;i++)
{
if(i != c)
{
_cogstop(i); // stop this cog if not the one running C
}
}
}
void main ()
{
int i;
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
sleep(4000); // for CRT display to warm up
stopallcogs(); // stop all cogs except C one
vga160(7); // start 160x120 graphic driver in cog 7
displayclear(); // clear the display
plotbox(0,0,159,119,rgb(255,0,0)); // draw a box in red round the edge of the screen
// pset(0,0,0xff); // top left corner to white
// pset(0,119,rgb(0,255,0)); // set pixel to red green blue value bottom left corner
// pset(159,0,rgb(0,0,255)); // top right corner
// pset(159,119,rgb(255,255,0)); // bottom right corner
displayicon(50,50); // display 32x32 picture
plotbox(49,49,82,82,rgb(0,255,0)); // draw a box around the picture
while (1); // Prop reboots on exit from main()
}
This is the working Kye driver and the beginnings of the 1280 vga text driver. I have been rereading post #493 about where things get stored, and in general I want to keep as much as possible in XMM.
So regarding this code:
1) Near the bottom is a large array called "tweety" which prints tweety bird on the screen. I have a feeling this is being stored in hub on the stack. If this is correct, what do you think is better - define this as a global array so it ends up in XMM, or define it locally with the malloc example?
2) I am confused about where the program data is being stored
void vga160(char cognumber) // function name copied from .spin name above CON section
{
/**
* @file vga160_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2011, John Doe
*/
unsigned long vga160_array[] =
{
0xa0bffc33, 0xa0bff434, 0x58fff00d, 0xa0fc6a01,
0xa0fc7864, 0xa0fc7478, 0xa0fc7204, 0xa0bffe2c,
0xa0fc6e28, 0x08bc703c, 0x80fc7804, 0x68bc7031,
0xfc7c70e4, 0xe4fc6e09, 0xa0bffe2b, 0xfc3c5e2e,
0x84fc78a0, 0xe4fc7207, 0x80fc78a0, 0xe4fc7406,
0x80fc7601, 0x003c7636, 0xa0fc6e0b, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e17,
0xa0fc6e04, 0xa0bffe2d, 0xfc7c6000, 0xa0bffe2b,
0xfc3c602e, 0xe4fc6e1d, 0xa0fc6e1f, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e23,
0x02bc7035, 0x7cbfec32, 0x5c7c0004, 0x000100a0,
0x00004010, 0x00000280, 0x00003ffc, 0x01030103,
0x00020002, 0x03030303, 0x00ff0000, 0x300004ff,
0x14242aee, 0x00004e1e, 0x00004e1f
};
unsigned long hub_array[512];
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
int i;
for (i = 0; i < 512; i++)
{
hub_array[i] = vga160_array[i];
}
_coginit((int)par_cogject>>2, (int)hub_array>>2, cognumber); // load into cog
}
I think the point of moving it from the locally defined array to the hubarray was to move it from xmm to hub. But on reading that previous post, I am wondering if both are in hub? Do I again need to use malloc, or put the array outside a function?
3) I'd like to pass some parameters to the cog.
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
passes some fixed values. In keeping with the C concept of keeping everything modular, would a better way be to create an array in XMM with some variable data that we might calculate (copied from spin), then pass a pointer to this loading function, which then copies the array to a hub array, and then pass the start of that location to _coginit.
There are a number of Spin objects where one creates a contiguous list of longs, puts variables in that list (I've been using constants but sooner or later I have to move to variables), then pass a pointer to the start of that list via 'par'.
Your advice would be most appreciated.
Addit: I went out and mowed the lawns and had a bit more of a think. What I think happens in XMM is you have a declaration of an array with some code. The data for that resides in XMM. The program gets to the function and finds there is an array and there is some data for that array. So it declares the array and moves the data onto the stack. It is now in hub ram but in the stack part of hub ram. Then I think the data gets moved to another array in hub ram - I'll have to think about that a little more...
Addit Addit:
Thinking through the above further, I replaced this code:
void vga160() // unique name for each loader
{
/**
* @file vga160_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2010, John Doe
*/
unsigned long vga160_array[] =
{
0xa0bffc33, 0xa0bff434, 0x58fff00d, 0xa0fc6a01,
0xa0fc7800, 0xa0fc7478, 0xa0fc7204, 0xa0bffe2c,
0xa0fc6e28, 0x08bc703c, 0x80fc7804, 0x68bc7031,
0xfc7c70e4, 0xe4fc6e09, 0xa0bffe2b, 0xfc3c5e2e,
0x84fc78a0, 0xe4fc7207, 0x80fc78a0, 0xe4fc7406,
0x80fc7601, 0x003c7636, 0xa0fc6e0b, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e17,
0xa0fc6e04, 0xa0bffe2d, 0xfc7c6000, 0xa0bffe2b,
0xfc3c602e, 0xe4fc6e1d, 0xa0fc6e1f, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e23,
0x02bc7035, 0x7cbfec32, 0x5c7c0004, 0x000100a0,
0x00004010, 0x00000280, 0x00003ffc, 0x01030103,
0x00020002, 0x03030303, 0x00ff0000, 0x300004ff,
0x14242aee, 0x00004b00, 0x00004b01
};
unsigned long hub_array[512];
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
int i;
for (i = 0; i < 512; i++)
{
hub_array[i] = vga160_array[i];
}
_coginit((int)par_cogject>>2, (int)hub_array>>2, 7); // load into cog 7
}
with this code
void vga160(char cognumber) // function name copied from .spin name above CON section
{
/**
* @file vga160_array.h
* Created with spin.binary PASM to C Array Converter.
* Copyright (c) 2011, John Doe
*/
unsigned long vga160_array[] =
{
0xa0bffc33, 0xa0bff434, 0x58fff00d, 0xa0fc6a01,
0xa0fc7864, 0xa0fc7478, 0xa0fc7204, 0xa0bffe2c,
0xa0fc6e28, 0x08bc703c, 0x80fc7804, 0x68bc7031,
0xfc7c70e4, 0xe4fc6e09, 0xa0bffe2b, 0xfc3c5e2e,
0x84fc78a0, 0xe4fc7207, 0x80fc78a0, 0xe4fc7406,
0x80fc7601, 0x003c7636, 0xa0fc6e0b, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e17,
0xa0fc6e04, 0xa0bffe2d, 0xfc7c6000, 0xa0bffe2b,
0xfc3c602e, 0xe4fc6e1d, 0xa0fc6e1f, 0xa0bffe2d,
0xfc7c5e00, 0xa0bffe2b, 0xfc3c5e2e, 0xe4fc6e23,
0x02bc7035, 0x7cbfec32, 0x5c7c0004, 0x000100a0,
0x00004010, 0x00000280, 0x00003ffc, 0x01030103,
0x00020002, 0x03030303, 0x00ff0000, 0x300004ff,
0x14242aee, 0x00004e1e, 0x00004e1f
};
unsigned long par_cogject[] = { 1, 2, 3 }; // data to pass to cog - ignore if not used
_coginit((int)par_cogject>>2, (int)vga160_array>>2, cognumber); // load into cog
}
and it still seems to work. I presume that the act of declaring an array in a local function moves the array from data within XMM to the stack, and if we start _coginit pointing to that array in the stack it will still load?
Working on translating Obex code into C. I've been building a structure such that the C code ends up similar to the Spin code. That involves passing parameters and having common memory locations. Lots of fun and it is great as more things come together.
I gather that the propeller stores longs in 'little endian' format. I wrote a 'pokelong' routine to do this one byte at a time. I tried doing this with one line but it didn't work. So this is the warts and all code and it has been tested. Any C experts see a way to simplify the pokelong routine?
char peek(int address) // function implementation of peek
{
return *((char *)address);
}
void poke(int address, char value) // function implementation of poke
{
*((char *)address) = value;
}
void pokelong(int address, unsigned long value) // function poke a long value to hub ram little endian first
{
char c;
c = value & 0x000000FF; // mask
poke(address,c);
value = value >> 8; // bit shift
c = value & 0x000000FF; // mask
poke(address+1,c);
value = value >> 8; // bit shift
c = value & 0x000000FF; // mask
poke(address+2,c);
value = value >> 8; // bit shift
c = value & 0x000000FF; // mask
poke(address+3,c);
}
Addit:
This seems to work
void pokelong(int address, unsigned long value) // poke long to hub ram
{
*((unsigned long *)address) = value;
}
I think this can become the building block for passing parameters.
Typical Spin code:
* Define a list of global variables in a Var section
* Fill them up in a Start routine
* Start a cog and pass the 'par' as the first variable in that Var section
This then passes as many variables in that Var list as the cog needs to use.
Translating this to C:
* Define a global constant at a known hub location (cogs may want exclusive use of this location)
* Define a list of unsigned longs in a Start function
* Poke them individually to the hub ram at the global constant location, as many as required
* Point variable 'parameters' to the beginning of the list
* Call the function to load the cog. Pass two variables - cog number and parameter eg void vga1280(char cognumber, unsigned long parameters)
* cog loader contains compiled pasm code.
* point 'par_cogject' array to paramters
* load the cog
unsigned long par_cogject[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // data to pass to cog - ignore if not used
_coginit((int)par_cogject>>2, (int)vga1280_array>>2, cognumber); // load into cog
For cog code that just needs a list of values passed but no reserved hub ram, there are the plugins.
For situations where the cog also needs fixed hub locations and needs exclusive use of those (eg video drivers needing video buffer space), use the above code.
One slightly complicated task is working out which of these two is best for a typical piece of obex code. Need to go through all the code looking for other references for passed variables to see if they are exclusively using fixed hub locations.
I'm working on translating this to C:
VAR
long cog[4]
long dira_ '9 contiguous longs
long dirb_
long vcfg_
long cnt_
long array_ptr_
long color_ptr_
long cursor_ptr_
long sync_ptr_
long mode_
PUB start(base_pin, array_ptr, color_ptr, cursor_ptr, sync_ptr, mode) : okay | i, j
'' Start driver - starts two or three cogs
'' returns false if cogs not available
''
'' base_pin = First of eight VGA pins, must be a multiple of eight (0, 8, 16, 24, etc):
''
'' 240Ω 240Ω 240Ω 240Ω
'' +7 ───┳─ Red +5 ───┳─ Green +3 ───┳─ Blue +1 ── H
'' 470Ω │ 470Ω │ 470Ω │ 240Ω
'' +6 ───┘ +4 ───┘ +2 ───┘ +0 ── V
''
'' array_ptr = Pointer to 5,120 long-aligned words, organized as 80 across by 64 down,
'' which will serve as the tile array. Each word specifies a tile bitmap and
'' a color palette for its tile area. The bottom 10 bits of each word hold
'' the base address of a 16-long tile bitmap, while the top 6 bits select a
'' color palette for the bitmap. For example, $B2E5 would specify the tile
'' bitmap spanning $B940..$B97F ($2E5<<6) and color palette $2C ($B2E5>>10).
''
'' color_ptr = Pointer to 64 longs which will define the 64 color palettes. The RGB data
'' in each long is arranged as %%RGBx_RGBx_RGBx_RGBx with the sub-bytes 3..0
'' providing the color data for pixel values %11..%00, respectively:
''
'' %%3330_0110_0020_3300: %11=white, %10=dark cyan, %01=blue, %00=gold
''
'' cursor_ptr = Pointer to 4 longs which will control the cursor, or 0 to disable the
'' cursor. If a pointer is given, an extra cog will be started to generate
'' the cursor overlay. Here are the 4 longs that control the cursor:
''
'' cursor_x - X position of cursor: ..0..1279.. (left to right)
'' cursor_y - Y position of cursor: ..0..1023.. (bottom to top)
''
'' cursor_color - Cursor color to be OR'd to background color as %%RGBx:
'' %%3330=white, %%2220 or %%1110=translucent, %%0000=off
''
'' cursor_shape - 0 for arrow, 1 for crosshair, or pointer to a cursor
'' definition. A cursor definition consists of 32 longs
'' containing a 32x32 pixel cursor image, followed by two
'' bytes which define the X and Y center-pixel offsets
'' within the image.
''
'' sync_ptr = Pointer to a long which will be set to -1 after each refresh, or 0 to
'' disable this function. This is useful in advanced applications where
'' awareness of display timing is important.
''
'' mode = 0 for normal 16x16 pixel tiles or 1 for taller 16x32 pixel tiles. Mode 1
'' is useful for displaying the internal font while requiring half the array
'' memory; however, the 3-D bevel characters will not be usable because of
'' the larger vertical tile granularity of this mode.
'If driver is already running, stop it
stop
'Ready i/o settings
i := $FF << (base_pin & %011000)
j := base_pin & %100000 == 0
dira_ := i & j
dirb_ := i & !j
vcfg_ := $300000FF + (base_pin & %111000) << 6
'Ready cnt value to sync cogs by
cnt_ := cnt + $100000
'Ready pointers and mode
longmove(@array_ptr_, @array_ptr, 5)
'Launch cogs, abort if error
j := mode_
repeat i from 0 to 3
mode_ := j << 2 + i
if i == 3 'cursor cog?
ifnot cursor_ptr 'cursor enabled?
quit 'if not, quit loop
vcfg_ ^= $10000000 'else, set two-color mode
ifnot cog[i] := cognew(@entry, @dira_) + 1
stop
return {false}
waitcnt($8000 + cnt) 'allow cog to launch before modifying variables
'Successful
return true
Regarding your questions in post #568: Mostly you have answered them yourself - but just to confirm:
1. Yes, if your program is compiled in XMM mode, then the array "tweety" is indeed being created on the stack. But this array has an initializer (i.e. the values 0xfc etc) which will be in XMM RAM anyway, and get copied to the array on the stack at run time - so you may as well make the array global (i.e. give it file scope) and put the whole array in XMM RAM - this actually saves both time and space.
2. No, you don't need to do the copy here - as I mentioned in the answer to 1 (above) - if a local array has an initializer, that initializer will be in XMM RAM, but the compiler will copy it to the stack (i.e. hub RAM) for you at run time.
3. When calling another language from C, one way to make sure the variables you want to pass are physically located where you want them is to declare a structure in C with the appropriate memory layout, fill in the values at run time, and pass a pointer to the structure. For example:
// declare a structre to ensure our 3 values are layed out correctly
typedef struct {
int val1;
int val2;
int val3;
} val_type;
// this function gets values out of the structure given a pointer to it
void func (val_type *v) {
int v1;
int v2;
int v3;
v1 = v->val1;
v2 = v->val2;
v3 = v->val3;
}
void main() {
// declare an instance of the structure
val_type v;
// fill it in
v.val1 = 1;
v.val2 = 2;
v.val3 = 3;
// pass the address to a function (which may be written in another language!)
func(&v);
}
Comments
Re cutting and pasting, that sounds tedious! Every time I do something manually I'm also automating it in the IDE. I guess that is why it is taking so long to get some quite simple things working here. Hopefully only a few days away from talking to and from the registry.
Running Spinc creates a .h file that looks like this:
This appears to be the core of the original binary with 18 bytes stripped off the beginning and 8 bytes removed from the end. I understand the 18 bytes at the beginning being removed but I do not understand the 8 bytes at the end. Is it always 8 bytes regardless of the program?
Next- a big picture question. We have a .h file that is written in C (as an aside, this appears to be the C equivalent of the BASIC Data statement, and I've been looking for this syntax for a long time!).
How do we use this?
The idea I had is to create a binary file, ending in the extension .cog, that resides on the sd card and can be loaded into a cog.
Now I am thinking that in addition to this, maybe there is another way?
Because including fopen and fclose and other stdio commands in a c program made it a lot bigger, so big that it only can be run in XMM, I believe that this ties future development to XMM only platforms. Maybe this is not correct, but assuming this is the development path for the moment, what it means is that is a much lower cost for a piece of 2k cog code. 2k out of 32k on the propeller is a significant percentage. 2k out of 512k (or 32Mb) is much less significant.
Therefore, it could be quite possible to include code for, say, three different types of VGA drivers, inside a C program - either as standard code, or as included .h files.
So even though sd card access of cog code is quite easy to do, maybe it is better to include the code in the program. I am presuming that loading a cog then becomes as simple as doing a 'coginit' and pointing it at the array in the .h file?
I need to think more about the options here, eg how many cogjects might one ever want to conceivably use? Even if you had 50, that is only 100k out of 512k. Though I might be using more like 10-15.
Note that not every cog binary is 2K in size. For example the 3 built-in cog binaries in my current project only amount to 1008 bytes. So 2K each may be a rather pessimistic view.
Homespun will offer similar functionality real soon now.
I've added spinc into the batch file (it could also be BSTC).
This is a technical question for RossH, but I presume the .h file needs to be moved to the "include" folder, not my working "demos" folder?
Next step with "one click" pasm object creator is to add in some code to the main C program - the #include statement and a new function with the reference to the array and which loads it. My test program is called cogject.spin, the array created by spinc is called
unsigned long COGJECT_array[] and now I need a function:
void load_cogject()
which I think just needs to do a coginit pointing to the cogject_array.
Almost there with one keypress cogject creation...
addit: The following is being created with a few keypresses. I put the .h program in comments to remind me what it looks like. Now I'm a little stuck with the exact syntax for _coginit. From the catalina manual
int _coginit(int par, int addr, int cogid);
This function starts a new cog. The par and addr parameters must be
given as long addresses (which can easily be done by dividing the
normal byte addresses by 4). The cogid parameter can be a specific
cog, or the special value ANY_COG.
but I'm not quite sure what 'par' is, and whether I need to pass addr as the name of the array storing the data, or whether it has a * for a pointer.
Skeleton code below
help with the coginit line would be most appreciated (assuming this idea will actually load a cog!).
No - you can leave the header file in your working folder. But you say: instead of Correct - see the program test_spinc.c in the Catalina\demos\spinc subdirectory. As above - see the program test_spinc.c in the Catalina\demos\spinc subdirectory for a working example.
Found the test_spinc file. I also read the readme.txt file - I'm doing option 3. So - your spinc code
1) I take it that PAR is a series of parameters being passed to the cog. For the moment, can I just include the data array as a dummy array with your values 1,2,3? Does it always have three longs?
2) Each cogject will have a different PAR data array - would I be better off renaming these from data[] to something like, mycogject_par[]
3) Re (int)data>>2. Can we break that down into components, for a C newbie like me? I think there might be three things going on there -
i) a shift, two bits to the right. Can you explain this more?
ii) a conversion to an integer type. I'm not sure what is being converted here - is it the pointer to the data[] array?
iii) an implicit conversion to a pointer to the data[] array. Forgive my confusion regarding pointers - I think I understand pointers where the * and & are used, but I still get confused about pointers that are implied, like pointers to arrays.
4) (int)flash_led_array>>2
i) again, what is the shift for?
ii) flash_led_array is the same name as the .h file. Presumably this is the same as the name of the array within the .h file. But just to clarify, does this have to be the same as the .h name or the same as the array within the .h file?
Many thanks in advance.
eg
where cogject.h is this
ADDIT
worked out 4 ii) - it is the array name within the file, AND it is case sensitive. testing this out now - it is almost compiling
ADDIT again.
I have a program that compiles, but it is not quite working yet. The .h file is as above and the program is thus:
pasm part is
Ross.
Code compiles but the cog is not doing what it is supposed to do. This is getting close now, and it probably is just one tiny little mistake. Can you take a look at this and see what you think:
pasm code
.h code, filename is cogject.h
and the program to run this
and the batch file to compile to xmm
and the batch file to create the cogject file
it is returning B, so the poke is working but the cog code to replace it with an A is not working.
Thanks!
Dr_Acula ...
I realize this is just an example, but as a demonstration it might be better to pass the address (5000) in the par block. For example:
Then your PASM program might be:
Ross.
@Ross - not quite ready for your suggestion as the code still isn't doing what it is supposed to do. Looking forward to sending a variable, but first:
Just to double check, the .lst output is
and the .h file is
Any help would be most appreciated.
Your main problem is that you are using the XMM LARGE (-x5) memory model, where data is in XMM RAM, not Hub RAM. This means your cogject_array is in XMM RAM, and therefore the coginit function can't load it - instead, it is loading some random rubbish from hub RAM.
If you want to continue to use this memory model, you will need to copy the cogject_array to hub RAM before attempting to use it. The same is true of the par_cogject array. Since local variables are always in hub RAM, you should use code something like this:
Also, you are quite likely to be running out of cogs. Since you don't need the mouse, or the keyboard, or the floating point plugin, or the SD card plugin (all of which are being loaded for you) you should use a compile command more like the following:
This works for me.
Ross.
Looks like I need a generic routine based on your example to move data from array x to hub. And if we pass the location of an array, it could 'hide' the >> for the coginit like you mentioned earlier.
In a general sense, all the catalina comms are in high memory, so maybe I could use a buffer from 0-512, (0-2 kilobytes) and then maybe 20k for video buffer, and that still should leave room for stack space (?10k)
OR - I just noticed . Where are they exactly - up the top of hub ram?
Re the options without the mouse, the next thing I am going to do is try wiping out the cogs one at a time and then reloading them from the software. So I'll probably leave the default there for debugging. That might be a bit of a challenge, but it is a step along the way towards doing clever things like changing the VGA from text to color to grayscale from within software.
Just use a local array, as in my example. When you need it, it gets allocated automatically, and when you don't it doesn't consume any valuable hub space.
Ross.
If so - does this mean the biggest array one could have in a local function is about 30k?
Yes, 32kb is the biggest you can have as a local variable under any Catalina memory model.
If you need anything bigger than that you must use the XMM LARGE memory model and allocate the space you need on the heap (using malloc) - you can have megabytes of storage that way.
This is one of the main differences between the XMM LARGE and XMM SMALL memory models - i.e. in the XMM LARGE (-x5) memory model the heap is in XMM RAM. In the XMM SMALL (-x2) memory model the heap is in HUB RAM.
Ross.
It works! Prints "A".
Isn't C just full of surprises. malloc for instance.
But now I know how to load and reload cogs from within a program, I think this is going to open up a whole lot of new possibilities. First, the obvious one is that it frees up hub ram to do the one thing that I think hub ram does better than anything else, and that is to act as a video buffer. The second thing is to help solve the problem of never having quite enough cogs. For instance, you can load an SD driver cog, read the sd card with 100k of data, replace that driver with a serial port driver cog, and output the data. Or load some vga driver cogs and play a little movie with that 100k of data. Or play a wav file.
Ok, next little task is to pass a variable and get it back. I need to go back and study the registry in more detail.
Then I need to take a look at a typical Obex spin/pasm piece of code and think about how to turn this into a C/pasm piece of code. It probably will involve keeping the pasm largely as is, and turning the Spin into C. But it is the binding of the two that is the complicated part. From a vague top-down point of view, I think the idea is to take each Spin PUB function and translate to a C function, but keep links to the associated pasm code. So I think I'll be using the vb.net IDE and frequently swapping between the C and PASM tabs.
BTW - if there is anyone out there who wants to be part of this, let us know as I can post the IDE code. I had a crazy idea on the way home from work that vb.net does lend itself to collaborative input on a program, in that you can create a program and create a tab frame and allow contributors to add a tab item to the frame, then they can add code and visual objects as much as they like and it would stay separate from the other code. eg I have some skeleton code for image manipulation as a separate tab, so if, hypothetically, someone wanted to add a tab for sound manipulation, they could contribute to the IDE without affecting other code.
Just a thought, anyway. My little task here is to make things easier to use. eg - you can run the following code on a propeller by pressing F12
Ok, this is the design challenge. I want to be able to write inline pasm code within a C program in the same way as the proptool. Now, Catalina in external memory can have inline pasm because it is running in LMM, but this isn't quite as useful as I'd like because the timing is not precise, it is not as fast as real cog code, and you can't set things running in the background.
So I want some pasm code that I can load into a cog on the fly from within code. All fine, but for me personally, I want the entire program to be a single text file. RossH on the other hand has said he does not want me to create code that only my IDE can compile, and it is great he has said this because it makes a lot of sense.
So - how do you put pasm code in a C program that will compile with Catalina and also with my IDE?
The answer I have come up with is to put the pasm code within a comment block. So C treats it as a comment. But my IDE is able to find that code, preprocess it, shell out to SpinC to produce a .h file, then reinsert the compiled binary data into the program as a data statement for an array.
So what you can do is use the IDE and compile/download/run the included cog code with a single press of F12.
But you could also do a copy/paste of the pasm code, run it through spinc manually and then manually paste it back into the program, and so this would also run fine with any catalina compiler.
I've also added some automatic color highlighting of the pasm code.
There is also the ability to rebuild all the indents so they look neat (the code to do this is easier to write if { sits on a line by itself, so that is the reason I'm using the slightly more unusual C formatting of putting { on its own line.)
Screenshot of what the code looks like is below. I'm adding various buttons so you can pre-write this code easily, eg this was written with one click of a button.
Hi Dr_Acula,
This seems like a good solution - incluing the PASM that the C code relies on as a comment block makes it self-documenting. And being able to compile it with other tools means it is not tied to any specific toolset.
Ross.
PASM Start mycode.spin
and 'mycode' becomes the name of the .h file as well as the unique name of the array in the C function.
I've got a 'precompile' button on the IDE that rebuilds the code. Take the pasm code, create a .h file, remove the previous C function below the pasm code block and completely rebuild it with the new code.
So easy to use - I changed the test letter from A to D, clicked the precompile button, then F12 to compile and download and it works.
Ok, now on to something more complicated. The whole point of all this is to make it much easier to translate Spin objects into Catalina objects. Using the above it is now possible to write lots of C functions that copy the Spin functions, and then hopefully copy and paste the PASM in directly.
The hard part is binding the two together. I'm going to try this with some real code and see what happens. It may be that the registry is enough and you can pass the location of a block of hub ram.
The hard sort of code is where the Pasm part of an existing spin object is talking to a block of hub ram. It might be an array, or worse, it might be a number of variables that on the spin compiler end up scattered all over hub ram. The spin compiler does not care, as the pasm object code knows where the variables ended up.
I'm thinking of how this fits with the registry model. Say existing spin code has pasm that is talking to 100 bytes of hub ram. At the very least, if those are not contiguous you would rewrite the code so that it is. But even after doing this, we need a way to say that these 100 bytes of ram are reserved and I am not sure how to do that with the registry.
Say object 1 reserves 100 bytes and object 2 reserves 10 and object 3 reserves 4. We don't want each one to overwrite the other. And it would help to have the flexibility to add more code.
I am wondering about using constants in the C code and linking the C functions using those constants. And also passing those constants to the pasm comment blocks.
I guess there will never be a perfect solution if many cogs are being loaded and reloaded. As an example, say one is loading cogs to drive text graphics, then to drive color graphics. Color might reserve 19200 bytes of hub ram. Text would be 3200. So one might say that color graphics are bigger than text, so then put the keyboard circular buffer above 19200. One could be loading in a variety of display drivers/tile drivers etc, but as long as they are never more than 19200 then things would be ok.
Mostly this is something that C contains within itself, but sometimes changing something might need to get passed to pasm as well. So any constant definitions in C might be at the beginning, and maybe the pasm preprocessor looks for these and modifies pasm code accordingly?
To put it another way, maybe the whole hub memory map is defined with constants at the beginning of the C program?
I guess I'll need to have a go at translating an existing object to get a feel for how this might work.
Addit: Brainwave this morning in the shower. At the beginning of the C program, define a list of constants that show what the memory map should be eg
2048 for cog loading
19200 for graphics buffer
50 for keyboard buffer
128 for serial port buffer
C knows about this as these are const definitions
PASM can be told about it as the preprocessor can copy this list of constants into every bit of PASM code into the CON section, and translate from C syntax to pasm syntax.
This becomes the glue between pasm and C
This will make translating existing object code to C a lot easier.
In the code below I'm starting to define constants at the beginning of the C program that define how hub memory is to be used.
I am still a little confused about loading cogs. I'd like to reserve hub memory locations 0 to 2047 for loading cogs.
In this code, how does unsigned long hub_array[512]; know where it should end up?
Does it end up at location 0 or some other location in hub?
Help here would be most appreciated!
Below is some code that glues together C and PASM. Hit Shift F12 and it compiles/downloads/runs on a propeller. But there are some subtleties here:
1) constants defined at the beginning of the C program are translated to CON and copied to every example of PASM code.
2) The precompiler takes all PASM code and converts it to C and copies it back into the C program by reading back the .h program that has been created.
3) at the bottom of the screenshot is a line "testvariable long graphicstart". This shows an example of gluing C and PASM together. At the bottom of the code text is how this is used in C.
i=graphicstart; // location to peek and poke
So - this is still a standard C program. It will compile with catalina.
But- change a constant at the beginning of the C program and recompile and all the PASM gets updated too.
If you want to share variables, use a constant hub location as both C and PASM will know where it is.
And yes, I know this bypasses the registry system. Why am I doing that? Well, I am worried about existing PASM objects that have no code space free at all. This system means that such code should drop into Catalina objects with no need to add any registry interface code.
I think I am now getting close to attempting to port spin objects over to C.
These are some notes to myself as I port Kye's video driver over to C. Some of these will be obvious to those experienced with C and Spin.
1) in the dummy PUB that has coginit, the value in cogint that starts with an @ should match with the first line in the DAT section that comes after the org 0.
2) Kye's code uses some calculations to work out values based on pins and frequency, including some spin code that does a 64 by 32 bit divide. For pin group 2, the constants work out as
directionstate_const = $00FF0000 ' for pin group 2
videostate_const = $30_00_04_FF ' for pin group 2
frequencystate_const = 337914606 ' calculated for 80Mhz
3) For constants just used locally in PASM, define them in the CON section above the _clkfreq line. The precompiler uses the line _clkmode as a flag and copies the C constants after this line, so this is where common constants go that are used by both C and PASM.
4) For passing longs between C and PASM, define a memory location in the global constants part of C. Then use wrlong and rdlong in pasm, and peek and poke in C to pass data back and forth.
5) I keep forgetting what I learned about PASM and again I've found out through various syntax errors that PASM is a hybrid 32 bit/ 9 bit language. So to help my flagging neurons fix this in memory, you can't use constants in pasm code. mov myvariable,myconstant will not work. Instead, near the bottom of the code, define a new value with myvalue long myconstant, then in the code, mov myvariable,myvalue
6) Passing values between Spin and PASM is not the same as between C and PASM. A typical example of some spin code is to do some maths on a long in a supporting PUB. Where is this long first defined? Not as a | in the PUB and not as a global variable in the Spin code. This long is defined usually at the bottom of the PASM code as 'myvariable long 0'. This is a special sort of long and the only way to find these is to do ^F on the name of the longs, scroll through the PASM code where they will usually be mentioned several times, then find that they are defined in PASM code. The proptool compiles this code and 'myvariable' ends up in a hub location that happens to be in the middle of some PASM code that is going to be loaded into a cog. When the PUB Start is run to start this cog, a value might be calculated for myvariable, the value gets stored in hub ram at the location that ends up part of cog code, and then when the cog is started, this value is ready for the cog to use.
We can't do this in C!
The precompiler can glue together constants easily but these 'PASM defined' variables are more complex to handle, especially with self contained cogjects that are going to be loaded in and out multiple times. We can cheat and turn some into constants and then define them in the PASM code as "myvariable long myconstant". But for others, I can't see any solution apart from calculating them in C, storing them to fixed hub locations that are defined in the global constants section using poke, then using a wrlong in PASM to read them back.
Or use the registry?
So - in general, when translating to C, look in the PUB start of the existing object. Any variables that seem to be passed with @ symbols will need known global locations defined in hub ram. Any variables that seem to end up in the PASM code as well - either try to convert these to constants, or if not, then also define a hub ram location for each, and use a rdlong in PASM.
I have yet to look at code in other supporting PUBs but I suspect there will be more of these 'longs that are defined in pasm code and used in Spin'. I guess all the variables will end up as a group which might make the code easier to read. It also would be a step towards reloadable spin objects as these are currently not possible for most objects due to the way code is glued together.
Load up a catalina program, then load a video driver into a cog and change from a white on blue text screen to a graphics screen using Kye's 64 color 160x120 video driver.
Compilation and download is simply Shift F12.
If we can port one object into C, then other objects should be possible too. Next I need to see if I can load a text driver back in. Then a different SD card driver. Serial port drivers etc etc.
This is a new way of using the propeller. 512k of memory. And never run out of cogs - just keep recycling them. You could put the whole Obex on an SD card and load them in and out as needed.
I've attached the vb.net IDE. This is very messy code and some things like the auto indenting and color text are much slower than I would like. You can see some of the complexities of the precompiler where it goes through and removes old code, then creates new code and rebuilds the program prior to sending it off to spinc and catalina and homespun. The aim here is to make it easy for the coder - change one line of text and hit Shift F12 to test it.
I'm making a lot of progress with translating Spin objects into C. I was wondering if I could ask your sage advice on where things get stored?
This is the working Kye driver and the beginnings of the 1280 vga text driver. I have been rereading post #493 about where things get stored, and in general I want to keep as much as possible in XMM.
So regarding this code:
1) Near the bottom is a large array called "tweety" which prints tweety bird on the screen. I have a feeling this is being stored in hub on the stack. If this is correct, what do you think is better - define this as a global array so it ends up in XMM, or define it locally with the malloc example?
2) I am confused about where the program data is being stored
I think the point of moving it from the locally defined array to the hubarray was to move it from xmm to hub. But on reading that previous post, I am wondering if both are in hub? Do I again need to use malloc, or put the array outside a function?
3) I'd like to pass some parameters to the cog.
passes some fixed values. In keeping with the C concept of keeping everything modular, would a better way be to create an array in XMM with some variable data that we might calculate (copied from spin), then pass a pointer to this loading function, which then copies the array to a hub array, and then pass the start of that location to _coginit.
There are a number of Spin objects where one creates a contiguous list of longs, puts variables in that list (I've been using constants but sooner or later I have to move to variables), then pass a pointer to the start of that list via 'par'.
Your advice would be most appreciated.
Addit: I went out and mowed the lawns and had a bit more of a think. What I think happens in XMM is you have a declaration of an array with some code. The data for that resides in XMM. The program gets to the function and finds there is an array and there is some data for that array. So it declares the array and moves the data onto the stack. It is now in hub ram but in the stack part of hub ram. Then I think the data gets moved to another array in hub ram - I'll have to think about that a little more...
Addit Addit:
Thinking through the above further, I replaced this code:
with this code
and it still seems to work. I presume that the act of declaring an array in a local function moves the array from data within XMM to the stack, and if we start _coginit pointing to that array in the stack it will still load?
Working on translating Obex code into C. I've been building a structure such that the C code ends up similar to the Spin code. That involves passing parameters and having common memory locations. Lots of fun and it is great as more things come together.
I gather that the propeller stores longs in 'little endian' format. I wrote a 'pokelong' routine to do this one byte at a time. I tried doing this with one line but it didn't work. So this is the warts and all code and it has been tested. Any C experts see a way to simplify the pokelong routine?
Addit:
This seems to work
I think this can become the building block for passing parameters.
Typical Spin code:
* Define a list of global variables in a Var section
* Fill them up in a Start routine
* Start a cog and pass the 'par' as the first variable in that Var section
This then passes as many variables in that Var list as the cog needs to use.
Translating this to C:
* Define a global constant at a known hub location (cogs may want exclusive use of this location)
* Define a list of unsigned longs in a Start function
* Poke them individually to the hub ram at the global constant location, as many as required
* Point variable 'parameters' to the beginning of the list
* Call the function to load the cog. Pass two variables - cog number and parameter eg void vga1280(char cognumber, unsigned long parameters)
* cog loader contains compiled pasm code.
* point 'par_cogject' array to paramters
* load the cog
For cog code that just needs a list of values passed but no reserved hub ram, there are the plugins.
For situations where the cog also needs fixed hub locations and needs exclusive use of those (eg video drivers needing video buffer space), use the above code.
One slightly complicated task is working out which of these two is best for a typical piece of obex code. Need to go through all the code looking for other references for passed variables to see if they are exclusively using fixed hub locations.
I'm working on translating this to C:
I'm back. I have some work I need to do today, but I'll try and get a chance to answer your questions later today.
Ross.
Regarding your questions in post #568: Mostly you have answered them yourself - but just to confirm:
1. Yes, if your program is compiled in XMM mode, then the array "tweety" is indeed being created on the stack. But this array has an initializer (i.e. the values 0xfc etc) which will be in XMM RAM anyway, and get copied to the array on the stack at run time - so you may as well make the array global (i.e. give it file scope) and put the whole array in XMM RAM - this actually saves both time and space.
2. No, you don't need to do the copy here - as I mentioned in the answer to 1 (above) - if a local array has an initializer, that initializer will be in XMM RAM, but the compiler will copy it to the stack (i.e. hub RAM) for you at run time.
3. When calling another language from C, one way to make sure the variables you want to pass are physically located where you want them is to declare a structure in C with the appropriate memory layout, fill in the values at run time, and pass a pointer to the structure. For example:
Ross.