Professional Documents
Culture Documents
2011
Different operating systems use this instruction in different ways. For the SPIM exception handler
it is used like this:
li $v0,code
.......
.......
syscall
The syscall instruction causes an exception, which transfers control to an exception handler. An
exception handler is the part of an operating system that receives requests for operating system
services. Different services expect data in different registers, and not all services return values to the
caller.
Code in $v0
Arguments
Returned Value
print integer
$a0 == integer
$f12 == single
print double
print string
read integer
read double
read string
allocate memory
exit
10
Here is an example of how to use a service. Load register $v0 with the code for the exit service and
then use the syscall instruction. The exit service stops the program. (Until now we have been single
stepping the program or crashing into the bytes beyond the end of the program).
li
$v0,10
syscall
# code 10 == exit
# Return control to the
# operating system.
The exit service is normally used when the program is finished. A real computer is always running,
and control has to go somewhere, so this is how control is sent back to the OS. The OS then goes
about its own tasks, possibly starting another user program. (On SPIM, the exit service stops all
execution.)
Print String
The print string SPIM exception handler service prints a null terminated string to the simulated
monitor. The address of the string is loaded into register $a0. Typically the string is in the data
section of memory.
.data
string: .asciiz
"Hello SPIM!\n"
.text
.globl main
main: li
$v0,4
la
$a0,string
syscall
li
$v0,10
syscall
The print string service sends bytes to the simulated monitor one by one starting with the byte
pointed to by $a0. It continues until it hits a null byte. It does not check that the bytes are ASCII.
You will print garbage if you point $a0 at the wrong location.
If you want to advance to a new line, use the newline character '\n' inside or at the end of the string.
The string is printed to the monitor window of the simulator.
Read Integer, Print Integer
The read integer service reads an entire line of input from your keyboardall the characters you
type up to the newline character. These characters are expected to be ASCII digits '0', '1', .., '9' with
an optional leading '-' or '+'. The characters are converted into a 32-bit two's complement
representation of the integer which is returned in $v0.
li
$v0,5
syscall
The print integer service prints the integer represented by the 32 bits in $a0 to the SPIM terminal.
Of course, there are many ways that the integer can be placed in $a0, not just lw.
li
$v0,1
# code 1 == print integer
lw
$a0,int # $a0 == the integer
syscall
# Invoke the operating system.
# Convert the 32-bit integer into characters.
# Print the character to the monitor.
The following example program reads in an integer, presumed to be a number of ounces, then writes
out the equivalent number of pounds and ounces. Write this program and save it as lab31.asm.
# ounces.asm
#
# Convert ounces to pounds and ounces.
.data
prompt: .asciiz "Enter ounces: "
pout:
.asciiz " Pounds\n"
ozout:
.asciiz " Ounces\n"
.text
.globl main
main:
li
$t1,16
divu $v0,$t1
mflo $a0
li
$v0,1
syscall
# print
# pounds
li
$v0,4
la
$a0,pout
syscall
# print "pounds"
mfhi $a0
li
$v0,1
syscall
# print
# ounces
#
li
$v0,4
la
$a0,ozout
syscall
# print
# "ounces"
# read in ounces
# restore now the return address in $ra and return from main
addu $ra, $0, $s0 # return address back in $31
jr $ra
# return from main
# end of file
Exercise
Write a program that prompts the user for the number of miles traveled and the gallons of gasoline
consumed, and then prints out the miles per gallon. Use integer math.
main:
j subroutine
return:
call
subroutine:
return
j return
Figure 1. Subroutine
main:
j subroutine
return:
j subroutine
????:
j subroutine
????:
subroutine:
j return
# $ra <-- PC+4 $ra <-- address 8 bytes away from the jal
# PC <-- sub load the PC with the subroutine entry point
Very Tricky: the middle step of the machine cycle has already incremented the PC by four. At this
point the PC holds the address of the instruction just after the jal instruction. Now the execute phase
of the jal instruction adds four to that address and puts the result in $ra. So now $ra holds the address
of the second instruction after the jal instruction.
The correct return address is "address of the jal plus eight". This is because: (i) returning from the
subroutine to the jal instruction would be a disaster (since it would execute again, sending control
back to the subroutine), and (ii) the instruction following the jal is a branch delay slot.
Example jal Instruction
It would not be a disaster to return control to an instruction
that does nothing. But sometimes clever programmers or
clever compilers put something unexpected in the branch
delay slot, so it is best not to pass control to it.
The diagram shows the execution of a jal instruction. The jal
is at address 0x00400014. The return address is
0x0040001C which is the address of the jal plus eight. (The
addu instruction there is just used as an example of what
might be at the return address).
Here is how the jal instruction works in general:
jal sub
# $ra <-- PC+4 $ra <-- address 8 bytes away from the jal
# PC <-- sub load the PC with the subroutine entry point
Here is how it works in this example. The entry point of sub is 0x00400100.
Fetch:
Increment:
# PC <-- $ra
Figure 3. shows the subroutine returning to the return address that was loaded into $ra by the jal
instruction in the caller.
Calling Convention
Figure 4. shows the subroutine sub being called from three
different points in the main routine. Each time the subroutine
is called it returns to the appropriate return address.
A calling convention is an agreement about how subroutines
are called and how control is returned to the caller. Mostly (as
we will later see) this is an agreement about how software
will work. In processors (like MIPS) that support subroutines,
the convention says how those support features are used.
Different languages and different operating systems for the
same processor usually have different calling conventions. A
"C" program compiled for Linux on a MIPS processor will
not work correctly with Irix on a MIPS processor. This is
largely because the calling conventions are different. These
sections discuss several calling conventions that show what
calling conventions are about, but are not the calling
convention of any particular operating system.
Register Use
To answer the question, look at the code for main in Figure 4. to determine which registers contain
information and can not be altered. Do this for each call to sub, because registers are likely to hold
different information at different points. This is tedious and error prone. Worse, if main is altered,
now you have to inspect the registers again, and possibly re-code sub.
One of the goals of subroutines is to create a module that is independent of the rest of the code. We
have not achieved that, yet.
Another issue is how data is passed into and out of the subroutine. Often data is in registers, and the
results are in registers. Which registers?
By agreement between programmers (not by hardware) registers have been assigned different roles
with subroutine linkage:
$t0 - $t9 The subroutine is free to change these registers.
$s0 - $s7 The subroutine must not change these registers.
$a0 - $a3 These registers contain arguments for the subroutine. The subroutine can change
them.
$v0 - $v1 These registers contain values returned from the subroutine.
Simple Linkage
Here is an example of a calling convention. This convention is very simple and is not suitable for
any serious program. But it illustrates some ideas that will be used later on in more complex
conventions. Let us call it the Simple Linkage convention. Most of the rules of this convention you
have already seen:
1. A subroutine is called using jal.
2. A subroutine will NOT call another subroutine.
3. The subroutine returns to its caller using jr $ra.
4. Registers are used as follows:
a. $t0 - $t9
The subroutine is free to change these registers.
b. $s0 - $s7
The subroutine must not change these registers.
c. $a0 - $a3
These registers contain arguments for the subroutine. The subroutine can
change them.
d. $v0 - $v1
These registers contain values returned from the subroutine.
5. The main routine returns control by using the exit service (service 10) of the SPIM exception
handler.
Since a subroutine may not call another subroutine (in this Simple Linkage Convention) programs
will consist of a main routine that calls any number of subroutines. But the subroutines do not call
other subroutines and always return directly to main.
Figure 5. shows main calling
mySub. Two arguments are
passed, in $a0 and $a1. The
subroutine rea ds t h e
arguments from those
registers.
In Figure 5, the arguments
are set up with move and li
instructions, but any means
of loading the argument
registers can be used.
The Si mpl e L i n k a g e
Convention is limited in
some obvious ways. A more
advanced calling convention is beyond this class. The caller and the subroutine should communicate
only through arguments and return values all contained in registers. The code for the subroutine
should not use any symbolic address defined in the caller.
Figure 5. Calling Convention summary
Example Program
Let us write a program that uses the Simple Linkage convention. The program is to read three
integers from the user and compute the sum. The outline of the program is:
# read an integer (x)
# compute x2
# write out the result
Of course, the user will enter integers as characters from the keyboard. The program uses the
exception handler service number five to read the characters and convert then to a full word.
# square --- read an integer and print its square
#
# This program uses simple linkage.
#
# Settings: Load delays ON; Branch delays ON
#
Trap file ON; Pseudoinstructions ON
#
.data 0x10010000
prompt:
.asciiz "Enter an integer: "
.text
.globl main
main:
la $a0,prompt
li $v0,4
syscall
# print string
# service 4
li $v0,5
syscall
# move x to $a0
# because square expect x in $a0
jal square
nop
move $a0,$v0
# compute x^2
#
# save it in $a0
li
$v0,1
syscall
# restore now the return address in $ra and return from main
addu $ra, $0, $s0
# return address back in $31
jr $ra
# return from main
#############################################################
# square -- compute x^2
#
# on entry:
# $ra -- return address
# $a0 -- x
#
# on exit:
# $v0 -- x^2
.text
.globl square
square:
mult $a0, $a0
mflo $v0
# compute x^2
# move result to $v0
jr $ra
nop
# return
#