In this part we’ll properly cover the MIPS architecture in depth.
Parameter transferring
When calling a function in C code, what is happening at the assembler level is, calling a subroutine (usually).
What we need to do before a subroutine call, is the following:
- Place the input parameters into their respective registers
- Call the function/subroutine
- Allocate memory for eventual saved and local variables
- Execute the function/procedure
- Place the result in a register
- Reset the stack, if needed
- Return to where the function/subroutine got called.
Register conventions
Since we now need to store our input parameters into registers before we call a function, we need to create a convention to where we expect things to be.
MIPS has this following convention:
- –: Input parameters
- : Result values
- –: Temporary variables
- –: “Saved” registers
- Must be saved and reset by each respective subroutine.
- : global pointer
- : stack pointer
- : frame pointer
- : return address
We discussed a lot of subroutines, but how do we actually call one?
jal Label will jump to a label (starting point of a subroutine).
To return from a subroutine call jr $ra.
A very simple function like:
int example(int g, int h, int i, int j) { int f; f = (g + h) - (i + j); return f;}Given that the arguments are in , f is in (therefore we must save to the stack before!).
The result will be stored in .
MIPS Code:
example: addi $sp, $sp, -4 # Allocate space for one variable, s_0 sw $s0, 0($sp) # Save s_0
add $t0, $a0, $a1 # (g + h) add $t1, $a2, $a3 # (i + j) sub $s0, $t0, $t1 # f = (g + h) - (i + j)a
add $v0, $s0, $zero # Store value
lw $s0, 0($sp) # Reset s_0 value addi $sp, $sp, 4 # Reset stack pointer
jr $ra # ReturnLet’s now take a recursive
int fact(int n) { if(n < 1) { return 1; } else { return n * fact(n - 1); }}input parameter in , result in .
MIPS:
fact: addi $sp, $sp, -8 # Adjust stack so we can store 2 variables sw $ra, 4($sp) # Save return address sw $a0, 0($sp) # Save input register
slti $t0, $a0, 1 # test for n < 1 beq $t0, $zero, L1 addi $v0, $zero, 1 # If n < 1, result = 1
addi $sp, $sp, 8 # Pop stack jr $ra # Return
L1: addi $a0, $a0, -1 # Decrement n jal fact # Recursive call
lw $a0, 0($sp) # restore orignal n lw $ra, 4($sp) # and return address
addi $sp, $sp, 8 # Pop stack mul $v0, $a0, $v0 # n * fact(n - 1) jr $ra # returnLess than a word
Sometimes we want to only access a byte or half-word:
lb rt, offset(rs)lh rt, offset(rs)
# Unsignedlbu rt, offset(rs)lhu rt, offset(rs)
# Savesb rt, offset(rs)sh rt, offset(rs)Indexing vs Pointers
- Vector indexing requires:
- Multiplication of the index with length
- Summation of this product to the vector’s start address
- Pointer corresponds directly to a memory address
- Therefore, it’s faster
Common mistakes
Common mistakes when writing programs in MIPS are:
- Incrementing with 1 and not 4!
- MIPS uses byte addressing