; 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 = main 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: main: CALL caller HALT 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 RET 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