SPIM S20: A MIPS R2000 Simulator

1 “ 25 th

the performance at none of the cost”

James R. Larus larus@cs.wisc.edu Computer Sciences Department University of Wisconsin–Madison 1210 West Dayton Street Madison, WI 53706, USA 608-262-9519 Modified by Ingrid Zukerman Faculty of Information Technology Monash University Clayton, VICTORIA 3800

Copyright c 1990–1993 by James R. Larus (This document may be copied without royalties, so long as this copyright notice remains on it.) April 12, 1993 (Revision 11 corresponding to SPIM Version 5.1)


Description of the MIPS R2000

MIPS has a RISC (Reduced Instruction Set Computer) architecture. A MIPS processor consists of an integer processing unit (the CPU) and two coprocessors (Figure 1). Coprocessor 0 handles exceptions and the memory system. Coprocessor 1 is the floating point unit. The processor contains 32 general-purpose registers and several special-purpose registers. MIPS is a load/store architecture, which means that only load and store instructions access memory. Computation instructions operate only on values in registers.


Memory Usage

A program’s address space is composed of three parts (Figure 2). • At the beginning of the user address space (0x00001000) is the text segment, which holds the instructions for a program. • After the text segment is the data segment (starting at 0x00002000), which is divided into two parts. – Static data – objects whose size and address are known to the compiler and linker. – Dynamic data – objects for which space is allocated at run time. • The stack segment resides at the end of the address space (0x00008000). It grows towards the data segment.



PC $0 Registers ... $31 Multiply Divide Lo Hi

Coprocessor 1 (FPU)
$0 Registers ... $31 Arithmetic FP Unit


Coprocessor 0 (exceptions and memory)

Figure 1: MIPS R2000 CPU and FPU.


CPU Registers

The MIPS central processing unit contains 32 general purpose registers that are numbered 0– 31. Register n is designated by $n. Register $0 always contains the hardwired value 0. MIPS has established a set of conventions as to how registers should be used. These suggestions are guidelines, which are not enforced by the hardware. Table 1 lists the registers and describes their intended use. A register may be addressed either by its name or by its number. • Registers $at (1), $k0 (26), and $k1 (27) are reserved for use by the assembler and operating system. • Registers $a0–$a3 (4–7) are used to pass the first four arguments to routines (remaining arguments are passed on the stack). • Registers $v0 and $v1 (2, 3) are used to return values from functions. • Registers $t0–$t9 (8–15, 24, 25) are caller-saved registers used for temporary quantities that do not need to be preserved across calls. • Registers $s0–$s7 (16–23) are callee-saved registers that hold long-lived values that should be preserved across calls.


(0 K) (4 K)

0x00000000 0x00001000


Text Segment (8 K) 0x00002000 Data Segment

(16 K)


Stack Segment

(32 K)


Figure 2: Layout of memory. • Register $sp (29) holds the stack pointer, which points to the last used location on the stack. • Register $fp (30) holds the frame pointer. • Register $ra (31) holds the return address for a call made by the jal instruction. • Register $gp (28) holds a global pointer that points into the middle of an 8K block of memory that holds constants and global variables.


Calling Convention

A stack frame consists of the memory starting at the arguments passed to a callee (arg n ) and ending at the stack pointer ($sp) (Figure 3). As typical of Unix systems, the stack grows down from higher memory addresses, so the frame pointer points to a memory address that is higher than or equal to the address pointed to by the stack pointer. The following steps are necessary to effect a call: 1. Save the registers the caller wants preserved across the call. By convention, this may include registers $t0–$t9 and $s0–$s7. 2. Pass the arguments. By convention, the first four arguments are passed in registers $a0– $a3. However, space is reserved on the stack for these arguments. The contents of these registers are put on the stack later on if the callee needs to change their value. The remaining arguments are pushed on the stack. 3. Execute a jal instruction. The stack resulting from these steps appears in Figure 3(b). Within the called routine, the following steps are necessary: 1. Push the return address in register $ra on the stack. 3

(a) Stack prior to saving registers
and pushing parameters onto stack

(b) Stack prior to procedure entry
Parameters pushed onto stack Address 0

(c) Stack after procedure entry
New frame established Address 0

Figure 3: Layout of a stack frame. The frame pointer points to the old frame pointer saved on the stack. The stack pointer points to the last word in the frame.

Address 0

first local variable ... last local variable old frame pointer

$fp $sp
arg 1 arg 2

New Stack frame

return address arg 1 arg 2 ... arg n-1 arg n saved register x

New Stack frame

... arg n-1 arg n saved register x ...


$sp local variables Old Stack frame . . .
original frame pointer return address ...

Old Stack frame

saved register y

saved regs to restore on return Old Stack frame

... saved register y

saved regs to restore on return

local variables
original frame pointer

old local variables


original frame pointer

return address return address

Stack start

Stack start

. . .


Stack start

. . .


Register Name zero at v0 v1 a0 a1 a2 a3 t0 t1 t2 t3 t4 t5 t6 t7 s0 s1 s2 s3 s4 s5 s6 s7 t8 t9 k0 k1 gp sp fp ra

Number 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

Usage Constant 0 Reserved for assembler Expression evaluation and results of a function Argument 1 Argument 2 Argument 3 Argument 4 Temporary (not preserved across call) Temporary (not preserved across call) Temporary (not preserved across call) Temporary (not preserved across call) Temporary (not preserved across call) Temporary (not preserved across call) Temporary (not preserved across call) Temporary (not preserved across call) Saved temporary (preserved across call) Saved temporary (preserved across call) Saved temporary (preserved across call) Saved temporary (preserved across call) Saved temporary (preserved across call) Saved temporary (preserved across call) Saved temporary (preserved across call) Saved temporary (preserved across call) Temporary (not preserved across call) Temporary (not preserved across call) Reserved for OS kernel Reserved for OS kernel Pointer to global area Stack pointer Frame pointer Return address (used by function call)

Table 1: MIPS registers and the convention governing their use. 2. Push the old frame pointer (currently in register $fp) on the stack. 3. Assign the current top-of-stack address to the frame pointer (in register $fp). 4. Allocate on the stack space for local variables by subtracting the number of words they require from the stack pointer, and assigning the result to register $sp. The stack resulting from these steps appears in Figure 3(c). Finally, to return from a call, a function executes the following steps: 1. Place the returned value into $v0 (and $v1 if necessary). 2. Restore to register $ra the return address which is stored in memory location $fp + 4. 3. Pop the stack by assigning $fp + 8 to register $sp. This points the stack pointer to the memory location of arg1 . 4. Restore the frame pointer in register $fp by assigning to it the old value of the frame pointer, which resides in the memory location stored in register $fp.


5. Return by jumping to the address in register $ra. Now we have returned to the calling program, which performs the following steps: 1. Pop the arguments off the stack by making the stack pointer point to the last saved register. 2. Restore any registers that were saved upon entry, and pop the stack to the original position. The stack resulting from these steps appears in Figure 3(a).



SPIM S20 is a simulator that runs programs for the MIPS R2000/R3000 RISC. 1 SPIM can read and immediately execute files containing assembly language or MIPS executable files. SPIM is a self-contained system for running these programs and contains a debugger and interface to a few operating system services. You can download the SPIM simulator from: http://www.cs.wisc.edu/~larus/spim.html


Why Use a Simulator?

An obvious question is: why use a simulator when many people have workstations that contain a hardware, and hence significantly faster, implementation of this computer? One reason is that these workstations are not generally available. Another reason is that these machines will not persist for many years because of the rapid progress leading to new and faster computers. Unfortunately, the trend is to make computers faster by executing several instructions concurrently, which makes their architecture more difficult to understand and program. The MIPS architecture represents a simple, clean RISC machine. In addition, simulators can provide a better environment for low-level programming than an actual machine because they can detect more errors and provide more features than an actual computer. For example, SPIM has an X-window interface that is better than most debuggers for the actual machines. Finally, simulators are a useful tool for studying computers and the programs that run on them. Because they are implemented in software, not silicon, they can be easily modified to add new instructions, build new systems such as multiprocessors, or simply to collect data.


SPIM Interface

To run SPIM in Linux you need to simply type xspim; in Windows click on PCSpim. The Linux version of SPIM has five panes (Figure 4). The Windows version has four panes; the control buttons in the second Linux pane described below appear in a drop-down menu from the Simulator label (top of the window). The top pane displays the contents of the registers. It is continually updated, except while a program is running. The next pane in the Linux version contains the buttons that control the simulator (dropdown menu in Windows): quit Exit from the simulator. load Read an assembly language file into memory.
1 For a description of the real machines, see Gerry Kane and Joe Heinrich, MIPS RISC Architecture, Prentice Hall, 1992.


Figure 4: X Windows interface to SPIM.


reload Reinitialize memory and registers, and read the previously loaded file into memory. run Start the program running. step Step through a program (each step may be composed of one or more instructions). clear Reinitialize registers or memory. set value Set the value in a register or memory location. print Print the value in a register or memory location. breakpoint Set or delete a breakpoint or list all breakpoints. help Print a help message. terminal Raise or hide the console window. mode Set SPIM operating modes. The quiet mode does not report exceptions. The bare mode does not provide the additional addressing modes provided by the assembler (Table 3). The next two panes display the memory contents. The top one shows instructions from the user and kernel text segments.2 The first few instructions in the text segment are startup code. The lower of these two panes displays the data and stack segments. Both panes are updated as a program executes. The bottom pane is used to display messages from the simulator. It does not display output from an executing program. When a program reads or writes, its I/O appears in a separate window, called Console, which pops up when needed.


Assembler Syntax

Comments in assembler files begin with a sharp-sign (#). Everything from the sharp-sign to the end of the line is ignored. Identifiers are a sequence of alphanumeric characters, underbars ( ) and dots (.) that do not begin with a number. Operation codes (opcodes) for instructions are reserved words that are not valid identifiers (the Appendix has a list of these opcodes). Labels are declared by putting them at the beginning of a line followed by a colon. The code is divided into two segments .data and .text. The first instruction in the .text segment must be labelled main.

Each source instruction appears as a comment on the first instruction to which it is translated.


Service print int read int exit

Call Code 1 5 10

Arguments $a0 = integer

Result integer (in $v0)

Table 2: System services. Example: .data item: .word 1 .text main: lw $t0, item # # # # indicates the beginning of the data stores the number 1 in a memory word labelled "item" indicates the beginning of the program load the word in item into register $t0

SPIM supports a subset of the assembler directives provided by the MIPS assembler: .asciiz str Store the string in memory and null-terminate it. .byte b1, ..., bn Store the n values in successive bytes of memory. .data <addr> The following data items should be stored in the data segment. If the optional argument addr is present, the items are stored beginning at address addr . .space n Allocate n bytes of space in the current segment (which must be the data segment in SPIM). .text <addr> The next items are put in the user text segment. In SPIM, these items may only be instructions or words (see the .word directive below). If the optional argument addr is present, the items are stored beginning at address addr . .word w1, ..., wn Store the n 32-bit quantities in successive memory words.


System Calls

SPIM provides a small set of operating-system-like services through the system call (syscall) instruction. To request a service 1. put the system call code (Table 2) in register $v0, and 2. put the argument(s) in register $a0, and $a1 if necessary. System calls that return values put their result in register $v0. print int is passed an integer (in $a0) and prints it on the console. read int reads an entire line of input up to and including the newline. Characters following the number are ignored. The result is placed in $v0. exit stops a program from running. 9


Very Reduced Instruction Set

In all the instructions below, Rsrci is a register, and Imm an immediate value (a 16 bit integer).


Arithmetic Instructions

add Rdest, Rsrc1, Rsrc2 Addition (with overflow) addi Rdest, Rsrc1, Imm Addition Immediate (with overflow) Put the sum of the integers from register Rsrc1 and Rsrc2 (or Imm) into register Rdest. div Rsrc1, Rsrc2 Divide (with overflow) Divide the contents of register Rsrc1 by those of Rsrc2. Leave the quotient in register Lo and the remainder in register Hi. If an operand is negative, the result is unspecified in SPIM, and depends on the machine on which SPIM is run. mult Rsrc1, Rsrc2 Multiply Multiply the contents of the two registers. Leave the low-order word of the product in register Lo and the high-order word in register Hi. sub Rdest, Rsrc1, Rsrc2 Subtract (with overflow) Put the difference of the integers from register Rsrc1 and Rsrc2 into register Rdest.


Data Movement Instructions

The multiply and divide instructions put their result in two additional registers, Hi and Lo. The following instructions move values from these registers. mfhi Rdest mflo Rdest Move the contents of the Hi (Lo) register to register Rdest. Move From Hi Move From Lo


Logical Instructions

and Rdest, Rsrc1, Rsrc2 AND andi Rdest, Rsrc1, Imm AND Immediate Put the logical AND of the integers from register Rsrc1 and Rsrc2 (or Imm) into register Rdest. nor Rdest, Rsrc1, Rsrc2 Put the logical NOR of the integers from register Rsrc1 and Rsrc2 into register Rdest. NOR

or Rdest, Rsrc1, Rsrc2 OR ori Rdest, Rsrc1, Imm OR Immediate Put the logical OR of the integers from register Rsrc1 and Rsrc2 (or Imm) into register Rdest. xor Rdest, Rsrc1, Rsrc2 XOR xori Rdest, Rsrc1, Imm XOR Immediate Put the logical XOR of the integers from register Rsrc1 and Rsrc2 (or Imm) into register Rdest.


Format (register) Imm [−]Imm(register) symbol symbol+[−]Imm symbol+[−]Imm(register)

Address Computation contents of register immediate (actual value) [−]immediate + contents of register address of symbol address of symbol + [−]immediate { address of symbol + contents of register } + [−]immediate

Table 3: Addressing modes.


Shift Instructions

sll Rdest, Rsrc1, Imm Shift Left Logical sllv Rdest, Rsrc1, Rsrc2 Shift Left Logical Variable sra Rdest, Rsrc1, Imm Shift Right Arithmetic srav Rdest, Rsrc1, Rsrc2 Shift Right Arithmetic Variable srl Rdest, Rsrc1, Imm Shift Right Logical srlv Rdest, Rsrc1, Rsrc2 Shift Right Logical Variable Shift the contents of register Rsrc1 left (right) by the distance indicated by Imm (the contents of Rsrc2) and put the result in register Rdest. The arithmetic shift replicates the sign, while the logical shift replaces it with 0.


Load and Store Instructions

The machine provides the addressing modes in Table 3 for load and store instructions: lw Rdest, address Load the 32-bit quantity (word) at address into register Rdest. sw Rsrc, address Store the word from register Rsrc into address. Load Word Store Word


Comparison Instructions

slt Rdest, Rsrc1, Rsrc2 Set Less Than slti Rdest, Rsrc1, Imm Set Less Than Immediate Set register Rdest to 1 if register Rsrc1 is less than Rsrc2 (or Imm) and to 0 otherwise.


Control Transfer Instructions

Branch instructions use a signed 16-bit offset field; hence they can jump 2 15 −1 instructions (not bytes) forward or 215 instructions backwards. The jump instruction contains a 26 bit address field. beq Rsrc1, Rsrc2, label Branch on Equal Conditionally branch to the instruction at label if the contents of register Rsrc1 equals the contents of register Rsrc2. bne Rsrc1, Rsrc2, label Branch on Not Equal Conditionally branch to the instruction at label if the contents of register Rsrc1 are not equal to the contents of register Rsrc2. 11

j label Unconditionally jump to the instruction at label.


jal label Jump and Link Unconditionally jump to the instruction at label. Save the address of the next instruction in register $ra (31). jr Rsrc Unconditionally jump to the instruction whose address is in register Rsrc. Jump Register


System Calls

syscall System Call Register $v0 contains the number of the system call provided by SPIM (Table 2).



Instructions that expand to short sequences of MIPS instructions. For example, the pseudoinstruction blt $t2, $t3, label (branch-less-than) is expanded to: slt $t1, $t2, $t3 bne $t1, $0, label


Appendix: Opcodes (Cannot be Used as Labels)
Common Opcodes .align .end add b bgezal blt div la lui mul or sd sle sltu sub usw .ascii .extern addi bal bgt bltu divu lb lw mult ori seq sleu sne subu xor .asciiz .float addiu beq bgtu bltz j lbu lwl neg rem sge sll sra sw xori .data .globl addu bge ble bne jal ld lwr negu remu sgt slt srav swl .byte .text and bgeu bleu bnez jalr lh mfhi nop rol sgtu slti srl swr .double .word andi bgez blez break jr lhu mflo nor sb sh sltiu srlv syscall

Unusual Opcodes .asm0 .fmask .livereg .sdata abs bc1t c.eq.s c.nge.d c.ngt.s c.sf.d c.ult.s cop0 ctc3 div.d lwc3 mov.d mtc3 mulou sub.d tlbp ush .bgnb .frame .loc .set abs.s bc2f c.f.d c.nge.s c.ole.d c.sf.s c.un.d cop1 cvt.d.s l.d mfc0 mov.s mthi neg.d sub.s tlbr .endb .kdata .noalias .struct add.d bc2t c.le.d c.ngl.s c.olt.d c.ueq.s cfc0 cop3 cvt.s.d li.d mfc1 mtc0 mtlo neg.s swc0 tlbwi .endr .ktext .option .verstamp add.s bc3f c.le.s c.ngle.d c.olt.s c.ule.d cfc1 ctc0 cvt.s.w li.s mfc1.d mtc1 mul.d rfe swc1 tlbwr .ent .lab .rdata .vreg bc0f bc3t c.lt.d c.ngle.s c.seq.d c.ule.s cfc2 ctc1 cvt.w.d lwc0 mfc2 mtc1.d mul.s s.d swc2 ulh .file .lcomm .repeat bc0t c.eq.d c.lt.s c.ngt.d c.seq.s c.ult.d cfc3 ctc2 cvt.w.s lwc1 mfc3 mtc2 mulo s.s swc3 ulhu