pub enum Op {
Show 143 variants
Reserved,
Add,
Addu,
Sub,
Subu,
Dadd,
Daddu,
Dsub,
Dsubu,
Slt,
Sltu,
And,
Or,
Xor,
Nor,
Addi,
Addiu,
Daddi,
Daddiu,
Slti,
Sltiu,
Andi,
Ori,
Xori,
Lui,
Sll,
Srl,
Sra,
Dsll,
Dsrl,
Dsra,
Dsll32,
Dsrl32,
Dsra32,
Sllv,
Srlv,
Srav,
Dsllv,
Dsrlv,
Dsrav,
Mult,
Multu,
Div,
Divu,
Dmult,
Dmultu,
Ddiv,
Ddivu,
Mfhi,
Mthi,
Mflo,
Mtlo,
Lb,
Lbu,
Lh,
Lhu,
Lw,
Lwu,
Ld,
Sb,
Sh,
Sw,
Sd,
Ll,
Lld,
Sc,
Scd,
Lwl,
Lwr,
Ldl,
Ldr,
Swl,
Swr,
Sdl,
Sdr,
J,
Jal,
Jr,
Jalr,
Beq,
Bne,
Blez,
Bgtz,
Bltz,
Bgez,
Bltzal,
Bgezal,
Beql,
Bnel,
Blezl,
Bgtzl,
Bltzl,
Bgezl,
Bltzall,
Bgezall,
Bc1f,
Bc1t,
Bc1fl,
Bc1tl,
Tge,
Tgeu,
Tlt,
Tltu,
Teq,
Tne,
Tgei,
Tgeiu,
Tlti,
Tltiu,
Teqi,
Tnei,
Cache,
Cop0Extension,
Cfc1,
Ctc1,
Mfc1,
Dmfc1,
Mtc1,
Dmtc1,
Lwc1,
Ldc1,
Swc1,
Sdc1,
Cop1Unimplemented,
Cop1ReservedControl,
Cop2ReservedControl,
Mfc2,
Dmfc2,
Mtc2,
Cop2,
FpArith,
Tlbr,
Tlbwi,
Tlbwr,
Tlbp,
Eret,
Mfc0,
Dmfc0,
Mtc0,
Dmtc0,
Sync,
Syscall,
Break,
}Expand description
The decoded operation. Only the integer subset so far; see the module docs.
Variants§
Reserved
Not (yet) a recognized encoding — raises a reserved-instruction
exception rather than behaving as a NOP.
Add
ADD rd, rs, rt — traps on overflow.
Addu
ADDU rd, rs, rt.
Sub
SUB rd, rs, rt — traps on overflow.
Subu
SUBU rd, rs, rt.
Dadd
DADD rd, rs, rt — traps on overflow.
Daddu
DADDU rd, rs, rt.
Dsub
DSUB rd, rs, rt — traps on overflow.
Dsubu
DSUBU rd, rs, rt.
Slt
SLT rd, rs, rt.
Sltu
SLTU rd, rs, rt.
And
AND rd, rs, rt.
Or
OR rd, rs, rt.
Xor
XOR rd, rs, rt.
Nor
NOR rd, rs, rt.
Addi
ADDI rt, rs, imm — traps on overflow.
Addiu
ADDIU rt, rs, imm.
Daddi
DADDI rt, rs, imm — traps on overflow.
Daddiu
DADDIU rt, rs, imm.
Slti
SLTI rt, rs, imm.
Sltiu
SLTIU rt, rs, imm.
Andi
ANDI rt, rs, imm — immediate is zero-extended.
Ori
ORI rt, rs, imm — immediate is zero-extended.
Xori
XORI rt, rs, imm — immediate is zero-extended.
Lui
LUI rt, imm.
Sll
SLL rd, rt, sa. SLL $0, $0, 0 is the canonical NOP.
Srl
SRL rd, rt, sa.
Sra
SRA rd, rt, sa — reproduces the VR4300 erratum.
Dsll
DSLL rd, rt, sa.
Dsrl
DSRL rd, rt, sa.
Dsra
DSRA rd, rt, sa.
Dsll32
DSLL32 rd, rt, sa — sa + 32.
Dsrl32
DSRL32 rd, rt, sa — sa + 32.
Dsra32
DSRA32 rd, rt, sa — sa + 32.
Sllv
SLLV rd, rt, rs.
Srlv
SRLV rd, rt, rs.
Srav
SRAV rd, rt, rs — reproduces the VR4300 erratum.
Dsllv
DSLLV rd, rt, rs.
Dsrlv
DSRLV rd, rt, rs.
Dsrav
DSRAV rd, rt, rs.
Mult
MULT rs, rt.
Multu
MULTU rs, rt.
Div
DIV rs, rt.
Divu
DIVU rs, rt.
Dmult
DMULT rs, rt.
Dmultu
DMULTU rs, rt.
Ddiv
DDIV rs, rt.
Ddivu
DDIVU rs, rt.
Mfhi
MFHI rd.
Mthi
MTHI rs.
Mflo
MFLO rd.
Mtlo
MTLO rs.
Lb
LB rt, off(base) — signed byte.
Lbu
LBU rt, off(base).
Lh
LH rt, off(base) — signed halfword.
Lhu
LHU rt, off(base).
Lw
LW rt, off(base) — sign-extended into the 64-bit register.
Lwu
LWU rt, off(base) — zero-extended.
Ld
LD rt, off(base).
Sb
SB rt, off(base).
Sh
SH rt, off(base).
Sw
SW rt, off(base).
Sd
SD rt, off(base).
Ll
LL rt, off(base) — load word, sign-extend, set LLbit and LLAddr.
Lld
LLD rt, off(base) — the doubleword form.
Sc
SC rt, off(base) — store word iff LLbit; write the outcome to rt.
Scd
SCD rt, off(base) — the doubleword form.
Lwl
LWL rt, off(base).
Lwr
LWR rt, off(base).
Ldl
LDL rt, off(base).
Ldr
LDR rt, off(base).
Swl
SWL rt, off(base).
Swr
SWR rt, off(base).
Sdl
SDL rt, off(base).
Sdr
SDR rt, off(base).
J
J target — 26-bit region form.
Jal
JAL target — links to $31.
Jr
JR rs — register indirect.
Jalr
JALR rd, rs — register indirect, links to rd.
Beq
BEQ rs, rt, off.
Bne
BNE rs, rt, off.
Blez
BLEZ rs, off.
Bgtz
BGTZ rs, off.
Bltz
BLTZ rs, off.
Bgez
BGEZ rs, off.
Bltzal
BLTZAL rs, off — links to $31.
Bgezal
BGEZAL rs, off — links to $31.
Beql
BEQL — branch-likely.
Bnel
BNEL — branch-likely.
Blezl
BLEZL — branch-likely.
Bgtzl
BGTZL — branch-likely.
Bltzl
BLTZL — branch-likely.
Bgezl
BGEZL — branch-likely.
Bltzall
BLTZALL — branch-likely, links.
Bgezall
BGEZALL — branch-likely, links.
Bc1f
BC1F off — branch if the FP condition is clear.
Bc1t
BC1T off — branch if the FP condition is set.
Bc1fl
BC1FL — branch-likely on a clear FP condition.
Bc1tl
BC1TL — branch-likely on a set FP condition.
Tge
TGE rs, rt.
Tgeu
TGEU rs, rt.
Tlt
TLT rs, rt.
Tltu
TLTU rs, rt.
Teq
TEQ rs, rt.
Tne
TNE rs, rt.
Tgei
TGEI rs, imm.
Tgeiu
TGEIU rs, imm.
Tlti
TLTI rs, imm.
Tltiu
TLTIU rs, imm.
Teqi
TEQI rs, imm.
Tnei
TNEI rs, imm.
Cache
CACHE op, off(base) — a cache maintenance operation.
Operates on modeled cache state as of T-11-003: both primary caches hold real tags and data, so invalidate, write-back and the tag moves all act. This doc said “executed as an address-translating no-op” until the caches landed, which was true under ledger D-5 and is not any more — D-5 is superseded by D-6.
op’s rt slot is the operation selector, not a destination. What
mattered first is that it does not raise — IPL3 and libdragon both
issue it, so a Reserved decode blocks every real ROM. See docs/cpu.md.
Cop0Extension
A COP0 CO-class instruction in the funct 0x20-0x3F extension range,
executed as a no-op.
§Why this is not Reserved
n64-systemtest probes for the emux emulator by executing
COP0 CO funct 0x20 (its XDETECT) and reading the result out of a GPR.
It does this from init_allocator, inside entrypoint – before
main installs any exception handler. If a real VR4300 raised Reserved
Instruction there, the suite would derail on every N64 it has ever run
on, before printing a single line. It does not, so hardware must retire
these encodings harmlessly.
The range is not a guess: the suite’s own constant for the probe is named
XDETECT_CODE_EXTENSIONS_20_3F, i.e. emux claims funct 0x20-0x3F as
extension space precisely because the VR4300 leaves it inert.
Decoding these to Reserved is what made the suite appear to hang: the
RI dispatched to an uninstalled 0x8000_0180, ran zeros as NOPs into
.text, and faulted there instead.
Recorded as an inference in the accuracy ledger (C-8), not a manual citation – the writeback behavior of the target GPR is untested.
Cfc1
CFC1 rt, fs — read a COP1 control register.
Ctc1
CTC1 rt, fs — write a COP1 control register.
Mfc1
MFC1 rt, fs — move the low 32 bits of an FPR to a GPR, sign-extended.
Dmfc1
DMFC1 rt, fs — move a full 64-bit FGR to a GPR.
Mtc1
MTC1 rt, fs — move the low 32 bits of a GPR to an FPR.
Dmtc1
DMTC1 rt, fs — move a full 64-bit GPR to an FGR.
Lwc1
LWC1 ft, off(base) — load a word into an FPR.
Ldc1
LDC1 ft, off(base) — load a doubleword into an FPR.
Swc1
SWC1 ft, off(base) — store an FPR word.
Sdc1
SDC1 ft, off(base) — store an FPR doubleword.
Cop1Unimplemented
A COP1 encoding this crate does not implement.
Distinct from Op::Reserved: the encoding is valid, so it must raise
Coprocessor Unusable when Status.CU1 is clear rather than Reserved
Instruction. Conflating the two sends the handler the wrong ExcCode.
Cop1ReservedControl
DCFC1 / DCTC1 — the 64-bit forms of CFC1/CTC1, which the VR4300
does not implement.
They are not a silent no-op and not Reserved Instruction: with CU1
set they raise a floating-point exception whose FCSR.Cause is
only the unimplemented-operation bit, every other cause bit cleared.
With CU1 clear they raise Coprocessor Unusable like any COP1
instruction, and FCSR is left untouched.
Distinct from Op::Cop1Unimplemented on purpose: that one really does
retire silently, and folding these into it hides a trap behind a no-op.
Cop2ReservedControl
DCFC2 / DCTC2 — the 64-bit COP2 control moves.
COP2 exists on the VR4300 only as a stub, and these two encodings are
not implemented at all: with CU2 set they raise Reserved
Instruction, and with CU2 clear, Coprocessor Unusable.
Note the asymmetry with Op::Cop1ReservedControl, which raises a
floating-point exception in the equivalent position. The two
coprocessors decline in different ways and the encodings are otherwise
identical, so this is easy to get uniformly wrong.
Mfc2
MFC2 — read the COP2 latch’s low 32 bits, sign-extended.
Dmfc2
DMFC2 — read all 64 bits of the COP2 latch.
Mtc2
MTC2 / DMTC2 — write the COP2 latch.
Both write the whole 64-bit GPR, despite MTC2 being nominally a
32-bit move: n64-systemtest writes a 64-bit value with MTC2 and reads
all of it back with DMFC2.
Cop2
Any COP2 encoding.
The VR4300 has a COP2 unit, so these are architecturally valid
encodings. With Status.CU2 clear they raise Coprocessor Unusable,
not Reserved Instruction — the same distinction as
Op::Cop1Unimplemented, and for the same reason.
Decoding them as Reserved is what produced n64-systemtest’s
“Exception storm detected. Aborting.” during MFC2/MTC2/DMFC2/DMTC2:
the suite expects ExcCode 11 and got 10 five times running, which
tripped its recovery limit and truncated the whole run.
FpArith
A COP1 arithmetic operation, format and operation carried in the
already-decoded fields: rs is the format, funct the operation, with
rt=ft, rd=fs and sa=fd.
One variant rather than ~60, because the pipeline dispatches into
crate::fpu on (fmt, funct) anyway and a variant per opcode would just
be a second copy of that table.
Tlbr
TLBR — read the TLB entry Index names into the COP0 registers.
Tlbwi
TLBWI — write the COP0 registers into the entry Index names.
Tlbwr
TLBWR — write them into the entry Random names.
Tlbp
TLBP — probe for an entry matching EntryHi.
Eret
ERET — return from exception (UM Ch. 16, p. 434).
Has no delay slot and must not be placed in one, unlike every other control transfer in the instruction set.
Mfc0
MFC0 rt, rd — 32-bit read of a COP0 register, sign-extended.
Dmfc0
DMFC0 rt, rd — 64-bit read of a COP0 register.
Mtc0
MTC0 rt, rd — 32-bit write to a COP0 register.
Dmtc0
DMTC0 rt, rd — 64-bit write to a COP0 register.
Sync
SYNC — “handled as a NOP” on this processor (UM §3.1).
Not folded into Op::Sll-as-NOP: it is a distinct encoding that
compilers emit, and decoding it to Op::Reserved would raise a
reserved-instruction exception on code that runs fine on hardware.
Syscall
SYSCALL.
Break
BREAK.
Implementations§
Source§impl Op
impl Op
Sourcepub const fn is_64_bit(self) -> bool
pub const fn is_64_bit(self) -> bool
Is this one of the MIPS III 64-bit operations?
They raise a Reserved Instruction exception when executed in 32-bit User or Supervisor mode. The manual states it once, as the epsilon marker in the opcode table (UM Figure 16-1, Key): “The operation code marked with an epsilon is valid in the 64-bit mode and 32-bit Kernel mode. In the 32-bit User or Supervisor mode, this code generates the reserved instruction exception.”
That legend — not the per-instruction “Exceptions” notes — is the
authority, and reading it is what caught LWU: the set was first built
from n64-systemtest’s 28 tested instructions, which do not include it. Kernel mode may use them at any width.
The *32 shift forms are included on the same rule rather than by
extrapolation: DSLL32/DSRL32/DSRA32 carry the identical exception
note in the manual, being 64-bit operations by the same definition.
Not included: DMFC0/DMTC0 and DMFC1/DMTC1, which the table
does mark epsilon. Doubleword moves to and from a coprocessor are also
governed by that coprocessor’s own usability and reserved-encoding rules
(ledger C-18), and those raise a different exception; in User mode COP0
is unusable, so CpU is what hardware reports. n64-systemtest exercises
neither, so rather than pick an ordering on no evidence they stay out —
recorded here so the omission is a decision and not an oversight.
Sourcepub const fn has_delay_slot(self) -> bool
pub const fn has_delay_slot(self) -> bool
Does this instruction have a branch delay slot?
Every jump and branch on MIPS does. The instruction after it executes
before the target — which is why in_delay_slot has to travel with the
instruction rather than live in a global flag.
Sourcepub const fn reads_fp_condition(self) -> bool
pub const fn reads_fp_condition(self) -> bool
Does this instruction read FCSR.C?
Only the BC1 family does, which is why the condition can be interlocked
against rather than bypassed everywhere — see accuracy-ledger R-2 for
the interlock that is still outstanding.
Sourcepub const fn is_likely(self) -> bool
pub const fn is_likely(self) -> bool
Is this a branch-likely form?
When a likely branch is not taken it nullifies its delay slot — the instruction is fetched and then squashed. An ordinary branch executes its delay slot either way. Getting this backwards silently executes or skips one instruction per untaken branch.
Sourcepub const fn writes_hi_lo(self) -> bool
pub const fn writes_hi_lo(self) -> bool
Does this operation write HI/LO rather than a general register?
Used for the MFHI/MFLO hazard window: a MFHI followed within two
instructions by anything that writes HI produces hardware’s wrong
result, and that is a non-interlocked hazard (see
crate::alu::MFHI_MFLO_HAZARD_INSTRUCTIONS).