All of your work will be kept in specific directories on the Odin server. To create the area, first log in to Odin, then enter the commands below.$ cd $ /home/fac/gordon/p/2240/build_dirs.shYour directory structure is now setup.
Goals:
Understand the structure of a MIPS program.
Understand how to use SPIM.
Write a MIPS program to sum two integers and display a result.
Resources:
Quick guide
MIPS reference
MIPS-32 ISA
MIPS coder's reference
Other links from our home page.
Background: We will use a MIPS simulator (SPIM) in this course. We must use a simulator because you are most likely using a machine with an x86-64 microprocessor. SPIM is installed on Odin but you can also download SPIM for your home machine (see Resources above). SPIM is not an assembler; i.e., SPIM does not create an executable file in machine code. SPIM translates MIPS line by line, similar to how an interpreter runs code. Because of this, SPIM is a good learning environment.
Here are some files to start the lab.
cd 2240/1 cp /home/fac/gordon/p/2240/code/lab1/* .
# hello.s # a sample MIPS program to demonstrate MIPS basics # Usage: $ spim -f hello.s .data message: .asciiz "Hello World!\n" .text main: # this is a label la $a0, message # load the address of the string li $v0, 4 # 4 is syscall to print a string syscall # do system call li $a0, 10 # 10 is ascii value for linefeed li $v0, 11 # 11 is syscall to print char syscall # do system call li $v0, 10 # 10 is system call to exit syscall # do system call
There is no strict convention on the extension of a MIPS source code. You might see .s or .asm or .nasm or something else. We will use .s in this class since s is the first letter of both short and sweet.
The source code above has two sections, a data section and a text section. Everything is in one section or the other.
Comments are prefixed with a hash # everything to the end of the line past the hash is ignored by the assembler. The data section holds literals such as the "Hello World!" string. The text section contains instructions and assembler directives. Instructions are in the form of an opcode (operation code) followed by zero, one, two or three arguments for that opcode. The amount of whitespace between opcodes and arguments is irrelevant. An argument for a MIPS instruction is either a register, a literal, or an address. An address is marked by a label like 'stuff'. A literal is a number such as 4, 10, or 11.
Registers and Memory: A register is special storage on the microprocessor. This is opposed to variables stored in memory, which must be defined in the .data section. For more on this, read: http://cnx.org/content/m29470/1.1/.
In short, data is stored in either a register or in memory. Registers are very fast but limited in number and restricted to word-length. Memory is abundant for our purposes, is not necessarily restricted to word length, but is slower. Additionally, in MIPS, most operations operate on registers. If you want to add two values in memory you would need to issue commands to bring them from memory, place them in registers, then carry out your operation. In MIPS, a register can hold either a value or an address. The meaning of the data in the register is determined by its context in the instruction. It is up to you to keep track of whether a register holds an address or a value.
Load Address: Some MIPS instructions are not part of the MIPS instruction set architecture (the hardware) but are provided by the assembler to make the programmer's life a little easier. These instructions are called pseudo-instructions. Each pseudo-instruction is a macro for 2 or more instructions from the ISA. Load address, la, is a MIPS instruction that stuffs the address of a label into a register; e.g.:
la $a0, format #load address of label format into $a0
The two arguments for la specify the movement of data from memory (the source) to a register (the target). The movement is right to left. Be careful not to confuse the two. The target comes before the source (but only for load operations). Thus a LOAD always reads like an assignment statement in a high-level language.
Store: In the coming weeks we will also cover STORE operations. Unlike 80x86 (which we will cover in week 8), MIPS is a LOAD/STORE architecture. This means that computations cannot be done directly to values in memory. Data must always be read from memory into a register (LOAD) or written to memory from a register (STORE). This makes instructions simple since the first argument in a load instruction is always the target register. The first argument in a store is the source register. While the flow of data in a LOAD instruction is logically right to left (load the address of "Hello World!\n" into register a0), the flow of data in a STORE instructions is logically left to right:
sw $a0, $t0 # store value in $a0 to address in $t0
Load Immediate The load immediate, li, is also a pseudo-instruction that loads the numeric constant 10 (source) into the target register $v0.
syscall The syscall instruction executes a system call identified by the integer value in register $v0. In this case '10' is the exit call. Different values placed in $v0 will cause different behavior. syscall can print strings, read strings, print integers, exit, etc.
li dest, integer # load integer into dest register addu dest, src1, src2 # add values in src1 and src2 registers # and store result in dest register move dest, src # copy value in src reg to dest reg You can also use fewer instructions if you want to use the add immediate instruction: addi dest, src, integer # store result of value in src + integer # in dest
Which Registers to Use? Since all 32 MIPS registers are general purpose, you can generally get away with using any registers that you want. However, there are conventions that good coders should use. For example, temporary values (such as the integers you are adding together) should be loaded into temporary registers $t0 - $t7. Function arguments are loaded into $a0 - $a3. The results of computations are loaded into the value registers $v0 - $v1. To make sure you have added the values correctly you need to display the result.
The format of your output...The sum of 5 and 9 is 14.
When your program is run, it will look like the following sample. Notice how the program output is clean and stands out. Please choose 2 interesting numbers that add up to 2240.
you@odin:~/2240/1$ spim -f addnums.s SPIM Version 8.0 of January 8, 2010 Copyright 1990-2010, James R. Larus. All Rights Reserved. See the file README for a full copyright notice. Loaded: /usr/lib/spim/exceptions.s The sum of 1241 and 999 is 2240. you@odin:~/2240/1$
Input/Output is provided by the assembler via system calls. In a high-level language such as C++ you can sometimes get away with letting the compiler figure out what data type you are displaying,
int x = 5; cout << x << endl; char stuff[20] = "hello\n"; cout << stuff << endl;
will display an integer first and then a character string next. In assembly you need to specify precisely what type of data you wish the bit string in the register to be displayed as. In this lab you will make calls to print strings (printable text) and integers. The syscall service you need to print the integers is print int. To use this call load the value you want to display into $a0. Then load the value 1 into $v0. Then make the print int call.
The syscall service you need to print the text strings is the same as noted in hello.s. You will need to create strings with labels in the data segment for each string you want to print. For example:
.data str1: .asciiz "The sum of " Then make a syscall to print each string in the correct order. For example, la $a0, str1 # load the address of str1 into a0 li $v0, 4 # 4 is syscall to print a string syscall # make the call
Hint: Since you need to print three integers, you may need to use three registers for these values. Move each integer as you print it into $a0.
What to turn in
Your addnums.s program will be collected from your odin 2240/1 folder.