SPIM Instruction Set

An overview of the instruction set of the MIPS32 architecture as implemented by the SPIM emulator. Adapted from reference documents from the University of Stuttgart and Drexel University, from material in the appendix of Patterson and Hennessey's Computer Organization and Design, and from the MIPS32 (r5.04) Instruction Set reference.

Registers

As implemented by SPIM, MIPS has 32× 32-bit general purpose registers and 16× 64-bit floating point registers, as well as two special registers Hi and Lo for manipulating 64-bit integer quantities.

The 32 general purpose registers can be referenced $0 through $31, or by symbolic names, and are used as follows:

Regs Names Description
$0 $zero the value 0; writes are discarded
$1 $at assembler temporary; reserved for assembler use
$2 $3 $v0 $v1 value from expression evaluation or function return
$4 $5
$6 $7
$a0 $a1
$a2 $a3
first four arguments to a function/subroutine
$8 $9
$10 $11
$12 $13
$14 $15
$t0 $t1
$t2 $t3
$t4 $t5
$t6 $t7
temporary; callers relying on their values must save them before calling subroutines as they may be overwritten
$16 $17
$18 $19
$20 $21
$22 $23
$s0 $s1
$s2 $s3
$s4 $s5
$s6 $s7
saved; subroutines must guarantee their values are unchanged (by, for example, restoring them)
$24 $25 $t8 $t9 temporary; callers relying on their values must save them before calling subroutines as they may be overwritten
$26 $27 $k0 $k1 for kernel use; may change unexpectedly — avoid using in user programs
$28 $gp global pointer (address of global area)
$29 $sp stack pointer (top of stack)
$30 $fp
$s8
frame pointer (bottom of current stack frame);
if not using a frame pointer, becomes a save register
$31 $ra return address of most recent caller

The 16 floating-point registers are referenced in pairs — each pair gives a 64-bit value — and are used as follows:

Regs Description
$f0/$f1
$f2/$f3
floating-point expression evaluation or function return
$f4/$f5
$f6/$f7
$f8/$f9
$f10/$f11
temporary; must be saved by caller to subroutine; subroutine can overwrite
$f12/$f13
$f14/$f15
first two double-precision function arguments
$f16/$f17
$f18/$f19
temporary registers; used for expression evaluation
$f20/$f21
$f22/$f23
$f24/$f25
$f26/$f27
$f28/$f29
$f30/$f31
safe function variables; must be preserved across function calls

Memory

SPIM's memory is partitioned as follows:

Segment Base Description
text 0x00400000 where user program code resides; on SPIM, it is the only area of memory where instructions are executable; its initial size is 256 kiB.
data 0x10000000 where user data resides; its initial size is 256 kiB, but its size is not fixed, and can be changed with the sbrk syscall up to a maximum of 1 MiB.
stack 0x7ffffeff the function call stack; grows towards negative addresses. its initial size is 64 kiB, but it will grow as needed up to a maximum of 256 kiB.
k_text 0x80000000 protected executable code, not accessible in user mode; in a real system, the operating system kernel's code would be mapped here. on SPIM, the exception handler is loaded here; its initial size is 64 kiB
k_data 0x90000000 protected data, not accessible in user mode; in a real system, the operating system's data would be mapped here. on SPIM, the exception handler's data is loaded here; its initial size is 64 kiB; but it will grow as needed up to a maximum of 1 MiB.

Syntax

Each instruction is written on a single line, and has the general format

label:   opcode   [operand₁[, operand₂[, operand₃]]]

The number of operands for each instruction varies, but could be between zero and three. In the descriptions below, the following notation is used to describe instruction operands.

Operand Description
Rn a register — commonly, Rs and Rt are sources, and Rd is a destination; registers may be specified either by a numeric name ($0 to $31), or by a symbolic name ($sN, $tN, etc.)
Imm a literal constant value, or immediate: may be specified as an octal, decimal, hexadecimal, or character literal; if followed by a number (e.g., Imm16) that specifies the width in bits and implies the range of the value.
Label a symbolic name which is associated with a memory address
Addr a memory address, in one of the formats described below

Many instructions have an address operand; these may be written in a number of formats:

Format Address
Label the address associated with the label
(Rn) the value stored in register Rn (indirect address)
Imm(Rn) the sum of Imm and the value stored in register Rn
Label(Rn) the sum of Label's address and the value stored in register Rn
Label ± Imm the sum of Label's address and Imm
Label ± Imm(Rn) the sum of Label's address and Imm and the value stored in register Rn

Instructions

The SPIM emulator implements instructions from the MIPS32 instruction set, as well as pseudo-instructions (which look like MIPS instructions, but which aren't provided on real hardware). Real MIPS instructions are marked with a ✓. All other instructions are pseudo-instructions. Operators in expressions have the same meaning as their C counterparts.

Instruction Description Encoding
CPU Arithmetic Instructions
ADD Rd, Rs, Rt INTEGER OVERFLOW Rd = Rs + Rt 000000ssssstttttddddd00000100000
ADDI Rt, Rs, Imm16 INTEGER OVERFLOW Rt = Rs + Imm16 001000ssssstttttIIIIIIIIIIIIIIII
ADDU Rd, Rs, Rt Rd = Rs + Rt 000000ssssstttttddddd00000100001
ADDIU Rt, Rs, Imm16 Rt = Rs + Imm16 001001ssssstttttIIIIIIIIIIIIIIII
SUB Rd, Rs, Rt INTEGER OVERFLOW Rd = Rs - Rt 000000ssssstttttddddd00000100010
SUBU Rd, Rs, Rt Rd = Rs - Rt 000000ssssstttttddddd00000100011
MUL Rd, Rs, Rt Rd = Rs * Rt 011100ssssstttttddddd00000000010
MULT Rs, Rt (Hi,Lo) = Rs * Rt 000000sssssttttt0000000000011000
MULTU Rs, Rt (Hi,Lo) = Rs * Rt 000000sssssttttt0000000000011001
MADD Rs, Rt (Hi,Lo) += Rs * Rt 011100sssssttttt0000000000000000
MADDU Rs, Rt (Hi,Lo) += Rs * Rt 011100sssssttttt0000000000000001
MSUB Rs, Rt (Hi,Lo) -= Rs * Rt 011100sssssttttt0000000000000100
MSUBU Rs, Rt (Hi,Lo) -= Rs * Rt 011100sssssttttt0000000000000101
DIV Rs, Rt Lo = Rs / Rt; Hi = Rs % Rt 000000sssssttttt0000000000011010
DIVU Rs, Rt Lo = Rs / Rt; Hi = Rs % Rt 000000sssssttttt0000000000011011
DIV Rd, Rs, Rt Rd = Rs / Rt SPIM
DIVU Rd, Rs, Rt Rd = Rs / Rt SPIM
REM Rd, Rs, Rt Rd = Rs % Rt SPIM
REMU Rd, Rs, Rt Rd = Rs % Rt SPIM
CLO Rd, Rs Rd = count_leading_ones(Rs) 011100ssssstttttddddd00000100001
CLZ Rd, Rs Rd = count_leading_zeroes(Rs) 011100ssssstttttddddd00000100000
SEB Rd, Rs Rd = sign_extend (Rs & 0x000000ff) 01111100000tttttddddd10000100000
SEH Rd, Rs Rd = sign_extend (Rs & 0x0000ffff) 01111100000tttttddddd11000100000
SEQ Rd, Rs, Rt Rd = Rs == Rt SPIM
SNE Rd, Rs, Rt Rd = Rs != Rt SPIM
SLE Rd, Rs, Rt Rd = Rs <= Rt SPIM
SLEU Rd, Rs, Rt Rd = Rs <= Rt SPIM
SLT Rd, Rs, Rt Rd = Rs < Rt 000000ssssstttttddddd00000101010
SLTU Rd, Rs, Rt Rd = Rs < Rt 000000ssssstttttddddd00000101011
SGT Rd, Rs, Rt Rd = Rs > Rt SPIM
SGTU Rd, Rs, Rt Rd = Rs > Rt SPIM
SGE Rd, Rs, Rt Rd = Rs >= Rt SPIM
SGEU Rd, Rs, Rt Rd = Rs >= Rt SPIM
SLTI Rt, Rs, Imm16 Rt = Rs < Imm16 001010ssssstttttIIIIIIIIIIIIIIII
SLTIU Rt, Rs, Imm16 Rt = Rs < Imm16 001011ssssstttttIIIIIIIIIIIIIIII
ABS Rt, Rs Rt = |Rs| SPIM
NEG Rt, Rs INTEGER OVERFLOW Rt = -Rs SUB Rt, $0, Rs
NEGU Rt, Rs Rt = -Rs SUBU Rt, $0, Rs
CPU Logical Instructions
AND Rd, Rs, Rt Rd = Rs & Rt 000000ssssstttttddddd00000100100
ANDI Rt, Rs, Imm16 Rt = Rs & Imm16 001100ssssstttttIIIIIIIIIIIIIIII
OR Rd, Rs, Rt Rd = Rs | Rt 000000ssssstttttddddd00000100101
ORI Rt, Rs, Imm16 Rt = Rs | Imm16 001101ssssstttttIIIIIIIIIIIIIIII
NOR Rd, Rs, Rt Rd = ~(Rs | Rt) 000000ssssstttttddddd00000100111
XOR Rd, Rs, Rt Rd = Rs ^ Rt 000000ssssstttttddddd00000100110
XORI Rt, Rs, Imm16 Rt = Rs ^ Imm16 001110ssssstttttIIIIIIIIIIIIIIII
NOT Rt, Rs Rt = ~Rs NOR Rt, Rs, $0
CPU Shift Instructions
ROL Rd, Rt, Rs Rd = Rt rot < Rs SPIM
ROR Rd, Rt, Rs Rd = Rt rot > Rs SPIM
ROTR Rd, Rt, a Rd = Rt rot > a 00000000001tttttdddddaaaaa000010
ROTRV Rd, Rt, Rs Rd = Rt rot > Rs 000000ssssstttttddddd00001000110
SLL Rd, Rt, a Rd = Rt << a 00000000000tttttdddddaaaaa000000
SLLV Rd, Rt, Rs Rd = Rt << Rs 000000ssssstttttddddd00000000100
SRA Rd, Rt, a SIGN EXTENDED Rd = Rt >> a 00000000000tttttdddddaaaaa000011
SRAV Rd, Rt, Rs SIGN EXTENDED Rd = Rt >> Rs 000000ssssstttttddddd00000000111
SRL Rd, Rt, a Rd = Rt >> a 00000000000tttttdddddaaaaa000010
SRLV Rd, Rt, Rs Rd = Rt >> Rs 000000ssssstttttddddd00000000110
CPU Load, Store, and Memory Control Instructions
LI Rt Imm32 Rt = Imm32 LUI Rt, Imm16-32
ORI Rt, Rt, Imm0-16
LA Rt Label Rt = Label SPIM
LUI Rt, Imm16 Rt = Imm16 << 16 00111100000tttttIIIIIIIIIIIIIIII
LB Rt, Offset16(Rb) SIGN EXTENDED ADDRESS ERROR Rt = RAM[Rb + Offset16] 100000bbbbbtttttOOOOOOOOOOOOOOOO
LBU Rt, Offset16(Rb) ADDRESS ERROR Rt = RAM[Rb + Offset16] 100100bbbbbtttttOOOOOOOOOOOOOOOO
LH Rt, Offset16(Rb) SIGN EXTENDED ADDRESS ERROR Rt = RAM[Rb + Offset16] 100001bbbbbtttttOOOOOOOOOOOOOOOO
LHU Rt, Offset16(Rb) ADDRESS ERROR Rt = RAM[Rb + Offset16] 100101bbbbbtttttOOOOOOOOOOOOOOOO
LW Rt, Offset16(Rb) ADDRESS ERROR Rt = RAM[Rb + Offset16] 101000bbbbbtttttOOOOOOOOOOOOOOOO
LD Rt, Offset16(Rb) ADDRESS ERROR Rt = RAM[Rb + Offset16]
Rt+1 = RAM[Rb + Offset16 + 4]
SPIM
SB Rt, Offset16(Rb) ADDRESS ERROR RAM[Rb + Offset16] = Rt 100100bbbbbtttttOOOOOOOOOOOOOOOO
SH Rt, Offset16(Rb) ADDRESS ERROR RAM[Rb + Offset16] = Rt 100101bbbbbtttttOOOOOOOOOOOOOOOO
SW Rt, Offset16(Rb) ADDRESS ERROR RAM[Rb + Offset16] = Rt 101011bbbbbtttttOOOOOOOOOOOOOOOO
SD Rt, Offset16(Rb) ADDRESS ERROR RAM[Rb + Offset16] = Rt
RAM[Rb + Offset16 + 4] = Rt+1
SPIM
CPU Move Instructions
MFHI Rd Rd = HI 0000000000000000ddddd00000010000
MFLO Rd Rd = LO 0000000000000000ddddd00000010010
MTHI Rd HI = Rd 000000sssss000000000000000010001
MTLO Rd LO = Rd 000000sssss000000000000000010011
MOVE Rt, Rs Rt = Rs ADDU Rt, $0, Rs
MOVZ Rd, Rs, Rt IF Rt == 0 THEN Rd = Rs 000000ssssstttttddddd00000001010
MOVN Rd, Rs, Rt IF Rt != 0 THEN Rd = Rs 000000ssssstttttddddd00000001011
CPU Branch and Jump Instructions
B Offset16 PC += Offset16 << 2 0001000000000000OOOOOOOOOOOOOOOO
BAL Offset16 $ra = PC + 4
PC += Offset16 << 2
0000010000010001OOOOOOOOOOOOOOOO
BEQ Rs, Rt, Offset16 IF Rs == Rt THEN PC += Offset16 << 2 000100ssssstttttOOOOOOOOOOOOOOOO
BEQZ Rs, Offset16 IF Rs == 0 THEN PC += Offset16 << 2 BEQ $0, Rs, Offset16
BNE Rs, Rt, Offset16 IF Rs != Rt THEN PC += Offset16 << 2 000101ssssstttttOOOOOOOOOOOOOOOO
BNEZ Rs, Offset16 IF Rs != 0 THEN PC += Offset16 << 2 BNE $0, Rs, Offset16
BGE Rs, Rt , Offset16 IF Rs >= Rt THEN PC += Offset16 << 2 SLT $at, Rs, Rt
BEQ $0, $at, Offset16
BGEU Rs, Rt , Offset16 IF Rs >= Rt THEN PC += Offset16 << 2 SLTU $at, Rs, Rt
BEQ $0, $at, Offset16
BGEZ Rs, Offset16 IF Rs >= 0 THEN PC += Offset16 << 2 000001sssss00001OOOOOOOOOOOOOOOO
BGT Rs, Rt , Offset16 IF Rs > Rt THEN PC += Offset16 << 2 SLT $at, Rt, Rs
BNE $0, $at, Offset16
BGTU Rs, Rt , Offset16 IF Rs > Rt THEN PC += Offset16 << 2 SLTU $at, Rt, Rs
BNE $0, $at, Offset16
BGTZ Rs, Offset16 IF Rs > 0 THEN PC += Offset16 << 2 000111sssss00000OOOOOOOOOOOOOOOO
BLT Rs, Rt , Offset16 IF Rs < Rt THEN PC += Offset16 << 2 SLT $at, Rs, Rt
BNE $0, $at, Offset16
BLTU Rs, Rt , Offset16 IF Rs < Rt THEN PC += Offset16 << 2 SLTU $at, Rs, Rt
BNE $0, $at, Offset16
BLTZ Rs, Offset16 IF Rs < 0 THEN PC += Offset16 << 2 000001sssss00000OOOOOOOOOOOOOOOO
BLE Rs, Rt , Offset16 IF Rs <= Rt THEN PC += Offset16 << 2 SLT $at, Rt, Rs
BEQ $0, $at, Offset16
BLEU Rs, Rt , Offset16 IF Rs <= Rt THEN PC += Offset16 << 2 SLTU $at, Rt, Rs
BEQ $0, $at, Offset16
BLEZ Rs, Offset16 IF Rs <= 0 THEN PC += Offset16 << 2 000110sssss00000OOOOOOOOOOOOOOOO
BGEZAL Rs, Offset16 $ra = PC + 4
IF Rs >= 0 THEN PC += Offset16 << 2
000001sssss10001OOOOOOOOOOOOOOOO
BLTZAL Rs, Offset16 $ra = PC + 4
IF Rs < 0 THEN PC += Offset16 << 2
000001sssss10000OOOOOOOOOOOOOOOO
J Address26 PC = PC[31-28] && Address26 << 2 000010AAAAAAAAAAAAAAAAAAAAAAAAAA
JAL Address26 $ra = PC + 4
PC = PC[31-28] && Address26 << 2
000011AAAAAAAAAAAAAAAAAAAAAAAAAA
JR Rs PC = Rs 000000sssss0000000000hhhhh001000
JALR Rs $ra = PC + 4
PC = Rs
000000sssss00000dddddhhhhh001001
JALR Rd, Rs Rd = PC + 4
PC = Rs
000000sssss00000dddddhhhhh001001

System Services

The SPIM emulator provides a number of mechanisms for interacting with the host system, to provide input and output, file operations, and other miscellaneous services, which we refer to as system calls or syscalls. These are invoked via the syscall instruction after storing the service code in the register $v0.

$v0= Arguments Result Description
Printing Values
1 $a0: int print_int: Print the integer in $a0 to the console as a signed decimal.
2 $f12: float print_float: Print the float in $f12 to the console as a %.8f.
3 $f12/$f13: double print_double: Print the double in $f12/$f13 to the console as a %.18g
4 $a0: char * print_string: Print the nul-terminated array of bytes referenced by $a0 to the console as an ASCII string.
11 $a0: char print_character: Print the character in $a0, analogous to putchar(3).
Reading Values
5 $v0: int read_int: Read an integral value from the console, with atol(3)'s semantics, into register $v0
6 $f0: float read_float: Read a floating-point value from the console, with atof(3)'s semantics, into register $f0
7 $f0/$f1: double read_double: Read a double-precision floating-point value from the console, with atof(3)'s semantics, into registers $f0/$f1
8 $a0: char *;
$a1: int
read_string: Read a string into the provided buffer (referenced by $a0); up to size (given in $a1) bytes are read, and the result is nul-terminated.
12 $v0: char read_character: Read the next character from the console into register $v0; analogous to getchar(3)
File Manipulation
13 $a0: char *;
$a1: int;
$a2: mode_t
$v0: fd open: Open the file specified by name (referenced by $a0) in a particular access mode as specified by flags (given by $a1), and, if it is to be created, with mode mode (given by $a2). Returns a file descriptor, a small non-negative int. Effectively, open(2).
14 $a0: fd;
$a1: void *;
$a2: int
$v0: int read: On the file given by the file descriptor fd (given in $a0), read len bytes (given by $a2) into buffer (given by $a1). Returns the number of bytes read, or -1 if an error occurred. Effectively, read(2).
15 $a0: fd;
$a1: void *;
$a2: int
$v0: int write: On the file given by the file descriptor fd (given in $a0), write len bytes (given by $a2) from buffer (given by $a1). Returns the number of bytes written, or -1 if an error occurred. Effectively, write(2).
16 $a0: fd $v0: int close: Close the file given by the file descriptor fd (given in $a0). Returns 0 if successful, or -1 if an error occurred. Effectively, close(2).
Process Services
9 $a0: int sbrk: Extend the .data segment by adding $a0 bytes; a primitive useful for, e.g., implementing malloc(3)
10 exit: The program exits with code 0.
17 $a0: int exit2: The program exits with code (given in $a0).

Directives

The SPIM assembler supports a number of directives, which allow things to be specified at assembly time.

Directive Description
.text the instructions following this directive are placed in the text segment of memory
.data the data defined following this directive is placed in the data segment of memory
.ktext the instructions following this directive are placed in the kernel text segment of memory
.kdata the data defined following this directive is placed in the kernel data segment of memory
.align N arrange that the next datum is stored with appropriate alignment (that the lower N bits of its address are set to zero) by inserting enough padding — nearly always automatically done; a half word requires .align 1 (for two bytes), a word requires .align 2 (for four bytes), and a double requires .align 3 (for eight bytes).
.ascii "string" store an ASCII string without a '\0'-terminator at the next location(s) in the current data segment. nearly always not what you want; use .asciiz instead!
.asciiz "string" store a '\0'-terminated ASCII string at the next location(s) in the current data segment
.space n allocate n uninitialised bytes of space at the next location in the current segment
.byte val [, ...] store values in successive byte(s) at the next location(s) in the current segment
.half val [, ...] store values in successive half word(s) at the next location(s) in the current segment
.word val [, ...] store values in successive word(s) at the next location(s) in the current segment
.float val [, ...] store values in successive float(s) at the next location(s) in the current segment
.double val [, ...] store values in successive double(s) at the next location(s) in the current segment
.globl label [, ...] Declare the listed label(s) as global to enable referencing from other files