This document describes the conventions to use when writing SM213 code.
r5 is used for the stack pointer, and should not be used for anything else. When calling a function, its value immediately before the call must be the same as after the call returns. The stack is predecrement, postincrement: when pushing a value on the stack, decrease the stack pointer before writing the value, and when popping, increase the stack pointer after reading the value. This means the stack pointer will always point to the last pushed value, the value pushed before that will be at r5+4, the one before that at r5+8, etc.
A call is performed by setting the return address in r6 with gpc, then jumping to the function to be called. Since r6 is a callee-save register, ensure that its value has been saved on the stack (see below). On entry to the callee, r6 will contain the return address, so a return is done with a j 0(r6) instruction. Return only after restoring the stack to the state it was in upon entering the function.
r0-r3 are caller-save registers: This means that you may use them freely in your function, but if you call any other functions, do not assume that they will retain the values they had before the call. These registers are ideal for values that do not need to be preserved across function calls.
r4-r7 are callee-save registers: You may use them in your function, but must save their values on the stack upon entering, and restore their values before returning. This also means that you can depend on their values not changing after making another call from within your function (since that function must save them if it uses them, and your function will have saved them if it uses them, so that your function's caller can also depend on its values being preserved.) Note that although r5 is included in this set for completeness, it is the stack pointer and so should not be used for anything else, as described above. Because a stack is LIFO (last-in first-out), ensure that the you restore registers by popping from the stack in the reverse order to how they were pushed.
If your function returns a value, ensure that value is in r0 before the j 0(r6) that returns to the caller. Similarly, if you call a function that returns a value, its return value will be in r0 after the jump that called it.
Parameters are passed on the stack and should be present in right-to-left order, pushed immediately before making the call. Thus, immediately after entering the callee, the first (leftmost) parameter will be at 0(r5), the second at 4(r5), etc. Since these are relative to the stack pointer, if you push any other values in the callee, the offsets to the parameters will increase appropriately. The caller is responsible for removing the parameters from the stack after the call returns. Note that in some cases you can reuse the area allocated for parameters by simply storing the new values there, without needing to move the stack pointer up and down unnecessarily (as long as the stack is restored to the state it was before returning).
Here is an example of the C code and corresponding Asm that illustrates this:
int bar(int a, int b, int c) { return a + b + c; } int foo(int a) { return bar(1, 2, 3) + bar(a, 3, 4); }
bar: ld 0(r5), r0 # r0 = a ld 4(r5), r1 # r1 = b add r1, r0 # r0 = a + b ld 8(r5), r1 # r1 = c add r1, r0 # r0 = a + b + c j 0(r6) # return foo: deca r5 st r6, 0(r5) # save return address deca r5 st r4, 0(r5) # save r4 ld $3, r0 deca r5 st r0, 0(r5) # push 3 dec r0 # r0 = 2 deca r5 st r0, 0(r5) # push 2 dec r0 # r0 = 1 deca r5 st r0, 0(r5) # push 1 gpc $6, r6 j bar # call bar mov r0, r4 # r4 = bar(1, 2, 3) ld $4, r0 # r0 = 4 st r0, 8(r5) # write parameter 4 (reuse parameter area) dec r0 # r0 = 3 st r0, 4(r5) # write parameter 3 ld 20(r5), r0 # get parameter a (note offset: 20 = 3*4 parameter bytes + 2*4 saved registers) st r0, 0(r5) # write parameter a gpc $6, r6 j bar # call bar add r4, r0 # r0 = bar(1, 2, 3) + bar(a, 3, 4) inca r5 inca r5 inca r5 # discard parameters ld 0(r5), r4 # restore r4 inca r5 ld 0(r5), r6 # restore r6 inca r5 j 0(r6) # return