The stack is just a piece of memory where you can
push values on the end or
pop values from the end. The $sp register always points to the
next available spot.
Visually:
[ ]
[ ]
[ ] <-- $sp
[-7]
[12]
If we push 42 onto the stack, we get:
[ ]
[ ] <-- $sp
[42]
[-7]
[12]
Popping a value works in reverse.
MIPS doesn't have any "push" or "pop" commands, you just use
lw and
sw and directly add or subtract to change the $sp value.
Every procedure/function will look similar:
proc_name: sub $sp, $sp, 4 # push the return address...
sw $ra, 4($sp) # ...on the stack
# (do the subroutine stuff here)
# (stick any return values in $v0 (and $v1 if needed))
lw $ra, 4($sp) # pop the return address...
add $sp, $sp, 4 # ...off the stack
jr $ra
Because register 31 ($ra) is preserved on the stack during the function, you can easily call other functions, or even recurse without worry. To call the function, just do the usual:
jal proc_name
What follows is for your edification, but I don't think you'll need either to do your assignment:
Passing arguments
In MIPS, registers $a0..$a3 are expected to be used as arguments to functions. This is not entirely necessary, just convenient. You could push arguments onto the stack before calling the function as well. If you have more than four arguments, you'll need to push the extra ones anyway. For example, say we want to push $s0 and $s4 as arguments:
sub $sp, $sp, 8
sw $s0, 8($sp)
sw $s4, 4($sp)
jal my_proc
The order in which arguments are pushed is up to you, but on MIPS machines it is usually higher addresses first, lower addresses last (in this example, $s0 is argument 1 and $s4 is argument 2).
Also, whether your subroutine removes parameters from the stack or whether it is left to the caller is up to you. (C, for example, expects the caller to remove parameters, whereas Pascal expects the routine to remove the parameters.)
Local variables
You can also store local data on the stack once the routine begins. Once you have started the subroutine and stored the return value on the stack, just subtract more space from the stack for room to keep local variable values. Before you return, restore the stack pointer to its proper state, pop the return address (and maybe parameters if that's how you are doing things) and
jr $sp
to return.
Hope this helps.