; 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). IAR = caller RA = 12345 ; to test whether the caller stores and restores the RA correctly SP = 0xfffff FP = 0xfffff ; 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 100: caller: ; 1. Push RA onto current stack frame LDRA STVR 0 LDSP ADC -1 STSP ; SP offset: 1 ; 2. Create a new shared stack frame LDFP STVR 0 LDSP ADC -1 STSP STFP ; 3. Push function parameters onto shared stack frame LDC 5 STVR 0 LDC 7 STVR -1 LDSP ADC -2 STSP ; SP offset: 2 ; 4. Create space for return values LDSP ADC -2 STSP; SP offset: 4 ; Now, the shared stack frame looks like this: ; 4: argument 1 ; 3: argument 2 ; 2: return value 1 ; 1: return value 2 ; 5. Call callee CALL callee ; 6. Copy resulting values from the shared stack frame, so we can ; restore our own stack frame LDVR 2 STV tmp1 LDVR 1 STV tmp2 ; 7. Restore own stack frame LDFP ADC 1 STSP LDVR 0 STFP ; 8. Pop and restore RA LDVR 1 STRA LDSP ADC 1 STSP ; SP offset: 0 ; Now, we can use the results, or put them onto our own stack like so: LDV tmp1 STVR 0 LDV tmp2 STVR -1 LDSP ADC -2 STSP ; SP offset: 2 HALT 500: 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 STVR 0 LDSP ADC -1 STSP STFP ; Now, the shared stack frame looks like this: ; 5: argument 1 ; 4: argument 2 ; 3: return value 1 ; 2: return value 2 ; 1: previous FP ; ; The compiler needs to keep track of the offset of the SP within ; the current stack frame, so that LDVR and STVR have the correct ; offset when accessing variables inside it (or the previous, shared ; stack frame). ; 2. Load arguments into temporary variables LDVR 5 STV tmp1 LDVR 4 STV tmp2 ; 3. Add arguments and put result into return value 1 LDV tmp1 ADD tmp2 STVR 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 STVR 2 ; 5. Restore shared stack frame LDFP ADC 1 STSP LDVR 0 STFP ; And this function is done :) RET