Question regarding "stack marker" and bootloader's role to create it
ags
Posts: 386
Working on a new bootloader (with helpful examples and instruction from others) has raised two questions:
1) I believe the stack starts after VAR space, and grows down (towards $7FFF. I see there is a "secret marker" consisting of two sequential long values $FFF9FFFF after the end of VAR space (the end of user-defined SPIN code). I also see in the "header" (not sure of the correct term) at $000A there is a word value indicating the beginning of stack space after the "stack marker" (free space). So, the "stack marker" seems redundant. Question: why/when is it needed? I came up with what seems to me a plausible answer which would be to make handling abort easier. Rather than unwinding the stack "manually", or checking the "end of VAR" address, just pop until the "stack marker" is seen. Is this corrrect?
2) I wrote some simple SPIN code and created and saved a binary file. From the length (I miss unix's "od" utility) alone it seems like the "stack marker" is not present in the binary file. If I were to download the binary file to RAM and then execute it (and the "stack marker" is required) then that would be a problem. In all the bootloader examples I've seen, I've not found (or recognized) any code that takes care of adding this "stack marker". I'm not sure if the bootloader examples assume that what is being downloaded is a full EEPROM image (which may contain the "stack marker" - I can't tell, but I can see that the image is 32k bytes long, at least) - or if the "stack marker" is not needed (unlikely, I presume) - or if I'm just not recognizing the code. Does a robust bootloader implementation not assume the "stack marker" is present in the image being loaded, and ensure one is inserted?
Thanks!
1) I believe the stack starts after VAR space, and grows down (towards $7FFF. I see there is a "secret marker" consisting of two sequential long values $FFF9FFFF after the end of VAR space (the end of user-defined SPIN code). I also see in the "header" (not sure of the correct term) at $000A there is a word value indicating the beginning of stack space after the "stack marker" (free space). So, the "stack marker" seems redundant. Question: why/when is it needed? I came up with what seems to me a plausible answer which would be to make handling abort easier. Rather than unwinding the stack "manually", or checking the "end of VAR" address, just pop until the "stack marker" is seen. Is this corrrect?
2) I wrote some simple SPIN code and created and saved a binary file. From the length (I miss unix's "od" utility) alone it seems like the "stack marker" is not present in the binary file. If I were to download the binary file to RAM and then execute it (and the "stack marker" is required) then that would be a problem. In all the bootloader examples I've seen, I've not found (or recognized) any code that takes care of adding this "stack marker". I'm not sure if the bootloader examples assume that what is being downloaded is a full EEPROM image (which may contain the "stack marker" - I can't tell, but I can see that the image is 32k bytes long, at least) - or if the "stack marker" is not needed (unlikely, I presume) - or if I'm just not recognizing the code. Does a robust bootloader implementation not assume the "stack marker" is present in the image being loaded, and ensure one is inserted?
Thanks!
Comments
The "stack marker" contains the context of the caller's stack pointer, program base, variable base and return address. When a method is called in Spin the interpreter puts this information on the stack. The $FFF9 that is part of the initial stack frame is a return address pointing to code in the Prop's ROM. This code contains a cogstop(cogid) instruction, which shuts down the cog. The return to $FFF9 isn't required if the loaded program never terminates. However, if the first method does return, or an abort is issued that is not trapped, then the $FFF9 should be used to cleanly terminate the program.
So, to answer your question, the bootloader should add the stack marker.
Dave
In case you're using the PropTool, once you pressed F8 you get an overview about the memory allocations, binary files will only save the grey/red area, EEPROM files the whole 32K (gray/red/yellow/blue). You can also load external (already compiled and saved) binaries from this view. For example: generates the attached view. The binary length for this file would be 32.
I actually have my custom bootloader running (in primitive form) and it is great! It is loading through LAN (similar to Darco's ybox2 code - but mine is not (yet) nearly as robust) and is very, very fast. I have to look very closely at the blinking "I'm alive" LED to catch the pause during restart.
I have compiled a list of "magic bytes" in the binary image that have been explained by others, found in documentation, and have explored and understand them somewhat. There are a few missing pieces, can anyone clarify?
LONG[0]: clock frequency
BYTE[4]: clock mode
BYTE[5]: checksum (summing all bytes 0 through the $FFF9FFFF,FFF9FFFF stack marker results in 0
WORD[3]: ?? (or perhaps BYTE[6:7] Is this some kind of "magic identifier" for a valid SPIN binary image? It seems to be consistently $0001 for binaries I've generated.
WORD[4]: beginning of VAR block
WORD[5]: top of stack (immediately following the $FFF9FFFF,FFF9FFFF stack marker)
WORD[6]: no clue
WORD[7]: can't buy a clue...
Not that it matters (this is minutia) but... I understand that with the Propeller tool and a PropPlug or ProbUSB board, the "Load EEPROM" function creates a full 32kB image and loads the entire lower 32kB of EEPROM. The "Load RAM" function generates a binary (<object.binary>) file, which I've learned (through the helpfulness of others) contains only code (& DAT), but no VAR or STACK information. (That is, the VAR space is not included and intialized to 0 as guaranteed in the SPIN language docs, nor is the $FFF9FFFF (x2) stack marker present). That means those (important) steps are done somewhere in the process of loading an (incomplete) .binary image to RAM. Is that done by the serial load code that listens to pin 30 (which I presume is in Propeller ROM)? I guess that makes sense, given how slow the serial load is (for impatient people such as myself), since the VAR space to be cleared, and the location where the stack marker should be written can be inferred from the first few words of the image. Does anyone know if that was the thinking?
Thanks!
I've always computed the checksum without including the two $FFF9FFFF longs and got $14. I never understood why it should add up to $14, but now I do. Thanks for pointing that out.
Words 3 through 7 are as follows:
word[3] - PBASE - Program base. This is the start of the DAT variables and program code after the 16-byte header. It always has a value of 16, or $10
word[4] - VBASE - Variable base. This starts immediately after the program code. It should always be the same as the file size.
word[5] - DBASE - Stack variable base. This points to the first variable on the stack at program start, which is the RESULT variable.
word[6] - PCURR - Current program counter. This points at the starting address of the first instruction to be executed.
word[7] - DCURR - Stack pointer. This is the initial value of the stack pointer.
The loader adds the $FFF9FFFF longs and sets the PAR register to point to the header. The Spin interpreter initializes its internal state variables with the 5 words in the header and begins execution at PCURR.
Dave
Thanks for the response. That was just what I was looking for. It also prompted me to do a bit more tinkering.
1) In an object with no variables or instructions, there are still two longs before the program start. Any idea what they are? Something used to initialize a cog loaded with the DAT block for PASM execution?
2) The PBASE is redundant (today) but can serve with the checksum to help ensure a valid image, I suppose. I guess it may be useful in the future.
3) While described as providing initial values at startup, does SPIN ever modify some of these words during execution? PBASE, VBASE won't change, but DBASE, PCURR and DCURR contain values that will. I suppose the answer is "no" - not because this memory is special (I can write to it, can't I - not that I want to) but because it would be hub memory access which is slower than a cog memory access.
Thanks again.
3) Absolute address locations $0000 to $0004 must be preserved because the Spin interpreter uses these locations for the system clock information. The rest of the header can be modified after startup.
Thanks again.
Is there an online source for all this information? I have what I need for now, and while you seem an encyclopedic source of knowledge on this topic, I don't want to keep bothering you with questions (and I assume you do sleep *sometime*).
Makes sense. I expected $0000-$0004 to be required. You say that the rest "can be" modified after startup, so by that I take it that the SPIN interpreter only accesses those values at startup, and doesn't use them to store dynamically changing values (perhaps for the reason I offered/guessed above). Is that correct?
The Spin interpreter doesn't read or write locations $0005 to $000F after startup.