When we call a function (procedure, subroutine), we normally want to come back to the place we left, the following instruction. To do so, we need a means of remembering our place, so the function can return to it. Some convention needs to be agreed upon as to where to store a return address. Some conventions that are in use are:
MIPS uses register 31 as the return address register
We have actually seen the jump instructions involved in function call and return, they are:
As an example, we could have a simple function to print an integer, that takes care of the detail of remembering the system call number, and another to print an end-of-line character: (I've added extra labels just for the trace below)
print_int: li $v0, 1
p: syscall
q: jr $ra #PC := $ra (c: or f:)
endline:
la $a0, endl #define in .data as "\n"
x: li $v0, 4
y: syscall
z: jr $ra #PC := $ra (d: or g: )
These can then be called to output some numbers
a: li $a0, 42 # writeln (42) b: jal print_int # $ra := c: and PC := print_int: c: jal endline # $ra := d: d: li $a0, 1999 # writeln (1999) e: jal print_int # $ra := f: f: jal endline # $ra := g: g:so the sequence of execution is
a: li $a0, 42
b: jal print_int #$ra == c:
print_int:li $v0, 1
p: syscall
q: jr $ra
c: jal endline #$ra := d:
endline: la $a0, endl
x: li $v0, 4
y: syscall
z: jr $ra PC := d:
d: ...
This suffices for a main program calling functions sequentially. However, it does not allow for a function to call another function, so later on we will see how to save return addresses systematically, using a stack.
A full length example based on Wednesday's class is on the file
sums.asm
It includes a function sum, and another, printsum, each called twice.
sumshex.asm
is a version that prints the sums in hexadecimal instead, using instructions
we will encounter in chapter 7, shift and logic instructions.