Unlike the DOS world, the Unix world rarely provides an integrated development environment, such as Turbo C++. Instead, a text editor is used to produce a file which is passed along to another tool. In this case, the second tool is spim, a MIPS R2000 simulator. spim expects to be given a file containing a MIPS R2000 assembly language program. You may use your favorite text editor ( emacs, vi, even pico) to create this file.
spim provides a few amenities over a bare R2000. There is a syscall facility for performing I/O. While not as sophisticated as C I/O, it is superior to using memory mapped I/O (see Section 5 of the SPIM S20 paper). There is also a facility for setting breakpoints, single-stepping a program, and examining the values of registers and memory locations.
The rest of this document is a brief introduction to using SPIM. For additional information, see the article SPIM S20: A MIPS R2000 Simulator.
Consider this skeletal program:
# Program documentation goes here. .data # Constants and variables # declarations. <Constant and variable declarations go here.> .text # Main (must be global). .globl main main: <Your program starts here.> li $v0, 10 # Syscall to exit. syscallNotes:
Everything is nicely aligned in columns. A complete program would be extensively commented, because of the low-level nature of assembly language programs and the difficulty in remembering what variable is currently in what register. Homework problems which does not follow these guidelines will be returned ungraded (i.e., you will receive no credit).
Adequate whitespace must be used to separate related instructions in a block from unrelated surrounding instructions. ( addn.spim is a good example of this.)
First, we run a program straight from the shell. Then we run a program from within spim, so that we have the opportunity of setting breakpoints and single-stepping, etc.
Let's say you have an R2000 program in the file SaveTheWorld.spim. Here's the easiest way to run it:
abacus% spim -file SaveTheWorld.spimspim will load and execute the program. When execution finishes, you'll be returned to the shell.
From the shell, just type spim. This leaves you at the spim prompt:
abacus% spim SPIM Version 5.3 of Aug 30, 1993 Copyright 1990-92 by James R. Larus (firstname.lastname@example.org). All Rights Reserved. See the file README a full copyright notice. (spim)The most important command is help, which prints a summary of all the commands.
To load a program into the simulator, use the load command:
(spim) load "SaveTheWorld.spim"Note that the program name must be enclosed in quotes.
To run the program, use run:
runBy default, execution begins with the statement labeled main.
Consider this program fragment:
.text # Main. .globl main main: li $v0, 4 # Syscall to print prompt string. ... .globl while while: blez $t0, endwhile li $v0, 4 la $a0, prmpt2 syscall li $v0, 5 syscall add $t1, $t1, $v0 # Increase sum by new input. sub $t0, $t0, 1 # Decrement n. b while endwhile: ...If I suspected that I had a problem with my while loop, I would begin debugging it by setting a breakpoint at an appropriate statement, in this case the statement labeled while. Note that I have had to declare while .globl. Breakpoints may only be set at global labels.
After loading the program into memory, I set my breakpoint:
(spim) breakpoint while
When the simulator reaches a statement upon which a breakpoint is set, it suspends execution just before executing the statement. The step command is used to execute one (the default) or more statements at a time. continue is used to resume normal operation. delete label removes breakpoints from the statement labeled label. Hitting the return key at a spim prompt re-executes the last command, so the easiest way to single-step a program is to get to a breakpoint, give a step command, and then start pressing return.
step prints program statements as it executes them. Often, they don't correspond to the assembler program because the simulator exchanges symbolic register names ( $v0) for actual register names ( $2), exchanges symbolic labels with the actual memory address, and, most confusingly, replaces certain assembly language statements with different statements. In some cases, a single statement may even be replaced with multiple statements. This is because the assembly language is a bit richer (more abstract) than the actual R2000 instruction set. For example, the assembler provides a load immediate instruction:
li $v0, 1The instruction set does not provide such an instruction. The assembler must translate this instruction to one that is a part of the R2000 instruction set:
ori $2, $0, 1The assembler translates $v0 to $2 and uses or immediate on register 0 (which is the constant 0) to implement load immediate.
Here's how to print the value of a register:
(spim) print $v0Here's how to print the value of a memory location:
(spim) print iThe assumption here is that i is a global label.
quit will terminate spim, returning you to the shell.
If spim seems to go haywire (it does sometimes, after misbehaved programs have been executed or if you load a second program), use reinitialize to restore it to a state of sanity and then load the assembler program.