Reference
MIPS32 cheat sheet
The whole book on one page: the three instruction formats, the 32 registers with their o32 jobs, every one of the 63 instructions your core runs with what it does, and the 26 things that catch a RISC-V reader. Every row links to the section that explains it.
Instruction formats
Every instruction is one 32-bit word, bit 31 on the left. The opcode (bits 31..26) picks the format; opcode 0 means "look at funct". §1.3
R-typeregister–register: add, and, sll, jr, mult, mfhi, movz, seb, ext …
add $t0,$t1,$t2 = 0x012a40a0- opcode — opcode 0 (SPECIAL), 28 (SPECIAL2), 31 (SPECIAL3)
- rs — rs: first source register
- rt — rt: second source, or the destination of an I-type
- rd — rd: destination register
- sa — sa: shift amount (0 when unused)
- funct — funct: which SPECIAL instruction
I-typeregister + 16-bit immediate: addiu, ori, lui, lw, sw, beq, slti, bgez …
addiu $t1,$zero,72 = 0x24090048- opcode — opcode: which instruction (0 = SPECIAL: see funct)
- rs — rs: first source register
- rt — rt: the destination, or the second register of beq/bne, or the REGIMM sub-opcode
- imm16 — imm: 16-bit constant, offset, or upper half
J-typejump to an absolute target: j, jal
jal 0xbfc0018c = 0x0ff00063- opcode — opcode 2 (j) or 3 (jal)
- index26 — index: 26 bits, ×4, under the top 4 bits of PC+4
Opcodes that need a second look
- op 0 SPECIAL — funct picks: arithmetic, logic, shifts, slt, jr/jalr, movz/movn, mult/div, mfhi/mflo/mthi/mtlo
- op 1 REGIMM — the rt field picks: bltz 0, bgez 1, bltzal 16, bgezal 17
- op 28 SPECIAL2 — funct: mul 2, clz 32, clo 33
- op 31 SPECIAL3 — funct: ext 0, ins 4, seb/seh 32 (sa 16 / 24)
Same funct, different field
- srl and rotr are both funct 2: rs = 0 shifts, rs = 1 rotates
- srlv and rotrv are both funct 6: sa = 0 shifts, sa = 1 rotates
- seb and seh are both SPECIAL3 funct 32: sa = 16 byte, sa = 24 half
- nop is the all-zero word: sll $zero,$zero,0
The register file (o32 names)
32 general registers of 32 bits, plus HI, LO and the PC. The names are a convention between compilers; the hardware knows only $0 (always zero) and $31 (where jal writes the link). §2.2
| number | name | job (o32) | after a call, the value is… |
|---|---|---|---|
| $0 | $zero | always 0 (hardware) | always 0 |
| $1 | $at | assembler temporary | gone (the assembler may use it) |
| $2 | $v0 | return value | gone (holds the result) |
| $3 | $v1 | return value, second word | gone |
| $4 | $a0 | argument 1 | gone |
| $5 | $a1 | argument 2 | gone |
| $6 | $a2 | argument 3 | gone |
| $7 | $a3 | argument 4 | gone |
| $8 | $t0 | temporary | gone (caller-saved) |
| $9 | $t1 | temporary | gone (caller-saved) |
| $10 | $t2 | temporary | gone (caller-saved) |
| $11 | $t3 | temporary | gone (caller-saved) |
| $12 | $t4 | temporary | gone (caller-saved) |
| $13 | $t5 | temporary | gone (caller-saved) |
| $14 | $t6 | temporary | gone (caller-saved) |
| $15 | $t7 | temporary | gone (caller-saved) |
| $16 | $s0 | saved | kept (callee-saved) |
| $17 | $s1 | saved | kept (callee-saved) |
| $18 | $s2 | saved | kept (callee-saved) |
| $19 | $s3 | saved | kept (callee-saved) |
| $20 | $s4 | saved | kept (callee-saved) |
| $21 | $s5 | saved | kept (callee-saved) |
| $22 | $s6 | saved | kept (callee-saved) |
| $23 | $s7 | saved | kept (callee-saved) |
| $24 | $t8 | temporary | gone (caller-saved) |
| $25 | $t9 | temporary | gone (caller-saved) |
| $26 | $k0 | reserved for the operating system | never use |
| $27 | $k1 | reserved for the operating system | never use |
| $28 | $gp | global pointer | kept |
| $29 | $sp | stack pointer | kept |
| $30 | $fp | frame pointer (objdump: s8) | kept (callee-saved) |
| $31 | $ra | return address (written by jal, hardware) | gone (jal overwrites it) |
| — | HI | high word of mult/multu; remainder of div/divu; read with mfhi, written with mthi | gone (caller-saved) |
| — | LO | low word of mult/multu; quotient of div/divu; read with mflo, written with mtlo | gone (caller-saved) |
| — | PC | address of the current instruction; not a register: only branches and jumps set it, jal/jalr/bgezal read it (+8) | the return address is in $ra |
- special: $zero $at $k0 $k1
- arguments and results: $a0–$a3 $v0 $v1
- temporaries, not saved: $t0–$t9
- saved across calls: $s0–$s7 $fp
- pointers: $gp $sp $ra
Every instruction
The 63 MIPS32 release-2 user-mode integer instructions your core runs, in the order the book teaches them. Register operands are named as in the assembler syntax; rd is written, rs and rt are read unless the syntax says otherwise.
Arithmetic
add/sub trap on signed overflow; the u forms wrap. GCC uses the u forms.| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| add rd, rs, rt | rd = rs + rt; traps on signed overflow | R · op 0 · funct 32 | add $t0,$t1,$t2 = 0x012a40a0 | §3.1 |
| addu rd, rs, rt | rd = rs + rt; wraps, never traps (what C uses) | R · op 0 · funct 33 | addu $t0,$t1,$t2 = 0x012a40a1 | §3.1 |
| sub rd, rs, rt | rd = rs − rt; traps on signed overflow | R · op 0 · funct 34 | sub $t0,$t1,$t2 = 0x012a40a2 | §3.1 |
| subu rd, rs, rt | rd = rs − rt; wraps, never traps | R · op 0 · funct 35 | subu $t0,$t1,$t2 = 0x012a40a3 | §3.1 |
| addi rt, rs, imm | rt = rs + sign-extended imm; traps on overflow | I · op 8 imm16, sign-extended | addi $t2,$t1,16 = 0x212a0010 | §3.2 |
| addiu rt, rs, imm | rt = rs + sign-extended imm; wraps (what C uses) | I · op 9 imm16, sign-extended | addiu $t2,$t1,16 = 0x252a0010 | §3.2 |
| lui rt, imm | rt = imm << 16 (the top half; low half zero) | I · op 15 imm16 → bits 31..16 | lui $t2,0x10 = 0x3d2a0010 | §3.2 |
Logic
The immediate forms zero-extend their 16 bits.| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| and rd, rs, rt | rd = rs AND rt, bit by bit | R · op 0 · funct 36 | and $t0,$t1,$t2 = 0x012a40a4 | §3.3 |
| or rd, rs, rt | rd = rs OR rt, bit by bit (move is or rd,rs,$zero) | R · op 0 · funct 37 | or $t0,$t1,$t2 = 0x012a40a5 | §3.3 |
| xor rd, rs, rt | rd = rs XOR rt, bit by bit | R · op 0 · funct 38 | xor $t0,$t1,$t2 = 0x012a40a6 | §3.3 |
| nor rd, rs, rt | rd = NOT (rs OR rt); not rd,rs is nor rd,rs,$zero | R · op 0 · funct 39 | nor $t0,$t1,$t2 = 0x012a40a7 | §3.3 |
| andi rt, rs, imm | rt = rs AND zero-extended imm | I · op 12 imm16, zero-extended | andi $t2,$t1,0x10 = 0x312a0010 | §3.3 |
| ori rt, rs, imm | rt = rs OR zero-extended imm | I · op 13 imm16, zero-extended | ori $t2,$t1,0x10 = 0x352a0010 | §3.3 |
| xori rt, rs, imm | rt = rs XOR zero-extended imm | I · op 14 imm16, zero-extended | xori $t2,$t1,0x10 = 0x392a0010 | §3.3 |
Compare
Only "less than". Other comparisons swap operands or add xori (§3.4).| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| slt rd, rs, rt | rd = 1 if rs < rt (signed), else 0 | R · op 0 · funct 42 | slt $t0,$t1,$t2 = 0x012a40aa | §3.4 |
| sltu rd, rs, rt | rd = 1 if rs < rt (unsigned), else 0 | R · op 0 · funct 43 | sltu $t0,$t1,$t2 = 0x012a40ab | §3.4 |
| slti rt, rs, imm | rt = 1 if rs < sign-extended imm (signed) | I · op 10 imm16, sign-extended | slti $t2,$t1,16 = 0x292a0010 | §3.4 |
| sltiu rt, rs, imm | rt = 1 if rs < sign-extended imm, compared unsigned | I · op 11 imm16, sign-extended | sltiu $t2,$t1,16 = 0x2d2a0010 | §3.4 |
Shifts and rotates
The value is rt; the count is sa (5 bits) or the low 5 bits of rs.| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| sll rd, rt, sa | rd = rt << sa; sll $zero,$zero,0 is nop | R · op 0 · funct 0 sa: 0–31 | sll $t0,$t2,2 = 0x012a4080 | §3.3 |
| srl rd, rt, sa | rd = rt >> sa, filling with zeros (logical) | R · op 0 · funct 2 · rs 0 sa: 0–31 | srl $t0,$t2,2 = 0x000a4082 | §3.3 |
| sra rd, rt, sa | rd = rt >> sa, filling with the sign bit (arithmetic) | R · op 0 · funct 3 sa: 0–31 | sra $t0,$t2,2 = 0x012a4083 | §3.3 |
| rotr rd, rt, sa r2 | rd = rt rotated right by sa | R · op 0 · funct 2 · rs 1 sa: 0–31 | rotr $t0,$t2,2 = 0x002a4082 | §8.2 |
| sllv rd, rt, rs | rd = rt << (rs & 31) | R · op 0 · funct 4 | sllv $t0,$t2,$t1 = 0x012a4084 | §3.3 |
| srlv rd, rt, rs | rd = rt >> (rs & 31), logical | R · op 0 · funct 6 · sa 0 | srlv $t0,$t2,$t1 = 0x012a4006 | §3.3 |
| srav rd, rt, rs | rd = rt >> (rs & 31), arithmetic | R · op 0 · funct 7 | srav $t0,$t2,$t1 = 0x012a4087 | §3.3 |
| rotrv rd, rt, rs r2 | rd = rt rotated right by (rs & 31) | R · op 0 · funct 6 · sa 1 | rotrv $t0,$t2,$t1 = 0x012a4046 | §8.2 |
Loads
Address = rs + signed 16-bit offset. Byte and half loads choose sign or zero extension.| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| lb rt, off(rs) | rt = one byte at rs+off, sign-extended | I · op 32 imm16, sign-extended | lb $t2,16($t1) = 0x812a0010 | §4.3 |
| lh rt, off(rs) | rt = one halfword at rs+off, sign-extended | I · op 33 imm16, sign-extended | lh $t2,16($t1) = 0x852a0010 | §4.3 |
| lw rt, off(rs) | rt = the word at rs+off | I · op 35 imm16, sign-extended | lw $t2,16($t1) = 0x8d2a0010 | §4.2 |
| lbu rt, off(rs) | rt = one byte at rs+off, zero-extended | I · op 36 imm16, sign-extended | lbu $t2,16($t1) = 0x912a0010 | §4.3 |
| lhu rt, off(rs) | rt = one halfword at rs+off, zero-extended | I · op 37 imm16, sign-extended | lhu $t2,16($t1) = 0x952a0010 | §4.3 |
Stores
Store the low byte, half or word of rt.| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| sb rt, off(rs) | the low byte of rt → memory at rs+off | I · op 40 imm16, sign-extended | sb $t2,16($t1) = 0xa12a0010 | §4.3 |
| sh rt, off(rs) | the low halfword of rt → memory at rs+off | I · op 41 imm16, sign-extended | sh $t2,16($t1) = 0xa52a0010 | §4.3 |
| sw rt, off(rs) | rt → the word at rs+off | I · op 43 imm16, sign-extended | sw $t2,16($t1) = 0xad2a0010 | §4.2 |
Branches
Target = PC+4 + offset×4. The instruction after the branch always runs (delay slot).| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| beq rs, rt, label slot | if rs == rt go to label (after the delay slot) | I · op 4 offset16 × 4, signed | beq $t1,$t2,0x00000044 = 0x112a0010 | §5.1 |
| bne rs, rt, label slot | if rs != rt go to label | I · op 5 offset16 × 4, signed | bne $t1,$t2,0x00000044 = 0x152a0010 | §5.1 |
| blez rs, label slot | if rs <= 0 go to label | I · op 6 offset16 × 4, signed | blez $t1,0x00000044 = 0x192a0010 | §5.2 |
| bgtz rs, label slot | if rs > 0 go to label | I · op 7 offset16 × 4, signed | bgtz $t1,0x00000044 = 0x1d2a0010 | §5.2 |
| bltz rs, label slot | if rs < 0 go to label | I · op 1 · rt 0 offset16 × 4, signed | bltz $t1,0x00000044 = 0x05200010 | §5.2 |
| bgez rs, label slot | if rs >= 0 go to label | I · op 1 · rt 1 offset16 × 4, signed | bgez $t1,0x00000044 = 0x05210010 | §5.2 |
| bltzal rs, label slot links | $ra = PC+8; if rs < 0 go to label | I · op 1 · rt 16 offset16 × 4, signed | bltzal $t1,0x00000044 = 0x05300010 | §5.3 |
| bgezal rs, label slot links | $ra = PC+8; if rs >= 0 go to label (bal is bgezal $zero) | I · op 1 · rt 17 offset16 × 4, signed | bgezal $t1,0x00000044 = 0x05310010 | §5.3 |
Jumps
j/jal keep the top 4 bits of PC+4; jr/jalr take an address from a register.| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| j target slot | go to target (same 256 MB region) | J · op 2 index26 × 4 | j 0x03c0018c = 0x08f00063 | §5.3 |
| jal target slot links | $ra = PC+8; go to target — the function call | J · op 3 index26 × 4 | jal 0x03c0018c = 0x0cf00063 | §5.3 |
| jr rs slot | go to the address in rs — the function return | R · op 0 · funct 8 | jr $t1 = 0x012a4088 | §5.3 |
| jalr rd, rs slot links | rd = PC+8 (default $ra); go to the address in rs | R · op 0 · funct 9 | jalr $t0,$t1 = 0x012a4089 | §5.3 |
Multiply and divide
mult/div write HI and LO; mul writes rd with the low word.| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| mult rs, rt | HI:LO = rs × rt, signed 64-bit product | R · op 0 · funct 24 | mult $t1,$t2 = 0x012a4098 | §7.1 |
| multu rs, rt | HI:LO = rs × rt, unsigned 64-bit product | R · op 0 · funct 25 | multu $t1,$t2 = 0x012a4099 | §7.1 |
| div rs, rt | LO = rs ÷ rt, HI = rs mod rt (signed) | R · op 0 · funct 26 | div $t1,$t2 = 0x012a409a | §7.2 |
| divu rs, rt | LO = rs ÷ rt, HI = rs mod rt (unsigned) | R · op 0 · funct 27 | divu $t1,$t2 = 0x012a409b | §7.2 |
| mul rd, rs, rt | rd = low 32 bits of rs × rt; HI/LO become undefined | R · op 28 · funct 2 | mul $t0,$t1,$t2 = 0x712a4082 | §7.2 |
| mfhi rd | rd = HI | R · op 0 · funct 16 | mfhi $t0 = 0x012a4090 | §7.1 |
| mflo rd | rd = LO | R · op 0 · funct 18 | mflo $t0 = 0x012a4092 | §7.1 |
| mthi rs | HI = rs | R · op 0 · funct 17 | mthi $t1 = 0x012a4091 | §7.1 |
| mtlo rs | LO = rs | R · op 0 · funct 19 | mtlo $t1 = 0x012a4093 | §7.1 |
Release-2 extras
Conditional moves, sign-extend, bit fields, count leading zeros.| instruction | what it does | encoding · immediate | example | § |
|---|---|---|---|---|
| movz rd, rs, rt | if rt == 0 then rd = rs (else rd unchanged) | R · op 0 · funct 10 | movz $t0,$t1,$t2 = 0x012a408a | §8.1 |
| movn rd, rs, rt | if rt != 0 then rd = rs (else rd unchanged) | R · op 0 · funct 11 | movn $t0,$t1,$t2 = 0x012a408b | §8.1 |
| clz rd, rs | rd = number of leading zero bits in rs (32 for zero) | R · op 28 · funct 32 | clz $t0,$t1 = 0x712a40a0 | §8.2 |
| clo rd, rs | rd = number of leading one bits in rs | R · op 28 · funct 33 | clo $t0,$t1 = 0x712a40a1 | §8.2 |
| seb rd, rt r2 | rd = the low byte of rt, sign-extended to 32 bits | R · op 31 · funct 32 · sa 16 | seb $t0,$t2 = 0x7d2a4420 | §8.1 |
| seh rd, rt r2 | rd = the low halfword of rt, sign-extended to 32 bits | R · op 31 · funct 32 · sa 24 | seh $t0,$t2 = 0x7d2a4620 | §8.1 |
| ext rt, rs, pos, size r2 | rt = bits pos+size-1..pos of rs, zero-extended | R · op 31 · funct 0 pos, size (in sa, rd) | ext $t2,$t1,2,9 = 0x7d2a4080 | §8.2 |
| ins rt, rs, pos, size r2 | replace bits pos+size-1..pos of rt with the low size bits of rs | R · op 31 · funct 4 pos, size (in sa, rd) | ins $t2,$t1,3,6 = 0x7d2a40c4 | §8.2 |
What to keep in mind
The rules a RISC-V reader trips over, each with the section that shows it running.
The delay slot
§5.4The instruction after every branch and jump runs whether or not the branch is taken. Your core squashes it today, so GCC fills every slot with nop (-fno-delayed-branch).
Link = PC + 8
§5.3jal, jalr, bgezal and bltzal write the address after the delay slot, not the next word. jr $ra therefore returns past the slot.
bgezal / bltzal link even when not taken
§5.3$ra is written before the condition is tested. bal label is bgezal $zero,label.
Branch target arithmetic
§5.1target = (address of the slot) + offset × 4, offset signed 16 bits: about ±128 KB. Relative to PC+4, not to the branch itself.
Jump target arithmetic
§5.3j and jal: target = top 4 bits of PC+4, then index × 4. A jump cannot leave its 256 MB region; jr/jalr can.
Only equality compares two registers
§5.2beq and bne are the only two-register branches. blez, bgtz, bltz, bgez compare with zero. a < b needs slt then bnez.
slt is the only compare
§3.4For >, >=, <= swap the operands or add xori rd,rd,1. sltiu rd,rs,1 tests rs == 0.
$zero is really zero
§2.1Reads give 0 and every write is dropped. nop is sll $zero,$zero,0; move rd,rs is or rd,rs,$zero (some assemblers: addu).
add traps, addu wraps
§3.1add, addi and sub trap on signed overflow; addu, addiu, subu wrap silently. GCC emits only the u forms. Your core does not implement the trap.
Three immediate extensions
§3.2addi, addiu, slti, sltiu and every load/store offset sign-extend. andi, ori, xori zero-extend. lui puts the 16 bits in the upper half and zeros the rest.
The %hi/%lo +1 rule
§3.2lui + addiu builds a 32-bit address. If bit 15 of the low half is set, addiu subtracts, so the linker adds 1 to the upper half.
li and la are not instructions
§9.1li is addiu (fits 16 signed) or ori (fits 16 unsigned) or lui + ori. la is lui + addiu with %hi/%lo. The dump shows the real words.
Shift counts
§3.3sll/srl/sra/rotr take the count from sa, the variable forms from the low 5 bits of rs. The value is always rt. srl and rotr share funct 2 and differ in the rs field (0/1); srlv and rotrv differ in sa.
Loads choose the extension
§4.3lb and lh sign-extend, lbu and lhu zero-extend. lw needs a 4-byte aligned address and lh/sh a 2-byte one; misaligned is an address error on real hardware and silently wrong on your core.
Little-endian
§4.1The byte at the lowest address is bits 7..0 of the word. sb stores the low byte of rt.
The stack grows down
§4.4Push is addiu $sp,$sp,-N then sw; $sp stays 8-byte aligned in o32. $fp is register 30, which objdump prints as s8.
o32 calling convention
§6.2Arguments in $a0–$a3 plus a 16-byte argument area the caller always reserves; results in $v0/$v1; $t* and $a* are clobbered by a call; $s*, $sp, $fp, $gp survive it.
HI and LO
§7.1mult/multu write the 64-bit product to HI:LO; div/divu put the quotient in LO and the remainder in HI. mul rd,rs,rt writes only the low 32 bits to rd. Divide by zero is undefined.
movz/movn read rd
§8.1The old value of rd is a third source: movz rd,rs,rt means rd = (rt == 0) ? rs : rd.
ext and ins fields
§8.2ext rt,rs,pos,size encodes size−1 in the rd field and pos in sa. ins rt,rs,pos,size encodes pos+size−1 in rd and pos in sa. seb/seh keep funct 32 and differ in sa (16/24).
clz of zero is 32
§8.2clz rd,rs counts leading zero bits; clo counts leading ones. Both are SPECIAL2 (opcode 28).
jalr rd,rs
§5.3The default rd is $ra. rs and rd must differ. jr and jalr targets must be 4-byte aligned.
Reserved registers
§2.2$at is the assembler’s scratch register (pseudo-instructions may use it). $k0/$k1 belong to the kernel. $gp is set by crt0 but unused with -G0.
Reset vector 0xBFC00000
§9.2Fixed by the architecture. crt0 is placed there by the linker script; it clears the registers, sets $gp and $sp, calls main and stores $v0 to the exit door.
The test machine’s doors
§4.1Stores to 0x10003FF0 print a character, 0x10003FF4 an integer, 0x10003FF8 ends the run with an exit code. They are the top 16 bytes of the 16 KB data memory (the 4 KB scenarios use 0x10000FF0…).
What a dump shows
§9.1objdump prints register names without $, s8 for $30, resolved branch targets with <symbol+offset>, and the aliases move, li, b, nop, beqz, bnez. Only the address and the word are in memory.
Generated from data/instructions/mips32.json, the table the book's opcode figures read, which was
produced from the Carolyne MIPS32 description; every example word was assembled by the site's own assembler
and decoded back. The register table is §2.2's. Print this page: the sidebar is left out.