mima-tools/examples/stack.mimasm

195 lines
3.6 KiB
Text

; This file is intended to demonstrate how stack frames and function
; calls could be implemented.
;
; The stack starts at 0xfffff and grows towards 0x00000. The stack
; pointer always points to the next free address below the stack. This
; means that if the stack pointer is at 0xfffff, the stack is empty.
;
; The frame pointer points to the beginning of the stack frame. If the
; stack pointer points at the same position as the frame pointer, the
; current stack frame is empty.
;
; Functions take a fixed number of arguments. For this example, all
; values have a size of one word (24 bit).
.reg IAR main
.reg SP -1
.reg FP -1
; Temporary variables at a fixed memory location
;
; These are useful for commands like ADD, which need a fixed memory
; address. They are always under the complete control of the currently
; running function.
tmp1: .lit 0
tmp2: .lit 0
tmp3: .lit 0
tmp4: .lit 0
tmp5: .lit 0
.org 100
main:
CALL caller
HALT
.org 200
caller:
;; 1. Initialisation
; 1.1. Create own stack frame
LDFP
STRS 0
LDSP
ADC -1
STSP
STFP
; 2. Push RA onto current stack frame
LDRA
STRS 0
LDSP
ADC -1
STSP
; Do some work here
;; 2. Prepare call
; 2.1. Create a new shared stack frame
LDFP
STRS 0
LDSP
ADC -1
STSP
STFP
; 2.2. Write function parameters onto shared stack frame and keep
; space for return values
LDC 5
STRF 0
LDC 7
STRF -1
LDFP
ADC -4 ; 2 parameters and 2 return values
STSP
; Now, the shared stack frame looks like this:
;
; FP offset | Value
; 0 | argument 1
; -1 | argument 2
; -2 | return value 1
; -3 | return value 2
;; 3. Call callee
CALL callee
;; 4. Cleanup after call
; 4.1. Copy resulting values from the shared stack frame, so we can
; restore our own stack frame
LDRF -2
STV tmp1
LDRF -3
STV tmp2
; 4.2. Restore own stack frame
LDFP
ADC 1
STSP
LDRF 1
STFP
; Now, we can use the results stored in tmp1 and tmp2.
; Do some more work here
;; 5. Cleanup before RET
; 5.1. Pop and restore RA
LDRS 1
STRA
LDSP
ADC 1
STSP
; 5.2. Restore previous stack frame
LDFP
ADC 1
STSP
LDRF 1
STFP
RET
.org 300
callee:
; This callee doesn't really need its own stack since all
; calculations did fit into the temporary variables. I still created
; a stack frame for this function to demonstrate how it would work
; if a stack was required.
; 1. Create own stack frame
LDFP
STRS 0
LDSP
ADC -1
STSP
STFP
; Since we've pushed the old FP to the shared stack frame (which now
; has length 5) and moved the FP to after that, we've in effect
; added 5 to the FP offset. This means that the shared stack frame
; now looks like this:
;
; FP offset | Value
; 5 | argument 1
; 4 | argument 2
; 3 | return value 1
; 2 | return value 2
; 2. Load arguments into temporary variables
LDRF 5
STV tmp1
LDRF 4
STV tmp2
; 3. Add arguments and put result into return value 1
LDV tmp1
ADD tmp2
STRF 3
; 4. Multiply arguments and put result into return value 2
LDC 0
STV tmp3 ; For comparison in loop
STV tmp4 ; For accumulating result
callee-loop:
; Break if counter (tmp1) is zero
LDV tmp1
EQL tmp3
JMN callee-loop-end
; Decrement counter (tmp1) by 1
LDV tmp1
ADC -1
STV tmp1
; Increment result (tmp4) by the second argument (tmp2)
LDV tmp4
ADD tmp2
STV tmp4
; And repeat the loop
JMP callee-loop
callee-loop-end:
; Save the result in return value 2
LDV tmp4
STRF 2
; 5. Restore shared stack frame
LDFP
ADC 1
STSP
LDRF 1
STFP
; And this function is done :)
RET