Skip to content
CS 233 // Spimbot
Pages

Assembly language

This page describes the assembly language spimbot assembles. Simulator describes the machine a program runs on.

Source files

#

Each line holds zero or more statements separated by ;. A # outside a string or character literal starts a comment that runs to the end of the line. A statement is any number of labels followed by at most one directive, instruction or constant definition.

# Prints "hello" and exits.
	.data
hello:	.asciiz	"hello\n"

	.text
main:
	li	$v0, 4		# print_string
	la	$a0, hello
	syscall
	jr	$ra

Mnemonics are case-insensitive. Directive names, labels, constants and register names are case-sensitive, and register names are lowercase.

A name starts with a letter, _ or . and continues with letters, digits, _ and .. name: defines a label at the address of the next item.

Operands are separated by commas, and the commas are required.

Numbers and strings

#

Integers are decimal (42, and 010 is ten) or hexadecimal (0x2a). Write negative numbers with a unary minus. A character literal such as '*' is one byte. A floating-point literal has a point with digits on both sides, an exponent, or both, such as 1.5, 2e3 and 6.02e-23. Only .float, .double, li.s and li.d accept one.

Strings are double-quoted and stay on one line. Strings and character literals take the C escapes, such as \n, \t, \\, \" and \0.

Registers

#

General-purpose registers are written $ and a number from 0 to 31, or $ and a name:

Number Name Use
$0 $zero Always 0
$1 $at Assembler temporary, which expansions use
$2-$3 $v0-$v1 Return values. $v0 selects the syscall
$4-$7 $a0-$a3 Arguments
$8-$15 $t0-$t7 Caller-saved temporaries
$16-$23 $s0-$s7 Callee-saved
$24-$25 $t8-$t9 Caller-saved temporaries
$26-$27 $k0-$k1 Reserved for the exception handler
$28 $gp Global pointer
$29 $sp Stack pointer
$30 $fp, $s8 Frame pointer
$31 $ra Return address

Floating-point registers are $f0-$f31, and condition codes are $fcc0-$fcc7 or a number from 0 to 7. Coprocessor 0 and floating-point control registers are written by number: $12 is Status in mfc0 $t0, $12, and $31 is FCSR in cfc1 $t0, $31.

Expressions

#

Any integer operand is an expression over numbers, labels and constants, such as table+8 or end-start. Parentheses group subexpressions. The operators, from highest precedence to lowest, are:

  1. Unary - and +
  2. * and /, with / rounding toward zero
  3. Binary + and -
  4. >>, an arithmetic shift right

Operands such as shift amounts, bit-field positions and sizes, alignments, .space sizes, repeat counts, segment addresses, lui and trap immediates, and integer values in .float and .double take no labels. A constant defined earlier in the same file without labels works there.

Constants

#

name = expression or .set name, expression defines a constant.

ROWS = 16
COLS = ROWS * 2
	.data
grid:	.space	ROWS * COLS

Constants are scoped like labels, as Multiple files describes.

Segments

#

A segment directive switches to its segment, at the address it gives or where the segment left off:

Directive Segment Starts at Address range
.text [addr] User code 0x00400000 0x00400000-0x0fffffff
.data [addr], .rdata [addr], .sdata [addr] User data 0x10010000 0x10000000-0x7fffffff
.ktext [addr] Kernel code 0x80000000 0x80000000-0x8fffffff
.kdata [addr] Kernel data 0x90000000 0x90000000-0xfffeffff

The memory map shows how much of these ranges a program can use. Instructions go only in .text and .ktext, and data directives work in any segment. Items that overlap are an error.

Multiple files

#

spimbot assembles every file you give it into one program, in the order given. Each file starts in .text, and each segment continues where the previous file left it.

Labels and constants are local to their file. .globl name or .global name exports a name the file defines to every file, and a file's own definition takes precedence over an exported one. Exporting a name the file does not define, or a name another file already exports, is an error. main is exported from the first file that defines it unless a file exports it with .globl.

Common and external symbols

#

.comm name, size reserves size zeroed bytes for name at the next word-aligned address in .data and exports name. .lcomm does the same without exporting. Neither changes the current segment.

.extern name[, size] makes name global. When the file does not define name and no other file exports it, a nonzero size gives name zeroed storage in the 64 KiB small-data area at 0x10000000, which $gp addresses.

Directives

#
Directive Arguments Meaning
.text, .data, .rdata, .sdata, .ktext, .kdata [address] Switch segment. See Segments
.byte value, … 8-bit integers, -128 to 255
.half value, … 16-bit integers, -32,768 to 65,535, aligned to 2
.word value, … 32-bit integers, -2,147,483,648 to 4,294,967,295, aligned to 4
.float value, … Single-precision floats, aligned to 4
.double value, … Double-precision floats, aligned to 8
.ascii "string", … Each string's bytes
.asciiz "string", … Each string's bytes and a zero byte
.space n n zero bytes
.align n Advance to the next multiple of 1 << n bytes, n from 1 to 16. .align 0 turns off automatic alignment until the next segment directive
.globl, .global name, … Export names from this file
.extern name[, size] Declare a global name. See Common and external symbols
.comm, .lcomm name, size Reserve zeroed storage in .data
.lab name Define the label name here
.struct offset Start a structure layout, described below
.repeat, .rept count Repeat the statements up to the matching .endr count times. Blocks nest
.endr End a .repeat block
.set name, value Define a constant
.err Report an error at this line
.set (other forms), .ent, .end, .file, .loc, .frame, .mask, .fmask, .option, .verstamp, .vreg, .livereg, .bgnb, .endb, .alias, .noalias, .asm0 Anything Ignored

A data directive value can repeat as value:count: .word 0:8 is eight zero words, and .byte 1, 2:3 is the bytes 1, 2, 2, 2. A value that does not fit its directive's size is an error.

Each repetition of a .repeat block defines its labels and constants again, so a block that repeats more than once cannot define any.

.struct offset starts a layout in which labels name offsets from offset. Its data directives advance the offset and write no data, so their values must be zero. The layout ends at the next segment directive or the end of the file.

	.struct	0
point_x:	.word	0
point_y:	.word	0
point_size:

	.text
get_y:
	lw	$v0, point_y($a0)	# $a0 holds a point's address
	jr	$ra

Here point_y is 4 and point_size is 8. These names are labels, so point_y($a0) expands through $at.

Instructions

#

The assembler accepts the MIPS32 Release 2 instruction set with its floating-point and coprocessor instructions. In the tables, rd, rs and rt are general-purpose registers, fd, fs, ft and fr floating-point registers, imm an immediate, sa a shift amount and addr a memory operand.

Arithmetic

#
Mnemonics Operands Operation
add, addu, sub, subu rd, rs, rt Sum or difference. add and sub raise Overflow on signed overflow
addi, addiu rt, rs, imm As add and addu, with a sign-extended 16-bit immediate
slt, sltu rd, rs, rt 1 if rs < rt, signed or unsigned, else 0
slti, sltiu rt, rs, imm As slt and sltu, with a sign-extended 16-bit immediate
mul rd, rs, rt Low 32 bits of the product. HI and LO keep their values
clz, clo rd, rs Count of leading zero or one bits
seb, seh rd, rt Sign-extended low byte or halfword
wsbh rd, rt Bytes swapped within each halfword
lui rt, imm imm in the upper halfword, zeros below
movz, movn rd, rs, rt Copy rs to rd if rt is zero, or nonzero
movf, movt rd, rs[, cc] Copy rs to rd if floating-point condition cc (default 0) is false, or true

Logic and bit fields

#
Mnemonics Operands Operation
and, or, xor, nor rd, rs, rt Bitwise operation
andi, ori, xori rt, rs, imm Bitwise operation with a zero-extended 16-bit immediate
ext rt, rs, pos, size The size-bit field of rs at bit pos, zero-extended
ins rt, rs, pos, size The low size bits of rs inserted into rt at bit pos

A bit field has at least 1 bit and fits in 32 bits: pos + size is at most 32.

Shifts and rotates

#

sll, srl, sra and rotr take rd, rt, sa and shift or rotate right rt by the constant sa, 0 to 31. sllv, srlv, srav and rotrv take rd, rt, rs and shift or rotate by the low 5 bits of rs. Each shift and rotate assembles to its register or constant form to match the amount: sll rd, rt, rs is sllv. nop, ssnop and ehb take no operands and do nothing.

Multiply and divide

#
Mnemonics Operands Operation
mult, multu rs, rt 64-bit product in HI (high word) and LO (low word)
madd, maddu, msub, msubu rs, rt Product added to or subtracted from HI:LO
div, divu rs, rt Quotient in LO, remainder in HI. Division by zero leaves HI and LO unchanged
mfhi, mflo rd Copy HI or LO to rd
mthi, mtlo rs Copy rs to HI or LO

Branches

#
Mnemonics Operands Branches if
beq, bne rs, rt, label rs == rt, rs != rt
blez, bgtz, bltz, bgez rs, label rs compared with zero, signed
bltzal, bgezal rs, label As bltz and bgez, and write the return address to $ra, taken or not
beql, bnel, blezl, bgtzl, bltzl, bgezl, bltzall, bgezall As above As above, and skip the delay slot when not taken
bc1t, bc1f, bc1tl, bc1fl [cc,] label Floating-point condition cc (default 0) is true or false

A branch reaches 32,768 instructions either way. For a farther target, use j.

Jumps

#
Mnemonics Operands Operation
j label or rs Jump. j rs is jr rs
jal label, rs or rd, rs Jump and write the return address to $ra, or to rd. The register forms are jalr
jr rs or label Jump to the address in rs. jr label is j label
jalr rs, rd, rs or label Jump to the address in rs and write the return address to rd (default $ra). jalr label is jal label

j and jal reach the 256 MiB region of the instruction after the jump. For a farther target, use jr.

Loads and stores

#
Mnemonics Operands Operation
lb, lbu, lh, lhu, lw rt, addr Load a byte, halfword or word. lbu and lhu zero-extend, the others sign-extend
lwl, lwr rt, addr Load the left or right part of an unaligned word, merged into rt
ll rt, addr Load a word and start an atomic sequence
sb, sh, sw rt, addr Store a byte, halfword or word
swl, swr rt, addr Store the left or right part of an unaligned word
sc rt, addr Store a word if the atomic sequence holds. rt becomes 1 on success, 0 on failure
lwc1, ldc1, swc1, sdc1 ft, addr Load or store a floating-point word or doubleword
lwxc1, ldxc1, swxc1, sdxc1 fd, index(base) Load or store at base + index

An indexed operand such as $t1($t0) has no space before the (. Alignment rules are in Bus errors and alignment. cache, pref, prefx, synci and sync assemble and do nothing.

Traps and system calls

#
Mnemonics Operands Operation
teq, tne, tge, tgeu, tlt, tltu rs, rt[, code] Raise Trap if the comparison holds
teqi, tnei, tgei, tgeiu, tlti, tltiu rs, imm Raise Trap if the comparison with the immediate holds
syscall [code] Perform the syscall $v0 selects
break [code] Raise Breakpoint

teq rs, imm is teqi, and likewise for the other register traps.

Coprocessor 0

#
Mnemonics Operands Operation
mfc0, mtc0 rt, $n[, sel] Read or write coprocessor 0 register n, select 0 to 7 (default 0)
eret Return from an exception to EPC
ei, di [rt] Enable or disable interrupts. rt receives the previous Status
wait [code] Stall until an interrupt can be taken

Floating point

#

Floating-point mnemonics end in a format: .s single, .d double or .w 32-bit integer.

Mnemonics Formats Operands Operation
add, sub, mul, div .s, .d fd, fs, ft Arithmetic
abs, neg, mov .s, .d fd, fs Absolute value, negation, copy
sqrt, recip, rsqrt .s, .d fd, fs Square root, reciprocal, reciprocal square root
madd, msub, nmadd, nmsub .s, .d fd, fr, fs, ft fs * ft + fr, fs * ft - fr, and their negations
movz, movn .s, .d fd, fs, rt Copy if rt is zero, or nonzero
movf, movt .s, .d fd, fs[, cc] Copy if condition cc is false, or true
c.cond .s, .d [cc,] fs, ft Set condition cc (default 0) to the comparison
cvt.s, cvt.d Another of .s, .d, .w fd, fs Convert to single or double
cvt.w, round.w, trunc.w, ceil.w, floor.w .s, .d fd, fs Convert to a 32-bit integer: current rounding mode, nearest, toward zero, up, down
mfc1, mtc1, mfhc1, mthc1 rt, fs Copy a word between rt and the low or high word of fs
cfc1, ctc1 rt, $n Read or write control register n

The conditions are f, un, eq, ueq, olt, ult, ole and ule, and the signaling sf, ngle, seq, ngl, lt, nge, le and ngt, which also signal Invalid on a quiet NaN.

A double names an even register and the odd register after it. The 64-bit integer format .l and the paired-single format .ps run only with Status.FR set, as Bare mode and 64-bit registers describes. Rounding and exceptions are in Floating-point unit.

Immediate and addressing forms

#

These forms of real instructions assemble to another real instruction or expand through $at:

  • Two operands: addi $t0, 5 is addi $t0, $t0, 5. addi, addiu, slti, sltiu, andi, ori, xori, add, addu, sub, subu, and, or, xor, nor, slt, sltu, sllv, srlv and srav take this form.
  • An immediate or label as the last operand of add, addu, sub, subu, and, or, xor, nor, slt, sltu, movz, movn and mul. A constant that fits the matching immediate instruction uses it: add $t0, $t1, 5 is addi $t0, $t1, 5. Any other value expands to li $at, value and the register instruction.
  • An immediate as rt of mult, multu, div, divu, madd, maddu, msub, msubu, beq, bne, beql and bnel: 0 is $zero, and any other value goes into $at with li.
  • A load or store address as offset(base), (base), label, label+offset, label(base) or an absolute address. A constant offset from -32,768 to 32,767 fits the instruction: lw $t0, 0x100 is lw $t0, 0x100($zero). A label or a larger offset expands to lui $at, high, then addu $at, $at, base when there is a base, then the access with the low half as offset.

Pseudo-instructions

#

Where the table says rt/imm, rt can be an immediate: 0 is $zero, and any other value goes into $at first with li $at, imm.

Pseudo-instruction Expansion
li rd, imm One addiu, ori or lui when the value allows, else lui $at, high; ori rd, $at, low. A value with labels always takes two instructions. The value is any 32-bit pattern
la rd, label As li rd, label
la rd, offset(base) addiu rd, base, offset when offset is a constant that fits 16 bits, else lui $at, high; ori $at, $at, low; addu rd, base, $at
move rd, rs addu rd, $zero, rs
not rd, rs nor rd, rs, $zero
neg rd, rs sub rd, $zero, rs
negu rd, rs subu rd, $zero, rs
abs rd, rs sra $at, rs, 31; xor rd, rs, $at; sub rd, rd, $at
b label, bal label beq $zero, $zero, label, bgezal $zero, label
beqz rs, label, bnez rs, label beq rs, $zero, label, bne rs, $zero, label
blt rs, rt/imm, label slt $at, rs, rt; bne $at, $zero, label, or slti $at, rs, imm; bne $at, $zero, label when imm is a nonzero constant from -32,768 to 32,767
bgt rs, rt/imm, label slt $at, rt, rs; bne $at, $zero, label, or slti $at, rs, imm+1; beq $at, $zero, label when imm is a nonzero constant and imm+1 is from -32,768 to 32,767
ble rs, rt/imm, label slt $at, rt, rs; beq $at, $zero, label, or slti $at, rs, imm+1; bne $at, $zero, label when imm is a nonzero constant and imm+1 is from -32,768 to 32,767
bge rs, rt/imm, label slt $at, rs, rt; beq $at, $zero, label, or slti $at, rs, imm; beq $at, $zero, label when imm is a nonzero constant from -32,768 to 32,767
bltu, bgeu As blt and bge, with sltu and sltiu
bgtu, bleu As the register forms of bgt and ble, with sltu
seq rd, rs, rt/imm xor rd, rs, rt; sltiu rd, rd, 1
sne rd, rs, rt/imm xor rd, rs, rt; sltu rd, $zero, rd
sgt rd, rs, rt/imm slt rd, rt, rs
sge rd, rs, rt/imm slt rd, rs, rt; xori rd, rd, 1
sle rd, rs, rt/imm slt rd, rt, rs; xori rd, rd, 1
sgtu, sgeu, sleu As sgt, sge, sle, with sltu
ror rd, rs, sa/rt rotr rd, rs, sa or rotrv rd, rs, rt
rol rd, rs, sa/rt rotr rd, rs, (32 - sa) & 31, or subu $at, $zero, rt; rotrv rd, rs, $at
div, divu, rem, remu with rd, rs, rt teq rt, $zero, 7; div rs, rt; mflo rd, with divu in the unsigned forms and mfhi in rem and remu
The same with rd, rs, imm li $at, imm; div rs, $at; mflo rd, with no zero check. A constant 0 is an error
The same with $zero as rd The division alone, with no zero check and no mflo or mfhi
mulo rd, rs, rt/imm mult rs, rt; mfhi $at; mflo rd; sra rd, rd, 31; tne rd, $at, 6; mflo rd, which traps on signed overflow
mulou rd, rs, rt/imm multu rs, rt; mfhi $at; mflo rd; tne $at, $zero, 6, which traps on unsigned overflow
ulw rd, addr, usw rs, addr lwl rd, offset+3(base); lwr rd, offset(base), or the same with swl and swr
ulh rd, addr lb rd, offset+1(base); lbu $at, offset(base); sll rd, rd, 8; or rd, rd, $at
ulhu rd, addr As ulh, with lbu for the high byte
ush rs, addr sb rs, offset(base); srl $at, rs, 8; sb $at, offset+1(base)
ld rt, addr, sd rt, addr Two lw or sw of rt and the next register, at offset and offset+4
l.s, l.d, s.s, s.d lwc1, ldc1, swc1, sdc1
li.s fd, value li $at, bits; mtc1 $at, fd
li.d fd, value li $at, low; mtc1 $at, fd; li $at, high; mthc1 $at, fd
mfc1.d rt, fs, mtc1.d rt, fs mfc1 or mtc1 of rt, then mfhc1 or mthc1 of the next register

With a label, an offset that does not fit 16 bits, or a load whose base is its destination, ulw, ulh, ulhu, usw, ush, ld and sd put the address in $at first and access it at offset 0.

The $at register

#

An instruction whose expansion uses $at cannot name $at in any operand, including a memory operand's base. The exceptions are the destinations of li, la, lb, lbu, lh, lhu, lw and ll. An instruction whose expansion does not use $at can name it anywhere.

Bare mode and 64-bit registers

#

check and exec take two assembler options. Matches always assemble bots with both off.

--bare requires each statement to be exactly one real instruction under its own mnemonic. Pseudo-instructions, forms that assemble to another instruction (add $t0, $t1, 5, jr label) and addresses that need $at are errors. Two-operand forms such as addi $t0, 5 are accepted.

--fp64 lets 64-bit floating-point operands name odd registers, for programs that set Status.FR to get 32 registers of 64 bits. See Floating-point unit.

Errors

#

The assembler reports every error at once, sorted by file, line and column, with the source line, carets under the problem and a note when a fix is likely. Errors that belong to no line, such as a missing main or overlapping items, come last. When there is any error, nothing runs.