Tutorial on Procedure Calls and how the Stack is Managed. Consider the following typical procedure: procA: $s0 = ... # some local variable $s1 = ... # some local variable $t0 = ... # some temp variable $t1 = ... # some temp variable $a0 = ... # setting up the first argument for the procedure call $a1 = ... # setting up the second argument for the procedure call jal procB # jump to procedure procB ... = $v0 # use the procedure's return value ... = $s0 # use the local variable ... = $t0 # use the temp variable ... = $a0 # use your original input argument $a0 jr $ra # the end of procA procB: $s0 = ... # some local variable $t0 = ... # some temp variable $v0 = ... # set up the return value jr $ra # the end of procB In the example above, procA is the caller and procB is the callee. procA begins by storing some results in registers and then calls procB. Before calling procB, it fills in the appropriate arguments into registers $a0-$a3. The jal instruction will cause a jump to the start of procedure procB. This instruction also stores the (current instruction address + 4) into the register $ra. This allows procB to know where it should jump back to after completing its instructions. Note that procB ends with the instruction "jr $ra" which causes a jump to the address stored in register $ra (this instruction is the one immediately after the jal instruction in procA). When procB begins executing, it'll access registers $a0-$a3 when it needs its arguments. Before returning, it stores the expected return value into register $v0. In addition to this exchange of values between procedures (through registers $a0-$a3, $v0, $ra), we must also be careful to save register values in memory so each procedure does not lose its register values when other procedures are called. In the example above, note that procA has stored some results in $s0 and $t0, which it accesses after the call to procB. Also note that procB writes something into $s0 and $t0. This will over-write the prior contents in these registers -- when procA reads $s0 and $t0 after the call to procB, it'll find unexpected data in these registers. Hence, these values must be saved on the stack while initiating the new procedure and restored from stack while wrapping up the procedure. The stack is a reserved space in memory that grows when a procedure is called and shrinks when a procedure completes. We'll adopt the following popular convention: values in $t0-$t9 are saved by the caller and values in $s0-$s7 are saved by the callee. In the example above, the caller realizes that it will need $t0 later, so it saves $t0 in the stack. It does not bother saving $t1 because it no longer needs $t1. Similarly, when procB begins executing, it first saves $s0 on stack because it knows it'll over-write the contents in $s0 and it expects that some caller still cares about this value. The same convention is also adopted while restoring values back from stack into registers. In addition, procA has to save $ra into stack because as soon as the jal instruction is invoked, a new return address is written into $ra. Further, before procA puts new arguments into $a0-$a3, procA must also save the values in $a0-$a3 if it's going to need them later. In the example above, only $a0 is being used later, so only $a0 is saved on the stack. The frame pointer points to the start of the stack space for each procedure. Before a procedure begins, its frame pointer is set to the current value of the stack pointer. Again, the old value must be saved on stack before the frame pointer is over-written. The code above is therefore transformed into the following: procA: $s0 = ... # some local variable $s1 = ... # some local variable $t0 = ... # some temp variable $t1 = ... # some temp variable $sp = $sp - 16 # create space for 4 values on the stack. # since the stack grows from a high address to a # low address, an increase in stack size corresponds # to a decrease in the stack pointer value sw $a0, 12($sp) # store the result in $a0 into the memory address # indicated by $sp+16 sw $ra, 8($sp) # save the second value on stack sw $t0, 4($sp) # save the third value on stack sw $fp, 0($sp) # save the fourth value on stack $fp = $sp # set the frame pointer to the stack pointer $a0 = ... # setting up the first argument for the procedure call $a1 = ... # setting up the second argument for the procedure call jal procB # jump to procedure procB lw $fp, 0($sp) # start restoring values from stack lw $t0, 4($sp) lw $ra, 8($sp) lw $a0, 12($sp) $sp = $sp + 16 # decrement the size of the stack ... = $v0 # use the procedure's return value ... = $s0 # use the local variable ... = $t0 # use the temp variable ... = $a0 # use your original input argument $a0 jr $ra # the end of procA procB: $sp = $sp - 4 # create space on the stack for one save sw $s0, 0($sp) # save the value in $s0 $s0 = ... # some local variable $t0 = ... # some temp variable $v0 = ... # set up the return value lw $s0, 0($sp) # restore the value of $s0 from the stack $sp = $sp + 4 # decrement the stack size jr $ra # the end of procB If a procedure uses many values that cannot all be accommodated in registers, it'll save some of these results on the stack. This space may be allocated at the start of the procedure or on a need basis when the program runs out of registers. Note that $fp is used because it is a stable pointer to the start of the activation record (easy to offset all local variables from $fp). The caller is responsible for saving/restoring $fp. The caller then sets the new $fp before the jal. As a performance optimization, $fp may not be used for small procedures. The caller could avoid the $fp save/restore if $fp is not used by the caller. Further, if more than 4 arguments need to be passed, they can be placed by the caller at the start of the activation record (after setting up the new value of $fp for the callee). The MARS simulator implements system calls in a manner that may be confusing. When making a system call for print_integer, it must pass two arguments to the syscall procedure. The first argument specifies the type of syscall -- in this case, an argument value of 1 specifies that we want to invoke the print_integer function. The second argument specifies the integer that we want printed. Not surprisingly, the second argument is placed in $a0. However, the first argument is placed in $v0. Be aware of this quirk because $v0 is usually reserved for placing the return value of a procedure.