Shop OBEX P1 Docs P2 Docs Learn Events
Question regarding "stack marker" and bootloader's role to create it — Parallax Forums

Question regarding "stack marker" and bootloader's role to create it

agsags Posts: 386
edited 2011-02-10 15:32 in Propeller 1
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!

Comments

  • Dave HeinDave Hein Posts: 6,347
    edited 2011-02-09 10:14
    ags,

    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
  • kuronekokuroneko Posts: 3,623
    edited 2011-02-09 15:43
    ags wrote: »
    In all the bootloader examples I've seen, I've not found (or recognized) any code that takes care of adding this "stack marker".
    Picking two random examples, sdspiFempto:
    nowBootSpin             test    Options,#ioNoStore wc
                            mov     i2cTemp,Preamble+2
                            shr     i2cTemp,#16             ' Get dbase value
                            sub     i2cTemp,#4
                    if_nc   [COLOR="red"]wrlong  StackMark,i2cTemp[/COLOR]       ' Place stack marker at dbase
                            sub     i2cTemp,#4
                    if_nc   [COLOR="red"]wrlong  StackMark,i2cTemp[/COLOR]
                            mov     i2cOther,Preamble+2     ' Get vbase value
    
                            ...
    
    StackMark               long    $FFF9FFFF               ' Two of these mark the base of the stack
    
    ROM bootloader:
    :zero   if_nz           wrlong  zero,address
            if_nz           add     address,#4
            if_nz           djnz    count,#:zero            '(count=0, address=$8000)
    
                            rdword  bits,#$0004+6           'get dbase address
                            sub     bits,#4                 'set pcurr to $FFF9
                            [COLOR="red"]wrlong  hFFF9FFFF,bits[/COLOR]
                            sub     bits,#4                 'set pbase flags
                            [COLOR="red"]wrlong  hFFF9FFFF,bits[/COLOR]
    
                            ...
    
    hFFF9FFFF               long    $FFF9FFFF
    
    It's easier if you look for the marker pattern first (you can't embed it in the instruction(s) as an immediate value) and then look where the label is referenced.
    ags wrote: »
    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.
    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:
    VAR
      long  storage[16]
      
    PUB null
    
      clkset(%10000000, 0)
    
    generates the attached view. The binary length for this file would be 32.
    885 x 514 - 52K
    F8.png 51.6K
  • agsags Posts: 386
    edited 2011-02-10 12:28
    @kuroneko-san: Wow, that's great information, very clear and well-written. Thank you.

    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!
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-02-10 12:49
    ags,

    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
  • agsags Posts: 386
    edited 2011-02-10 13:12
    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.
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-02-10 15:11
    1) Each object contains a method table at the beginning. The method table looks like this:
    word[PBASE]    Size of object in bytes
    word[PBASE][1] Number of methods and objects that are referenced
    word[PBASE][2] Starting address of the first method
    word[PBASE][3] Number of local varaible bytes used by the first method
    word[PBASE][4] Starting address of the second method
    ...
    
    2) The PBASE is alwasy $10 for programs loaded at location zero, but if you load a program at some other address the PBASE and the other four values must be biased by the address location. The Spn interpreter also creates a temporary 5-word header in the new stack when a cognew is performed on a method.

    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.
  • agsags Posts: 386
    edited 2011-02-10 15:26
    Dave:

    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*).
    Dave Hein wrote: »
    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.

    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?
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-02-10 15:32
    There is information scattered around in the forum and other places on the internals of the Spin interpreter. The wiki at http://propeller.wikispaces.com/ is a good place to look.

    The Spin interpreter doesn't read or write locations $0005 to $000F after startup.
Sign In or Register to comment.