The RESULT variable is located at DBASE. The stack frame consist of the 4 words (2 longs) just before the RESULT variable. You do not need to initialize it if your program doesn't do a return from the first method. However it is needed if your do a return. The value $FFF9FFFF will cause it to return to address $FFF9 in the ROM, which performs a cogstop(cogid).
Looks too simple, why would starting a cog that points to a function invoke a new copy of the Spin interpreter?
I guess Spin knows that is what you want, but I would had guess you would have to so something extra first.
PUB Main 'starts in cog 0
cognew(flash1,@stack1) 'start next cog which s/be #1
PRI flash1
dira[1] := 1
repeat
DBASE points to the STACK (if you specify one), else to the "end of the variables, etc" for the "boot" case.
Now, the first long is actually reserved for the RESULT, and we should set it to FFF9FFFF just in case???
The next long (DBASE+4) is the first long on the stack, and should also be set to FFF9FFFF.
DBASE points to the STACK.
The first long on the stack is actually reserved for the RESULT. Should we set it to 0 or FFF9FFFF ???
So on spin program start DCURR = DBASE + 4.
There are 2 longs of FFF9FFFF following the VAR, and before the STACK ???
The subtle part is at the beginning - coginint, then some parameters, then a line, then coginit and some parameters. I read that the first time and didn't get it, but reading it again, it becomes clearer - coginit is one instruction but it can be used in two completely different ways.
I've only ever coded it the second way - using it to launch some pasm code. But read that first method again " COGINIT (CogID, SpinMethod < (ParameterList) >, StackPointer )"
This is loading and launching the spin interpreter, and then running a particular PUB or PRI.
Ok, maybe that will do the trick. Maybe it need not be so complicated after all.
I guess it could get complicated if that PUB then called another PUB which was shared between two cogs. But maybe not - each would be running in its own stack space and each would be creating its own local variables so they would stay separate.
I need to think about this some more. First thought - declare some stack space and keep them all separate. Not sure how much stack space is needed - I guess it is one long for each subroutine call so those won't be much as most code is not that nested, but the local variables are presumably going to need stack space, so tally them up and should give a ballpark figure.
But I guess there is still the issue being discussed above about precompiled code that is brought in from say an SD card and loaded into a particular location in memory. Can that location change from the location where it was first compiled? From what dave is saying, yes it can, if a few parameters are adjusted.
So to keep things simple, I compile my led flasher program and it produces a few bytes of code, and I store that on an SD card. I then read it back to, say, location 5000H in hub ram, and I tell it to use a stack at, say, location 6000H. Then I do a coginit and pass the stack location and the cog number and the parameterlist and the location of the code.
Why go to this bother? Well the idea I have is that you have SD driver code which currently takes half the hub ram or more. Move that driver code into external ram. Have a routine to transfer between hub and external ram and this is just a few pasm instructions. Backup the current spin program to external ram and pause it, move the SD card code into hub, grab blocks of data and move to external ram, restore the original program, and maybe it is then possible to have SD code that effectively takes no memory. That frees up lots more space for the actual program you might write.
I think I can see how the bits might start to fit together.
Looks too simple, why would starting a cog that points to a function invoke a new copy of the Spin interpreter?
I guess Spin knows that is what you want, but I would had guess you would have to so something extra first.
PUB Main 'starts in cog 0
cognew(flash1,@stack1) 'start next cog which s/be #1
PRI flash1
dira[1] := 1
repeat
Because the spin interpreter is a PASM program that needs to be run in the cog (and it consumes the whole cog). So, coginit/cognew copies the spin interpreter from $F004 (hub ROM) to the cog RAM and begins execution of the spin interpreter (a pasm program).
.....
I guess it could get complicated if that PUB then called another PUB which was shared between two cogs. But maybe not - each would be running in its own stack space and each would be creating its own local variables so they would stay separate.
As long as they are compiled together and loaded together (relocated), then no problems.
I need to think about this some more. First thought - declare some stack space and keep them all separate. Not sure how much stack space is needed - I guess it is one long for each subroutine call so those won't be much as most code is not that nested, but the local variables are presumably going to need stack space, so tally them up and should give a ballpark figure.
But I guess there is still the issue being discussed above about precompiled code that is brought in from say an SD card and loaded into a particular location in memory. Can that location change from the location where it was first compiled? From what dave is saying, yes it can, if a few parameters are adjusted.
Bring it in from SD or RAM or FLASH, etc.
The main program just needs to reserve sufficient space in hub for the relocatable objects to reside, including their variables and stack space.
If you want to share variables between the main program and the relocatable object modules, then you will need to have a CON section (use an include style object) that equates to where the variables live in the main program.
So to keep things simple, I compile my led flasher program and it produces a few bytes of code, and I store that on an SD card. I then read it back to, say, location 5000H in hub ram, and I tell it to use a stack at, say, location 6000H. Then I do a coginit and pass the stack location and the cog number and the parameterlist and the location of the code.
No. Everything is correct up to telling it where the stack is. The relocatable spin loader (part of the main program) will put the stack at the end of the variables which come after the object. The variables and stack do not get loaded. The variables just get cleared in hub after the object is loaded, and the top of stack gets initialised with fff9ffff fff9ffff.
Why go to this bother? Well the idea I have is that you have SD driver code which currently takes half the hub ram or more. Move that driver code into external ram. Have a routine to transfer between hub and external ram and this is just a few pasm instructions. Backup the current spin program to external ram and pause it, move the SD card code into hub, grab blocks of data and move to external ram, restore the original program, and maybe it is then possible to have SD code that effectively takes no memory. That frees up lots more space for the actual program you might write.
It is going to take a little more work to be able to "pause" the cog and "unload the hub space - code/variables/stack", load in a new "code/variables/stack", "re-commence" the cog, etc.
But I can see that it is doable.
There would be no need to save out the "code" section to external memory (RAM/SD/etc) as long as it is there to be reloaded.
I think I can see how the bits might start to fit together.
DBASE points to the STACK (if you specify one), else to the "end of the variables, etc" for the "boot" case.
Now, the first long is actually reserved for the RESULT, and we should set it to FFF9FFFF just in case???
The next long (DBASE+4) is the first long on the stack, and should also be set to FFF9FFFF.
DBASE points to the STACK.
The first long on the stack is actually reserved for the RESULT. Should we set it to 0 or FFF9FFFF ???
So on spin program start DCURR = DBASE + 4.
There are 2 longs of FFF9FFFF following the VAR, and before the STACK ???
DBASE points to the RESULT variable, and not the beginning of the stack. The stack actually begins 8 bytes before DBASE. The first 8 bytes contain the initial stack frame, which is used to jump to $FFF9 if the first method returns.
DCURR will equal DBASE + 4 only if the starting method has no parameters, and has no local stack variables. In general, DCURR = DBASE + 4 + N*4 + M*4, where N is the number of calling parameters and M is the number of local stack variables.
Normally the VAR data space immediately follows the code in memory, which is normally followed by the stack. However, this isn't strictly required. The values of VBASE, DBASE and DCURR can be modified to point to any block of memory in hub RAM.
DBASE points to the RESULT variable, and not the beginning of the stack. The stack actually begins 8 bytes before DBASE. The first 8 bytes contain the initial stack frame, which is used to jump to $FFF9 if the first method returns.
DCURR will equal DBASE + 4 only if the starting method has no parameters, and has no local stack variables. In general, DCURR = DBASE + 4 + N*4 + M*4, where N is the number of calling parameters and M is the number of local stack variables.
Normally the VAR data space immediately follows the code in memory, which is normally followed by the stack. However, this isn't strictly required. The values of VBASE, DBASE and DCURR can be modified to point to any block of memory in hub RAM.
Got it. My sample listing was not complete enough to show all possibilities.
There doesn't seem to be any way to determine the exact length of VAR if the stack does not follow the variables ???
We could now separate the FAT file handling into spin object(s) from the raw SD sector access. This would reduce the FAT driver footprint, especially if we broke it up into smaller objects.
BTW Here is a listing of a single object "spin_flash1"
Note in the binary file, the VAR Section onwards is not included, and has to be fabricated by the spin object loader (see Dave's example a few posts ago).
Thanks cluso for doing these experiments. I think the propeller has a few tricks yet that we haven't tried out. I see there is a long running language thread of C vs Basic running on the forum - how crazy/nifty/impossible would it to be to neatly sidestep the argument by running multiple languages on the propeller... all at the same time? Allocate separate hub memory for each one, a cog per language, and set them all going.
Back in the olden days, compilers had the .org instruction - compile starting at this memory location. Is there an equivalent in spin?
You can sort of do this already with PropGCC and Spin. I wrote a program a while back called spinwrap that lets you call Spin methods from within a C program. Actually, it works best with C++ since Spin objects can be mapped to C++ objects so calling methods looks natural. The spinwrap code is in a repository in the Parallax github account if you're interested in looking at it.
The main program just needs to reserve sufficient space in hub for the relocatable objects to reside, including their variables and stack space.
I see Cypress 1MBit FRAM FM24V10 supports 2-wire and 3.4MHz speeds, so while it is not especially cheap, it could be a good device to prove larger scale swap behaviour. FRAM can also preserve variables, not just code.
Comments
I guess Spin knows that is what you want, but I would had guess you would have to so something extra first.
DBASE points to the STACK (if you specify one), else to the "end of the variables, etc" for the "boot" case.
Now, the first long is actually reserved for the RESULT, and we should set it to FFF9FFFF just in case???
The next long (DBASE+4) is the first long on the stack, and should also be set to FFF9FFFF.
DBASE points to the STACK.
The first long on the stack is actually reserved for the RESULT. Should we set it to 0 or FFF9FFFF ???
So on spin program start DCURR = DBASE + 4.
There are 2 longs of FFF9FFFF following the VAR, and before the STACK ???
A good question.... which has sent me back to the instruction manual (yes, I know, RTFM!)
There is a really good description here https://lamestation.atlassian.net/wiki/display/SPIN/COGINIT
The subtle part is at the beginning - coginint, then some parameters, then a line, then coginit and some parameters. I read that the first time and didn't get it, but reading it again, it becomes clearer - coginit is one instruction but it can be used in two completely different ways.
I've only ever coded it the second way - using it to launch some pasm code. But read that first method again " COGINIT (CogID, SpinMethod < (ParameterList) >, StackPointer )"
This is loading and launching the spin interpreter, and then running a particular PUB or PRI.
Ok, maybe that will do the trick. Maybe it need not be so complicated after all.
I guess it could get complicated if that PUB then called another PUB which was shared between two cogs. But maybe not - each would be running in its own stack space and each would be creating its own local variables so they would stay separate.
I need to think about this some more. First thought - declare some stack space and keep them all separate. Not sure how much stack space is needed - I guess it is one long for each subroutine call so those won't be much as most code is not that nested, but the local variables are presumably going to need stack space, so tally them up and should give a ballpark figure.
But I guess there is still the issue being discussed above about precompiled code that is brought in from say an SD card and loaded into a particular location in memory. Can that location change from the location where it was first compiled? From what dave is saying, yes it can, if a few parameters are adjusted.
So to keep things simple, I compile my led flasher program and it produces a few bytes of code, and I store that on an SD card. I then read it back to, say, location 5000H in hub ram, and I tell it to use a stack at, say, location 6000H. Then I do a coginit and pass the stack location and the cog number and the parameterlist and the location of the code.
Why go to this bother? Well the idea I have is that you have SD driver code which currently takes half the hub ram or more. Move that driver code into external ram. Have a routine to transfer between hub and external ram and this is just a few pasm instructions. Backup the current spin program to external ram and pause it, move the SD card code into hub, grab blocks of data and move to external ram, restore the original program, and maybe it is then possible to have SD code that effectively takes no memory. That frees up lots more space for the actual program you might write.
I think I can see how the bits might start to fit together.
The main program just needs to reserve sufficient space in hub for the relocatable objects to reside, including their variables and stack space.
If you want to share variables between the main program and the relocatable object modules, then you will need to have a CON section (use an include style object) that equates to where the variables live in the main program. No. Everything is correct up to telling it where the stack is. The relocatable spin loader (part of the main program) will put the stack at the end of the variables which come after the object. The variables and stack do not get loaded. The variables just get cleared in hub after the object is loaded, and the top of stack gets initialised with fff9ffff fff9ffff. It is going to take a little more work to be able to "pause" the cog and "unload the hub space - code/variables/stack", load in a new "code/variables/stack", "re-commence" the cog, etc.
But I can see that it is doable.
There would be no need to save out the "code" section to external memory (RAM/SD/etc) as long as it is there to be reloaded. Me too
DCURR will equal DBASE + 4 only if the starting method has no parameters, and has no local stack variables. In general, DCURR = DBASE + 4 + N*4 + M*4, where N is the number of calling parameters and M is the number of local stack variables.
Normally the VAR data space immediately follows the code in memory, which is normally followed by the stack. However, this isn't strictly required. The values of VBASE, DBASE and DCURR can be modified to point to any block of memory in hub RAM.
There doesn't seem to be any way to determine the exact length of VAR if the stack does not follow the variables ???
Been thinking (yes, dangerous I know )...
We could now separate the FAT file handling into spin object(s) from the raw SD sector access. This would reduce the FAT driver footprint, especially if we broke it up into smaller objects.
BTW Here is a listing of a single object "spin_flash1" Note in the binary file, the VAR Section onwards is not included, and has to be fabricated by the spin object loader (see Dave's example a few posts ago).
https://github.com/parallaxinc/spinwrap
I see Cypress 1MBit FRAM FM24V10 supports 2-wire and 3.4MHz speeds, so while it is not especially cheap, it could be a good device to prove larger scale swap behaviour. FRAM can also preserve variables, not just code.