The Stack |
The 6502 microprocessor features support for a stack, located at &1xx in memory, and extending for 256 bytes. It also featured instructions which performed instructions more quickly relative to page zero (&0xx).
Both of these are inflexible, and not in keeping with the RISC concept.
The ARM processor provides instructions for manipulating the stack (LDM and
STM). The actual location where your stack lays it's hat is entirely up to you and the rules
of good programming.
For example:
MOV R13, #&8000 STMFD R13!, {R0-R12, R14}would work, but is likely to scribble your registers over something important. So typically you would set R13 to the end of your workspace, and stack backwards from there.
These are conventions used in RISC OS. You can replace R13 with any register except R14 (if you
need it) and R15. As R14 and R15 have a defined purpose, the next register down is R13, so that
is used as the stack pointer.
Likewise, in RISC OS, the stacks are fully descending (FD, or IA) which means the stack grows
downwards in memory, and the updated stack pointer points to the next free location.
You can, quite easily, shirk convention and stack using whatever register you like (R0-R13 and R14 if you don't need it) and also you can set up any kind of stack you like, growing up, growing down, pointer to next free or last used... But be aware that when RISC OS provides you with stack information (if you are writing a module, APCS assembler, BASIC assembler, or being a transient utility, for example) it will pass the address in R13 and expect you to be using a fully descending stack. So while you can use whatever type of stack/location that suits you, it is suggested you follow the OS style. It makes life easier.
If you are not sure what a stack is, exactly, then consider it a temporary dumping area. When
you start your program, you will want to put R14 somewhere so you know where to branch to in
order to exit. Likewise, every time you BL
, you will want to put R14 someplace if
you plan to call another BL
.
To make this clearer:
; ...entry, R14 points to exit location BL one BL two MOV PC, R14 ; exit .one ; R14 points to instruction after 'BL one' ...do stuff... MOV PC, R14 ; return .two ; R14 points to instruction after 'BL two' ...do stuff... BL three MOV PC, R14 ; return .three ; R14 points to instruction after 'BL three' B four ; no return .four ; Not a BL, so R14 unchanged MOV PC, R14 ; returns from .three because R14 not changed.Take a moment to work through that code. It is fairly simple. And fairly obvious is that something needs to be done with R14, otherwise you won't be able to exit. Now, a viable answer is to shift R14 into some other register. So now consider that the "...do stuff..." parts use ALL of the remaining registers.
That code again:
; ...entry, R14 points to exit location, we assume R13 is set up STMFD R13!, {R14} BL one BL two LDMFD R13!, {PC} ; exit .one ; R14 points to instruction after 'BL one' STMFD R13!, {R14} ...do stuff... LDMFD R13!, {PC} ; return .two ; R14 points to instruction after 'BL two' STMFD R13!, {R14} ...do stuff... BL three LDMFD R13!, {PC} ; return .three ; R14 points to instruction after 'BL three' B four ; no return .four ; Not a BL, so R14 unchanged LDMFD R13!, {PC} ; returns from .three because R14 not changed.A quick note, you can write:
STMFD R13!, {R14} ...do stuff... LDMFD R13!, {R14} MOV PC, R14but the STM/LDM does NOT keep track of which stored values belong in which registers, so you can store R14, and reload it directly into PC thus disposing of the need to do a MOV afterwards.
The caveat is that the registers are saved in ascending order...
STMFD R13!, {R7, R0, R2, R1, R9, R3, R14}will save R0, R1, R2, R3, R7, R9, and R14 (in that order). So code like:
STMFD R13!, {R0, R1} LDMFD R13!, {R1, R0}to swap two registers will not work.
Please refer to this document for details on STM/LDM and how to use a stack.