Here are some common instructions in MIPS I and their effects. Some of those effects are important for optimization purposes, so they'll be described in greater detail.
These instructions are common on the Sony PlayStation. Counter-intuitively, they are also much more common than 64-bit instructions on the Nintendo 64: because its data bus is 32-bit, loading and storing 64-bit quantities to memory would take twice as long, so most games were written with 32-bit data and instructions only. Some games could even be run on a pure 32-bit MIPS processor!
You'll want to read MIPS part 1: Registers and calling convention first.
In this post: Arithmetic logic unit (ALU) instructions!
ADD: 32-bit addition with signed overflow check (#add)
ADD Rd, Rs, Rt ; Rd <- Rs + Rt
Adds the two 32-bit quantities in registers Rs
and Rt
, storing the result in register Rd
. On 64-bit MIPS processors, ADD
sign-extends the 32-bit result by copying bit 31 into 32..63.
ADD
raises the Arithmetic Overflow exception if adding two positive integers, or two negative integers, and the sign of the result has been flipped. If that happens, the Rd
register is not written.
ADDU: 32-bit unsigned addition (#addu)
ADDU Rd, Rs, Rt ; Rd <- Rs + Rt
Adds the two 32-bit quantities in registers Rs
and Rt
, storing the result in register Rd
. Despite the nature of this instruction as an "unsigned" addition, on 64-bit MIPS processors, ADDU
sign-extends the 32-bit result by copying bit 31 into 32..63.
ADDU
does not raise any exceptions.
In programs, ADDU
is often used with $0
, whose value is always 0, for two effects:
ADDU Rd, Rs, $0 ; Rd <- Rs (32-bit move; sign-extends on 64-bit)
ADDU Rd, $0, $0 ; Rd <- 0
ADDI: 32-bit addition with immediate, with signed overflow check (#addi)
ADDI Rt, Rs, Imm16 ; Rt <- Rs + Imm16
Adds the 32-bit quantity in register Rs
and the value in Imm16
sign-extended to 32 bits. Stores the result in register Rt
. On 64-bit MIPS processors, ADDI
sign-extends the 32-bit result by copying bit 31 into 32..63.
If bit 15 of the immediate is set, the immediate is negative. Some MIPS assemblers recognize the pseudo-instruction SUBI
for this.
ADDI
raises the Arithmetic Overflow exception if adding two positive integers, or two negative integers, and the sign of the result has been flipped. If that happens, the Rd
register is not written.
ADDIU: 32-bit unsigned addition with immediate (#addiu)
ADDIU Rt, Rs, Imm16 ; Rt <- Rs + (int16_t) Imm16
Adds the 32-bit quantity in register Rs
and the value in Imm16
sign-extended to 32 bits. Stores the result in register Rt
. On 64-bit MIPS processors, ADDIU
sign-extends the 32-bit result by copying bit 31 into 32..63.
If bit 15 of the immediate is set, the immediate is negative. Some MIPS assemblers recognize the pseudo-instruction SUBIU
for this.
ADDIU
does not raise any exceptions.
Despite its name, ADDIU
sign-extends its immediate. The word unsigned in the description only refers to skipping the signed overflow check. This code, for example:
ADDIU $4, $4, 65535
really subtracts 1 from the value of $4
, due to 65535 being the two's complement representation of -1.
AND: Bitwise and (#and)
AND Rd, Rs, Rt ; Rd <- Rs & Rt
Performs the bitwise and operation on the values in registers Rs
and Rt
, storing the result in register Rd
. On 64-bit MIPS processors, AND
performs the operation on all 64 bits. However, the last operation that wrote the values in Rs
and Rt
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
In programs, AND
is often used to mask away bits. For example, if one of the operands is 0x1FFFFF
, only bits 0..20 may be set in the result. This can also be used to restrict values to a certain range or test whether a certain bit is set.
ANDI: Bitwise and with immediate (#andi)
ANDI Rt, Rs, Imm16 ; Rt <- Rs & (uint16_t) Imm16
Performs the bitwise and operation on the value in register Rs
and the 16-bit immediate, zero-extended all the way, storing the result in register Rt
. On 64-bit MIPS processors, ANDI
performs the operation on all 64 bits. However, bits 16..63 will always be unset in the result.
In programs, ANDI
is often used to mask away bits. For example, if the immediate is 0x00FF
, only the low byte may be set in the result. This can also be used to restrict values to a certain range or test whether a certain bit is set.
DIV: 32-bit signed division (#div)
DIV Rs, Rt ; (HI, LO) <- (int32_t) Rs / (int32_t) Rt
Divides the 32-bit quantity in register Rs
by the 32-bit quantity in register Rt
, interpreted as signed integers.
Results are written to the multiplier unit's registers, HI
and LO
. The quotient goes to LO
and the remainder goes to HI
. Both are signed as well. On 64-bit MIPS processors, DIV
sign-extends the 32-bit results by copying bit 31 of each register into its bits 32..63.
Unlike on other architectures, DIV
does not raise any exceptions. If Rt
contains the value 0, the contents of HI
and LO
become undefined. The division of -2147483648 by -1 (= +2147483648), whose result can't be represented in 32 bits, also makes HI
and LO
undefined.
DIVU: 32-bit unsigned division (#divu)
DIVU Rs, Rt ; (HI, LO) <- (uint32_t) Rs / (uint32_t) Rt
Divides the 32-bit quantity in register Rs
by the 32-bit quantity in register Rt
, interpreted as unsigned integers.
Results are written to the multiplier unit's registers, HI
and LO
. The quotient goes to LO
and the remainder goes to HI
. Both are unsigned as well. Despite the nature of this instruction as an "unsigned" division, on 64-bit MIPS processors, DIVU
sign-extends the 32-bit results by copying bit 31 of each register into its bits 32..63.
Unlike on other architectures, DIVU
does not raise any exceptions. If Rt
contains the value 0, the contents of HI
and LO
become undefined.
LUI: Load upper immediate (#lui)
LUI Rt, Imm16 ; Rt <- Imm16 << 16
Shifts the immediate value left by 16 bits (i.e. into the upper part of 32-bit registers) and writes it to register Rt
. On 64-bit MIPS processors, LUI
sign-extends this 32-bit result by copying bit 31 into 32..63. It does not instead shift the immediate value by 48 bits.
The lower 16 bits are filled with 0. Therefore, none of the bits that were in register Rt
are preserved.
In programs, LUI
is often paired with ADDIU
or ORI
to make 32-bit constants. LUI
can also be used with load and store instructions, which support 16-bit offsets from these upper parts, to access constant addresses in memory.
MFHI: Move from HI
(#mfhi)
MFHI Rd ; Rd <- HI
Retrieves into register Rd
the last result written to the multiplier unit's HI
register by a preceding DIV
, DIVU
, MULT
or MULTU
instruction.
Due to the pipelining in early MIPS processors, it is not possible to grab the value in the HI
register then immediately request another DIV
, DIVU
, MULT
or MULTU
. The multiplication or division request has already been seen and put into the pipeline 2 cycles ago, and it may have already written partial results into HI
and LO
.
For example, this instruction sequence is illegal:
MFHI $8
DIVU $4, $5
because the DIVU
has already started.
Two unrelated instructions (or NOP
, if no meaningful work can be scheduled) must appear between MFHI
and the next request:
MFHI $8
NOP
NOP
DIVU $4, $5
MFLO: Move from LO
(#mflo)
MFLO Rd ; Rd <- LO
Retrieves into register Rd
the last result written to the multiplier unit's LO
register by a preceding DIV
, DIVU
, MULT
or MULTU
instruction.
The notes on MFHI
also apply to MFLO
.
MULT: 32-bit signed multiplication (#mult)
MULT Rs, Rt ; (HI, LO) <- (int32_t) Rs * (int32_t) Rt
Multiplies the 32-bit quantities in registers Rs
and Rt
, interpreted as signed integers.
Results are written to the multiplier unit's registers, HI
and LO
, exactly as if the signed 64-bit result was chopped into HI
(bits 63..32) and LO
(bits 31..0). If the width of the result is 32 or less, the HI
register will be filled with copies of bit 31 of LO
. On 64-bit MIPS processors, MULT
further sign-extends the chopped 32-bit results by copying bit 31 of each register into its bits 32..63.
MULTU: 32-bit unsigned multiplication (#multu)
MULTU Rs, Rt ; (HI, LO) <- (uint32_t) Rs * (uint32_t) Rt
Multiplies the 32-bit quantities in registers Rs
and Rt
, interpreted as unsigned integers.
Results are written to the multiplier unit's registers, HI
and LO
, exactly as if the unsigned 64-bit result was chopped into HI
(bits 63..32) and LO
(bits 31..0). If the width of the result is 32 or less, the HI
register will be 0. Despite the nature of this instruction as an "unsigned" multiplication, on 64-bit MIPS processors, MULTU
further sign-extends the chopped 32-bit results by copying bit 31 of each register into its bits 32..63.
NOR: Bitwise not-or (#nor)
NOR Rd, Rs, Rt ; Rd <- ~(Rs | Rt)
Performs the bitwise or operation on the values in registers Rs
and Rt
, then toggles all bits of the result and stores it in register Rd
. On 64-bit MIPS processors, NOR
performs the operation on all 64 bits. However, the last operation that wrote the values in Rs
and Rt
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
In programs, NOR
is often used with $0
, whose value is always 0, for two effects:
NOR Rd, Rs, $0 ; Rd <- ~Rs (toggle all bits)
NOR Rd, $0, $0 ; Rd <- -1
OR: Bitwise or (#or)
OR Rd, Rs, Rt ; Rd <- Rs | Rt
Performs the bitwise or operation on the values in registers Rs
and Rt
, storing the result in register Rd
. On 64-bit MIPS processors, OR
performs the operation on all 64 bits. However, the last operation that wrote the values in Rs
and Rt
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
In programs, OR
is often used with $0
, whose value is always 0, for two effects:
OR Rd, Rs, $0 ; Rd <- Rs (32- or 64-bit move)
OR Rd, $0, $0 ; Rd <- 0
ORI: Bitwise or with immediate (#ori)
ORI Rt, Rs, Imm16 ; Rt <- Rs | (uint16_t) Imm16
Performs the bitwise or operation between the value in register Rs
and the immediate, zero-extended, storing the result in register Rt
. On 64-bit MIPS processors, ORI
performs the operation on all 64 bits. However, the last operation that wrote the values in Rs
and Rt
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
In programs, ORI
is often after LUI
to make the lower half of 32-bit constants, or to set bits in the value of a register.
SLL: Shift left logical (#sll)
SLL Rd, Rt, Imm5 ; Rd <- Rt << Imm5
Shifts the bits in register Rt
to the left (i.e. towards the most significant bit) by the number of bits in the immediate. Bits that would be shifted into bit 32 and up are discarded. The lower bits are filled with 0. The result is written to register Rd
. On 64-bit MIPS processors, SLL
sign-extends the 32-bit result by copying bit 31 into 32..63.
In programs, SLL
is often used to multiply values by powers of 2. Paired with SRA
, it can sign-extend the lower bits of a register. Paired with SRL
, it can mask away the high bits of a register.
SLLV: Shift left logical variable (#sllv)
SLLV Rd, Rt, Rs ; Rd <- Rt << (Rs & 0x1F)
Shifts the bits in register Rt
to the left (i.e. towards the most significant bit) by the number of bits contained in the low 5 bits of register Rs
. Bits that would be shifted into bit 32 and up are discarded. The lower bits are filled with 0. The result is written to register Rd
. On 64-bit MIPS processors, SLLV
sign-extends the 32-bit result by copying bit 31 into 32..63.
SLT: Set to signed less-than (#slt)
SLT Rd, Rs, Rt ; Rd <- (int) Rs < (int) Rt
If the value in register Rs
is less than the value in register Rt
, if both are interpreted as signed integers, writes 1 into register Rd
. Otherwise, writes 0. On 64-bit MIPS processors, SLT
performs the operation on all 64 bits. However, the last operation that wrote the values in Rs
and Rt
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
SLTU: Set to unsigned less-than (#sltu)
SLTU Rd, Rs, Rt ; Rd <- (unsigned int) Rs < (unsigned int) Rt
If the value in register Rs
is less than the value in register Rt
, if both are interpreted as unsigned integers, writes 1 into register Rd
. Otherwise, writes 0. On 64-bit MIPS processors, SLTU
performs the operation on all 64 bits. However, the last operation that wrote the values in Rs
and Rt
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
SLTI: Set to signed less-than immediate (#slti)
SLTI Rt, Rs, Imm16 ; Rt <- (int) Rs < (int16_t) Imm16
If the value in register Rs
is less than the sign-extended immediate, if both are interpreted as signed integers, writes 1 into register Rt
. Otherwise, writes 0. On 64-bit MIPS processors, SLTI
performs the operation on all 64 bits. However, the last operation that wrote the value in Rs
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
SLTIU: Set to unsigned less-than immediate (#sltiu)
SLTIU Rt, Rs, Imm16 ; Rt <- (unsigned int) Rs < (unsigned int) (int16_t) Imm16
If the value in register Rs
, interpreted as an unsigned integer, is less than the sign-extended immediate also reinterpreted as an unsigned integer, writes 1 into register Rt
. Otherwise, writes 0. On 64-bit MIPS processors, SLTIU
performs the operation on all 64 bits. However, the last operation that wrote the value in Rs
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
Despite its name, SLTIU
sign-extends its immediate before comparing it as an unsigned integer. This code, for example:
SLTIU $1, $4, -16384
really compares the value of register $4
with the value 4294950912. On 64-bit MIPS processors, it will be even more than that.
SRL: Shift right logical (#srl)
SRL Rd, Rt, Imm5 ; Rd <- (uint32_t) Rt >> Imm5
Shifts the bits in register Rt
to the right (i.e. towards the least significant bit) by the number of bits in the immediate. Bits that would be shifted below bit 0 are discarded. The upper bits are filled with 0. The result is written to register Rd
. On 64-bit MIPS processors, SRL
sign-extends the 32-bit result by copying bit 31 into 32..63. Therefore, if the shift amount is greater than 0, bits 32..63 will become 0.
In programs, SRL
is often used to divide unsigned values by powers of 2. Paired with SLL
, it can mask away the high bits of a register.
SRLV: Shift right logical variable (#srlv)
SRLV Rd, Rt, Rs ; Rd <- (uint32_t) Rt >> (Rs & 0x1F)
Shifts the bits in register Rt
to the right (i.e. towards the least significant bit) by the number of bits contained in the low 5 bits of register Rs
. Bits that would be shifted below bit 0 are discarded. The upper bits are filled with 0. The result is written to register Rd
. On 64-bit MIPS processors, SRLV
sign-extends the 32-bit result by copying bit 31 into 32..63.
SRA: Shift right arithmetic (#sra)
SRA Rd, Rt, Imm5 ; Rd <- (int32_t) Rt >> Imm5
Shifts the bits in register Rt
to the right (i.e. towards the least significant bit) by the number of bits in the immediate. Bits that would be shifted below bit 0 are discarded. The upper bits are filled with copies of bit 31 in register Rt
. The result is written to register Rd
. On 64-bit MIPS processors, SRA
also copies bit 31 into 32..63.
In programs, SRA
is often used to divide signed values by powers of 2. Paired with SLL
, it can sign-extend the lower bits of a register.
SRAV: Shift right arithmetic variable (#srav)
SRAV Rd, Rt, Rs ; Rd <- (int32_t) Rt >> (Rs & 0x1F)
Shifts the bits in register Rt
to the right (i.e. towards the least significant bit) by the number of bits contained in the low 5 bits of register Rs
. Bits that would be shifted below bit 0 are discarded. The upper bits are filled with copies of bit 31 in register Rt
. The result is written to register Rd
. On 64-bit MIPS processors, SRLV
sign-extends the 32-bit result by copying bit 31 into 32..63.
SUB: 32-bit subtraction with signed overflow check (#sub)
SUB Rd, Rs, Rt ; Rd <- Rs - Rt
Subtracts the 32-bit quantity in register Rt
from that of register Rs
, storing the result in register Rd
. On 64-bit MIPS processors, SUB
sign-extends the 32-bit result by copying bit 31 into 32..63.
SUB
raises the Arithmetic Overflow exception if subtracting a positive value from a negative value, or vice versa, and the sign of the result has been flipped. If that happens, the Rd register is not written.
SUBU: 32-bit unsigned subtraction (#subu)
SUBU Rd, Rs, Rt ; Rd <- Rs - Rt
Subtracts the 32-bit quantity in register Rt
from that of register Rs
, storing the result in register Rd
. Despite the nature of this instruction as an "unsigned" addition, on 64-bit MIPS processors, SUBU
sign-extends the 32-bit result by copying bit 31 into 32..63.
SUBU
does not raise any exceptions.
XOR: Bitwise exclusive-or (#xor)
XOR Rd, Rs, Rt ; Rd <- Rs ^ Rt
Performs the bitwise exclusive-or operation on the values in registers Rs
and Rt
, storing the result in register Rd
. On 64-bit MIPS processors, XOR
performs the operation on all 64 bits. However, the last operation that wrote the values in Rs
and Rt
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
In programs, XOR
is often used to toggle bits in the value of a register.
XORI: Bitwise exclusive-or with immediate (#xori)
XORI Rt, Rs, Imm16 ; Rt <- Rs ^ (uint16_t) Imm16
Performs the bitwise exclusive-or operation on the value in register Rs
and the immediate, zero-extended, storing the result in register Rt
. On 64-bit MIPS processors, XORI
performs the operation on all 64 bits. However, the last operation that wrote the value in Rs
may have been a 32-bit operation which placed copies of bit 31 in bits 32..63.
In programs, XORI
is often used to toggle bits in the value of a register.
Great reference, I've printed it out as it is the clearest explanation I've found, so thank you.
ReplyDeleteI think in the explanations for ADDU, NOR, and OR, when you mention how sometimes '$0 is used twice', it would be clearer to say, instead, that sometimes '$0 is used for both instruction parameters'.
Also, I was wondering why the instructions that take immediates are listed with Rt as the destination register, instead of Rd like all the others.
I'll admit that "$0 is used twice" wasn't the clearest thing for those instructions. I updated the post to show the uses of $0 with annotated instruction examples. You might want to print out the new post, and it should be the last one... I hope!
DeleteAs for the destination register of instructions with immediates being Rt, that's because the encoding of operands on MIPS is very regular.
* Rs is encoded in bits 21..25.
* Rt is encoded in bits 16..20.
* Rd is encoded in bits 11..15.
* Imm16 is encoded in bits 0..15.
Instructions with Imm16 can't use Rd because they'd overlap, so they use Rt instead.
Hope that helped!