rustyn64_cpu/decode.rs
1//! MIPS III instruction decode (T-11-002).
2//!
3//! Word in, [`Decoded`] out. Pure and total: **every** 32-bit pattern decodes to
4//! something, with anything unrecognized becoming [`Op::Reserved`] rather than a
5//! panic or a silent no-op. A guest can execute arbitrary bytes, so decode must
6//! not be able to fail.
7//!
8//! # Encoding
9//!
10//! Three formats, distinguished by the primary opcode in bits 31..26:
11//!
12//! ```text
13//! 31 26 25 21 20 16 15 11 10 6 5 0
14//! ┌────────┬──────┬──────┬──────┬──────┬──────┐
15//! │ opcode │ rs │ rt │ rd │ sa │ funct│ R-type (opcode == 0, SPECIAL)
16//! ├────────┼──────┼──────┼──────┴──────┴──────┤
17//! │ opcode │ rs │ rt │ immediate │ I-type
18//! ├────────┼──────┴──────┴──────┴──────┴──────┤
19//! │ opcode │ target │ J-type
20//! └────────┴──────────────────────────────────┘
21//! ```
22//!
23//! This module covers the **integer subset** that [`crate::alu`] implements.
24//! Loads, stores, branches, jumps, COP0, COP1 and the trap family decode to
25//! [`Op::Reserved`] for now — they arrive with T-11-003 and T-11-004. That is
26//! deliberate: an unimplemented opcode that decodes to `Reserved` raises a
27//! reserved-instruction exception, which is visible, rather than executing as a
28//! `NOP`, which silently produces wrong results.
29
30use serde::{Deserialize, Serialize};
31
32/// The decoded operation. Only the integer subset so far; see the module docs.
33#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
34pub enum Op {
35 /// Not (yet) a recognized encoding — raises a reserved-instruction
36 /// exception rather than behaving as a `NOP`.
37 #[default]
38 Reserved,
39
40 // --- arithmetic, register form
41 /// `ADD rd, rs, rt` — traps on overflow.
42 Add,
43 /// `ADDU rd, rs, rt`.
44 Addu,
45 /// `SUB rd, rs, rt` — traps on overflow.
46 Sub,
47 /// `SUBU rd, rs, rt`.
48 Subu,
49 /// `DADD rd, rs, rt` — traps on overflow.
50 Dadd,
51 /// `DADDU rd, rs, rt`.
52 Daddu,
53 /// `DSUB rd, rs, rt` — traps on overflow.
54 Dsub,
55 /// `DSUBU rd, rs, rt`.
56 Dsubu,
57 /// `SLT rd, rs, rt`.
58 Slt,
59 /// `SLTU rd, rs, rt`.
60 Sltu,
61
62 // --- logical, register form
63 /// `AND rd, rs, rt`.
64 And,
65 /// `OR rd, rs, rt`.
66 Or,
67 /// `XOR rd, rs, rt`.
68 Xor,
69 /// `NOR rd, rs, rt`.
70 Nor,
71
72 // --- arithmetic / logical, immediate form
73 /// `ADDI rt, rs, imm` — traps on overflow.
74 Addi,
75 /// `ADDIU rt, rs, imm`.
76 Addiu,
77 /// `DADDI rt, rs, imm` — traps on overflow.
78 Daddi,
79 /// `DADDIU rt, rs, imm`.
80 Daddiu,
81 /// `SLTI rt, rs, imm`.
82 Slti,
83 /// `SLTIU rt, rs, imm`.
84 Sltiu,
85 /// `ANDI rt, rs, imm` — immediate is **zero**-extended.
86 Andi,
87 /// `ORI rt, rs, imm` — immediate is **zero**-extended.
88 Ori,
89 /// `XORI rt, rs, imm` — immediate is **zero**-extended.
90 Xori,
91 /// `LUI rt, imm`.
92 Lui,
93
94 // --- shifts, immediate amount
95 /// `SLL rd, rt, sa`. `SLL $0, $0, 0` is the canonical `NOP`.
96 Sll,
97 /// `SRL rd, rt, sa`.
98 Srl,
99 /// `SRA rd, rt, sa` — reproduces the VR4300 erratum.
100 Sra,
101 /// `DSLL rd, rt, sa`.
102 Dsll,
103 /// `DSRL rd, rt, sa`.
104 Dsrl,
105 /// `DSRA rd, rt, sa`.
106 Dsra,
107 /// `DSLL32 rd, rt, sa` — `sa + 32`.
108 Dsll32,
109 /// `DSRL32 rd, rt, sa` — `sa + 32`.
110 Dsrl32,
111 /// `DSRA32 rd, rt, sa` — `sa + 32`.
112 Dsra32,
113
114 // --- shifts, register amount
115 /// `SLLV rd, rt, rs`.
116 Sllv,
117 /// `SRLV rd, rt, rs`.
118 Srlv,
119 /// `SRAV rd, rt, rs` — reproduces the VR4300 erratum.
120 Srav,
121 /// `DSLLV rd, rt, rs`.
122 Dsllv,
123 /// `DSRLV rd, rt, rs`.
124 Dsrlv,
125 /// `DSRAV rd, rt, rs`.
126 Dsrav,
127
128 // --- multiply / divide and the HI/LO moves
129 /// `MULT rs, rt`.
130 Mult,
131 /// `MULTU rs, rt`.
132 Multu,
133 /// `DIV rs, rt`.
134 Div,
135 /// `DIVU rs, rt`.
136 Divu,
137 /// `DMULT rs, rt`.
138 Dmult,
139 /// `DMULTU rs, rt`.
140 Dmultu,
141 /// `DDIV rs, rt`.
142 Ddiv,
143 /// `DDIVU rs, rt`.
144 Ddivu,
145 /// `MFHI rd`.
146 Mfhi,
147 /// `MTHI rs`.
148 Mthi,
149 /// `MFLO rd`.
150 Mflo,
151 /// `MTLO rs`.
152 Mtlo,
153
154 // --- aligned loads
155 /// `LB rt, off(base)` — signed byte.
156 Lb,
157 /// `LBU rt, off(base)`.
158 Lbu,
159 /// `LH rt, off(base)` — signed halfword.
160 Lh,
161 /// `LHU rt, off(base)`.
162 Lhu,
163 /// `LW rt, off(base)` — sign-extended into the 64-bit register.
164 Lw,
165 /// `LWU rt, off(base)` — zero-extended.
166 Lwu,
167 /// `LD rt, off(base)`.
168 Ld,
169
170 // --- aligned stores
171 /// `SB rt, off(base)`.
172 Sb,
173 /// `SH rt, off(base)`.
174 Sh,
175 /// `SW rt, off(base)`.
176 Sw,
177 /// `SD rt, off(base)`.
178 Sd,
179
180 // --- the synchronization pair (UM §16, pp. 453 and 487)
181 //
182 // The VR4300 is not a multiprocessor, but it implements these "in order to
183 // maintain compatibility with VR4400 and VR4200" (UM §3.1), so they are real
184 // instructions with observable behavior, not reserved encodings.
185 /// `LL rt, off(base)` — load word, sign-extend, set `LLbit` and `LLAddr`.
186 Ll,
187 /// `LLD rt, off(base)` — the doubleword form.
188 Lld,
189 /// `SC rt, off(base)` — store word iff `LLbit`; write the outcome to `rt`.
190 Sc,
191 /// `SCD rt, off(base)` — the doubleword form.
192 Scd,
193
194 // --- the unaligned family (used in pairs; see [`crate::mem`])
195 /// `LWL rt, off(base)`.
196 Lwl,
197 /// `LWR rt, off(base)`.
198 Lwr,
199 /// `LDL rt, off(base)`.
200 Ldl,
201 /// `LDR rt, off(base)`.
202 Ldr,
203 /// `SWL rt, off(base)`.
204 Swl,
205 /// `SWR rt, off(base)`.
206 Swr,
207 /// `SDL rt, off(base)`.
208 Sdl,
209 /// `SDR rt, off(base)`.
210 Sdr,
211
212 // --- jumps
213 /// `J target` — 26-bit region form.
214 J,
215 /// `JAL target` — links to `$31`.
216 Jal,
217 /// `JR rs` — register indirect.
218 Jr,
219 /// `JALR rd, rs` — register indirect, links to `rd`.
220 Jalr,
221
222 // --- branches. The `*L` forms are **branch-likely**: when NOT taken they
223 // nullify the delay slot instead of executing it.
224 /// `BEQ rs, rt, off`.
225 Beq,
226 /// `BNE rs, rt, off`.
227 Bne,
228 /// `BLEZ rs, off`.
229 Blez,
230 /// `BGTZ rs, off`.
231 Bgtz,
232 /// `BLTZ rs, off`.
233 Bltz,
234 /// `BGEZ rs, off`.
235 Bgez,
236 /// `BLTZAL rs, off` — links to `$31`.
237 Bltzal,
238 /// `BGEZAL rs, off` — links to `$31`.
239 Bgezal,
240 /// `BEQL` — branch-likely.
241 Beql,
242 /// `BNEL` — branch-likely.
243 Bnel,
244 /// `BLEZL` — branch-likely.
245 Blezl,
246 /// `BGTZL` — branch-likely.
247 Bgtzl,
248 /// `BLTZL` — branch-likely.
249 Bltzl,
250 /// `BGEZL` — branch-likely.
251 Bgezl,
252 /// `BLTZALL` — branch-likely, links.
253 Bltzall,
254 /// `BGEZALL` — branch-likely, links.
255 Bgezall,
256 /// `BC1F off` — branch if the FP condition is **clear**.
257 Bc1f,
258 /// `BC1T off` — branch if the FP condition is **set**.
259 Bc1t,
260 /// `BC1FL` — branch-likely on a clear FP condition.
261 Bc1fl,
262 /// `BC1TL` — branch-likely on a set FP condition.
263 Bc1tl,
264
265 // --- the trap family
266 /// `TGE rs, rt`.
267 Tge,
268 /// `TGEU rs, rt`.
269 Tgeu,
270 /// `TLT rs, rt`.
271 Tlt,
272 /// `TLTU rs, rt`.
273 Tltu,
274 /// `TEQ rs, rt`.
275 Teq,
276 /// `TNE rs, rt`.
277 Tne,
278 /// `TGEI rs, imm`.
279 Tgei,
280 /// `TGEIU rs, imm`.
281 Tgeiu,
282 /// `TLTI rs, imm`.
283 Tlti,
284 /// `TLTIU rs, imm`.
285 Tltiu,
286 /// `TEQI rs, imm`.
287 Teqi,
288 /// `TNEI rs, imm`.
289 Tnei,
290
291 // --- COP0 access (T-12-001). The TLB and `ERET` encodings of this opcode
292 // are NOT here: they are separate instructions landing in T-12-002/T-12-004,
293 // and lumping them in would make `Op` claim support this crate lacks.
294 /// `CACHE op, off(base)` — a cache maintenance operation.
295 ///
296 /// Operates on **modeled cache state** as of T-11-003: both primary caches
297 /// hold real tags and data, so invalidate, write-back and the tag moves all
298 /// act. This doc said "executed as an address-translating no-op" until the
299 /// caches landed, which was true under ledger **D-5** and is not any more —
300 /// D-5 is superseded by **D-6**.
301 ///
302 /// `op`'s `rt` slot is the operation selector, not a destination. What
303 /// mattered first is that it does **not raise** — IPL3 and libdragon both
304 /// issue it, so a `Reserved` decode blocks every real ROM. See `docs/cpu.md`.
305 Cache,
306 /// A COP0 **CO-class instruction in the `funct` 0x20-0x3F extension range**,
307 /// executed as a no-op.
308 ///
309 /// # Why this is not `Reserved`
310 ///
311 /// n64-systemtest probes for the `emux` emulator by executing
312 /// `COP0 CO funct 0x20` (its `XDETECT`) and reading the result out of a GPR.
313 /// It does this from `init_allocator`, inside `entrypoint` -- **before**
314 /// `main` installs any exception handler. If a real VR4300 raised Reserved
315 /// Instruction there, the suite would derail on every N64 it has ever run
316 /// on, before printing a single line. It does not, so hardware must retire
317 /// these encodings harmlessly.
318 ///
319 /// The range is not a guess: the suite's own constant for the probe is named
320 /// `XDETECT_CODE_EXTENSIONS_20_3F`, i.e. emux claims `funct` 0x20-0x3F as
321 /// extension space precisely because the VR4300 leaves it inert.
322 ///
323 /// Decoding these to `Reserved` is what made the suite appear to hang: the
324 /// RI dispatched to an uninstalled `0x8000_0180`, ran zeros as `NOP`s into
325 /// `.text`, and faulted there instead.
326 ///
327 /// Recorded as an **inference** in the accuracy ledger (C-8), not a manual
328 /// citation -- the writeback behavior of the target GPR is untested.
329 Cop0Extension,
330 /// `CFC1 rt, fs` — read a COP1 **control** register.
331 Cfc1,
332 /// `CTC1 rt, fs` — write a COP1 **control** register.
333 Ctc1,
334 /// `MFC1 rt, fs` — move the low 32 bits of an FPR to a GPR, sign-extended.
335 Mfc1,
336 /// `DMFC1 rt, fs` — move a full 64-bit FGR to a GPR.
337 Dmfc1,
338 /// `MTC1 rt, fs` — move the low 32 bits of a GPR to an FPR.
339 Mtc1,
340 /// `DMTC1 rt, fs` — move a full 64-bit GPR to an FGR.
341 Dmtc1,
342 /// `LWC1 ft, off(base)` — load a word into an FPR.
343 Lwc1,
344 /// `LDC1 ft, off(base)` — load a doubleword into an FPR.
345 Ldc1,
346 /// `SWC1 ft, off(base)` — store an FPR word.
347 Swc1,
348 /// `SDC1 ft, off(base)` — store an FPR doubleword.
349 Sdc1,
350 /// A COP1 encoding this crate does not implement.
351 ///
352 /// Distinct from [`Op::Reserved`]: the encoding is *valid*, so it must raise
353 /// **Coprocessor Unusable** when `Status.CU1` is clear rather than Reserved
354 /// Instruction. Conflating the two sends the handler the wrong `ExcCode`.
355 Cop1Unimplemented,
356 /// `DCFC1` / `DCTC1` — the 64-bit forms of `CFC1`/`CTC1`, which the VR4300
357 /// **does not implement**.
358 ///
359 /// They are not a silent no-op and not Reserved Instruction: with `CU1`
360 /// set they raise a floating-point exception whose `FCSR.Cause` is
361 /// *only* the unimplemented-operation bit, every other cause bit cleared.
362 /// With `CU1` clear they raise Coprocessor Unusable like any COP1
363 /// instruction, and `FCSR` is left untouched.
364 ///
365 /// Distinct from [`Op::Cop1Unimplemented`] on purpose: that one really does
366 /// retire silently, and folding these into it hides a trap behind a no-op.
367 Cop1ReservedControl,
368 /// `DCFC2` / `DCTC2` — the 64-bit COP2 control moves.
369 ///
370 /// COP2 exists on the VR4300 only as a stub, and these two encodings are
371 /// not implemented at all: with `CU2` set they raise **Reserved
372 /// Instruction**, and with `CU2` clear, Coprocessor Unusable.
373 ///
374 /// Note the asymmetry with [`Op::Cop1ReservedControl`], which raises a
375 /// *floating-point* exception in the equivalent position. The two
376 /// coprocessors decline in different ways and the encodings are otherwise
377 /// identical, so this is easy to get uniformly wrong.
378 Cop2ReservedControl,
379 /// `MFC2` — read the COP2 latch's low 32 bits, sign-extended.
380 Mfc2,
381 /// `DMFC2` — read all 64 bits of the COP2 latch.
382 Dmfc2,
383 /// `MTC2` / `DMTC2` — write the COP2 latch.
384 ///
385 /// Both write the **whole 64-bit** GPR, despite `MTC2` being nominally a
386 /// 32-bit move: n64-systemtest writes a 64-bit value with `MTC2` and reads
387 /// all of it back with `DMFC2`.
388 Mtc2,
389 /// Any **COP2** encoding.
390 ///
391 /// The VR4300 has a COP2 unit, so these are architecturally *valid*
392 /// encodings. With `Status.CU2` clear they raise **Coprocessor Unusable**,
393 /// not Reserved Instruction — the same distinction as
394 /// [`Op::Cop1Unimplemented`], and for the same reason.
395 ///
396 /// Decoding them as `Reserved` is what produced n64-systemtest's
397 /// "Exception storm detected. Aborting." during `MFC2/MTC2/DMFC2/DMTC2`:
398 /// the suite expects `ExcCode 11` and got `10` five times running, which
399 /// tripped its recovery limit and truncated the whole run.
400 Cop2,
401 /// A COP1 **arithmetic** operation, format and operation carried in the
402 /// already-decoded fields: `rs` is the format, `funct` the operation, with
403 /// `rt`=ft, `rd`=fs and `sa`=fd.
404 ///
405 /// One variant rather than ~60, because the pipeline dispatches into
406 /// `crate::fpu` on `(fmt, funct)` anyway and a variant per opcode would just
407 /// be a second copy of that table.
408 FpArith,
409 /// `TLBR` — read the TLB entry `Index` names into the COP0 registers.
410 Tlbr,
411 /// `TLBWI` — write the COP0 registers into the entry **`Index`** names.
412 Tlbwi,
413 /// `TLBWR` — write them into the entry **`Random`** names.
414 Tlbwr,
415 /// `TLBP` — probe for an entry matching `EntryHi`.
416 Tlbp,
417 /// `ERET` — return from exception (UM Ch. 16, p. 434).
418 ///
419 /// Has **no delay slot** and must not be placed in one, unlike every other
420 /// control transfer in the instruction set.
421 Eret,
422 /// `MFC0 rt, rd` — 32-bit read of a COP0 register, sign-extended.
423 Mfc0,
424 /// `DMFC0 rt, rd` — 64-bit read of a COP0 register.
425 Dmfc0,
426 /// `MTC0 rt, rd` — 32-bit write to a COP0 register.
427 Mtc0,
428 /// `DMTC0 rt, rd` — 64-bit write to a COP0 register.
429 Dmtc0,
430
431 /// `SYNC` — *"handled as a NOP"* on this processor (UM §3.1).
432 ///
433 /// Not folded into [`Op::Sll`]-as-NOP: it is a distinct encoding that
434 /// compilers emit, and decoding it to [`Op::Reserved`] would raise a
435 /// reserved-instruction exception on code that runs fine on hardware.
436 Sync,
437 /// `SYSCALL`.
438 Syscall,
439 /// `BREAK`.
440 Break,
441}
442
443impl Op {
444 /// Is this one of the MIPS III **64-bit operations**?
445 ///
446 /// They raise a Reserved Instruction exception when executed in 32-bit User
447 /// or Supervisor mode. The manual states it once, as the **epsilon** marker
448 /// in the opcode table (UM Figure 16-1, Key): *"The operation code marked
449 /// with an epsilon is valid in the 64-bit mode and 32-bit Kernel mode. In
450 /// the 32-bit User or Supervisor mode, this code generates the reserved
451 /// instruction exception."*
452 ///
453 /// That legend — not the per-instruction "Exceptions" notes — is the
454 /// authority, and reading it is what caught `LWU`: the set was first built
455 /// from n64-systemtest's 28 tested instructions, which do not include it. Kernel mode may use them at any width.
456 ///
457 /// The `*32` shift forms are included on the same rule rather than by
458 /// extrapolation: `DSLL32`/`DSRL32`/`DSRA32` carry the identical exception
459 /// note in the manual, being 64-bit operations by the same definition.
460 ///
461 /// **Not** included: `DMFC0`/`DMTC0` and `DMFC1`/`DMTC1`, which the table
462 /// does mark epsilon. Doubleword moves to and from a coprocessor are also
463 /// governed by that coprocessor's own usability and reserved-encoding rules
464 /// (ledger C-18), and those raise a *different* exception; in User mode COP0
465 /// is unusable, so `CpU` is what hardware reports. n64-systemtest exercises
466 /// neither, so rather than pick an ordering on no evidence they stay out —
467 /// recorded here so the omission is a decision and not an oversight.
468 #[must_use]
469 pub const fn is_64_bit(self) -> bool {
470 matches!(
471 self,
472 Self::Dadd
473 | Self::Daddi
474 | Self::Daddiu
475 | Self::Daddu
476 | Self::Ddiv
477 | Self::Ddivu
478 | Self::Dmult
479 | Self::Dmultu
480 | Self::Dsll
481 | Self::Dsll32
482 | Self::Dsllv
483 | Self::Dsra
484 | Self::Dsra32
485 | Self::Dsrav
486 | Self::Dsrl
487 | Self::Dsrl32
488 | Self::Dsrlv
489 | Self::Dsub
490 | Self::Dsubu
491 | Self::Ld
492 | Self::Lwu
493 | Self::Ldl
494 | Self::Ldr
495 | Self::Lld
496 | Self::Scd
497 | Self::Sd
498 | Self::Sdl
499 | Self::Sdr
500 )
501 }
502
503 /// Does this instruction have a branch delay slot?
504 ///
505 /// Every jump and branch on MIPS does. The instruction *after* it executes
506 /// before the target — which is why `in_delay_slot` has to travel with the
507 /// instruction rather than live in a global flag.
508 #[must_use]
509 pub const fn has_delay_slot(self) -> bool {
510 matches!(
511 self,
512 Self::J
513 | Self::Jal
514 | Self::Jr
515 | Self::Jalr
516 | Self::Beq
517 | Self::Bne
518 | Self::Blez
519 | Self::Bgtz
520 | Self::Bltz
521 | Self::Bgez
522 | Self::Bltzal
523 | Self::Bgezal
524 | Self::Beql
525 | Self::Bnel
526 | Self::Blezl
527 | Self::Bgtzl
528 | Self::Bltzl
529 | Self::Bgezl
530 | Self::Bltzall
531 | Self::Bgezall
532 | Self::Bc1f
533 | Self::Bc1t
534 | Self::Bc1fl
535 | Self::Bc1tl
536 )
537 }
538
539 /// Does this instruction read `FCSR.C`?
540 ///
541 /// Only the `BC1` family does, which is why the condition can be interlocked
542 /// against rather than bypassed everywhere — see accuracy-ledger **R-2** for
543 /// the interlock that is still outstanding.
544 #[must_use]
545 pub const fn reads_fp_condition(self) -> bool {
546 matches!(self, Self::Bc1f | Self::Bc1t | Self::Bc1fl | Self::Bc1tl)
547 }
548
549 /// Is this a **branch-likely** form?
550 ///
551 /// When a likely branch is *not* taken it **nullifies** its delay slot — the
552 /// instruction is fetched and then squashed. An ordinary branch executes its
553 /// delay slot either way. Getting this backwards silently executes or skips
554 /// one instruction per untaken branch.
555 #[must_use]
556 pub const fn is_likely(self) -> bool {
557 matches!(
558 self,
559 Self::Beql
560 | Self::Bnel
561 | Self::Blezl
562 | Self::Bgtzl
563 | Self::Bltzl
564 | Self::Bgezl
565 | Self::Bltzall
566 | Self::Bgezall
567 | Self::Bc1fl
568 | Self::Bc1tl
569 )
570 }
571
572 /// Does this operation write `HI`/`LO` rather than a general register?
573 ///
574 /// Used for the `MFHI`/`MFLO` hazard window: a `MFHI` followed within two
575 /// instructions by anything that writes `HI` produces hardware's wrong
576 /// result, and that is a *non-interlocked* hazard (see
577 /// [`crate::alu::MFHI_MFLO_HAZARD_INSTRUCTIONS`]).
578 #[must_use]
579 pub const fn writes_hi_lo(self) -> bool {
580 matches!(
581 self,
582 Self::Mult
583 | Self::Multu
584 | Self::Div
585 | Self::Divu
586 | Self::Dmult
587 | Self::Dmultu
588 | Self::Ddiv
589 | Self::Ddivu
590 | Self::Mthi
591 | Self::Mtlo
592 )
593 }
594}
595
596/// A decoded instruction: the operation plus its raw encoded fields.
597///
598/// The fields are kept as encoded (`rs`, `rt`, `rd`, `sa`, `imm`) rather than
599/// resolved into "operands", because the load-delay interlock matches on the
600/// **fields** whether or not they are used as sources
601/// (see [`crate::pipeline::load_interlocks`]). Resolving them away would make
602/// that check impossible to state correctly.
603#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
604pub struct Decoded {
605 /// The operation.
606 pub op: Op,
607 /// `rs` field, bits 25..21.
608 pub rs: u8,
609 /// `rt` field, bits 20..16.
610 pub rt: u8,
611 /// `rd` field, bits 15..11.
612 pub rd: u8,
613 /// Shift amount, bits 10..6 — already adjusted by +32 for the `*32` forms,
614 /// so it is the **effective** amount the shift helpers expect.
615 pub sa: u32,
616 /// Immediate, bits 15..0, unextended.
617 pub imm: u16,
618 /// The general register this writes, or 0 for none. `$zero` is never
619 /// actually written, so 0 doubles as "no destination".
620 pub dest: u8,
621 /// The J-type 26-bit target field, bits 25..0. Shifted left 2 and combined
622 /// with the delay slot's region bits to form the address.
623 pub target: u32,
624}
625
626impl Decoded {
627 /// Does this instruction load into a general register?
628 ///
629 /// The load-delay interlock keys off this: only a *load* result is
630 /// unavailable in time to bypass, which is why the interlock exists at all.
631 #[must_use]
632 pub const fn is_load(self) -> bool {
633 matches!(
634 self.op,
635 Op::Lb
636 | Op::Lbu
637 | Op::Lh
638 | Op::Lhu
639 | Op::Lw
640 | Op::Lwu
641 | Op::Ld
642 | Op::Lwl
643 | Op::Lwr
644 | Op::Ldl
645 | Op::Ldr
646 | Op::Ll
647 | Op::Lld
648 )
649 }
650
651 /// Does this instruction write `rt` with a value the `DC` stage produces
652 /// *without* going to memory for it?
653 ///
654 /// `SC`/`SCD` are the only such forms: they write the success flag to `rt`
655 /// whether or not the store happens (UM §16 p. 487, *"A successful SC
656 /// instruction sets the contents of general purpose register rt to 1; an
657 /// unsuccessful SC instruction sets it to 0"*). They are therefore stores
658 /// that also have a register destination — a shape nothing else in the
659 /// integer set has, and one that a `is_load`-vs-store dichotomy silently
660 /// gets wrong in both directions.
661 ///
662 /// Deliberately **not** folded into [`Self::is_load`]: the load-delay
663 /// interlock exists because a *memory* result is not ready in time, and the
664 /// `SC` flag is not a memory result. Treating it as a load would stall a
665 /// cycle the hardware does not.
666 #[must_use]
667 pub const fn is_store_conditional(self) -> bool {
668 matches!(self.op, Op::Sc | Op::Scd)
669 }
670
671 /// Does this instruction target a floating-point register?
672 ///
673 /// Always `false` for the integer subset. Present because the load-delay
674 /// interlock does **not** cross the GPR/FPR boundary, so the check needs to
675 /// know which file a destination belongs to.
676 #[must_use]
677 pub const fn targets_fpr(self) -> bool {
678 false
679 }
680}
681
682// Primary opcodes (bits 31..26).
683const OP_SPECIAL: u32 = 0o00;
684const OP_ADDI: u32 = 0o10;
685const OP_ADDIU: u32 = 0o11;
686const OP_SLTI: u32 = 0o12;
687const OP_SLTIU: u32 = 0o13;
688const OP_ANDI: u32 = 0o14;
689const OP_ORI: u32 = 0o15;
690const OP_XORI: u32 = 0o16;
691const OP_LUI: u32 = 0o17;
692const OP_DADDI: u32 = 0o30;
693const OP_DADDIU: u32 = 0o31;
694const OP_LDL: u32 = 0o32;
695const OP_LDR: u32 = 0o33;
696const OP_LB: u32 = 0o40;
697const OP_LH: u32 = 0o41;
698const OP_LWL: u32 = 0o42;
699const OP_LW: u32 = 0o43;
700const OP_LBU: u32 = 0o44;
701const OP_LHU: u32 = 0o45;
702const OP_LWR: u32 = 0o46;
703const OP_LWU: u32 = 0o47;
704const OP_SB: u32 = 0o50;
705const OP_SH: u32 = 0o51;
706const OP_SWL: u32 = 0o52;
707const OP_SW: u32 = 0o53;
708const OP_SDL: u32 = 0o54;
709const OP_SDR: u32 = 0o55;
710const OP_SWR: u32 = 0o56;
711const OP_COP0: u32 = 0o20;
712const OP_COP1: u32 = 0o21;
713/// COP2.
714const OP_COP2: u32 = 0o22;
715const OP_LWC1: u32 = 0o61;
716const OP_LDC1: u32 = 0o65;
717const OP_SWC1: u32 = 0o71;
718const OP_SDC1: u32 = 0o75;
719const OP_CACHE: u32 = 0o57;
720const OP_LL: u32 = 0o60;
721const OP_LLD: u32 = 0o64;
722const OP_SC: u32 = 0o70;
723const OP_SCD: u32 = 0o74;
724const OP_LD: u32 = 0o67;
725const OP_SD: u32 = 0o77;
726const OP_REGIMM: u32 = 0o01;
727const OP_J: u32 = 0o02;
728const OP_JAL: u32 = 0o03;
729const OP_BEQ: u32 = 0o04;
730const OP_BNE: u32 = 0o05;
731const OP_BLEZ: u32 = 0o06;
732const OP_BGTZ: u32 = 0o07;
733const OP_BEQL: u32 = 0o24;
734const OP_BNEL: u32 = 0o25;
735const OP_BLEZL: u32 = 0o26;
736const OP_BGTZL: u32 = 0o27;
737
738/// Decode one instruction word. Total — never fails, never panics.
739#[must_use]
740#[allow(clippy::too_many_lines)] // a flat opcode table reads better than nested helpers
741pub const fn decode(word: u32) -> Decoded {
742 let opcode = word >> 26;
743 let rs = ((word >> 21) & 0x1F) as u8;
744 let rt = ((word >> 16) & 0x1F) as u8;
745 let rd = ((word >> 11) & 0x1F) as u8;
746 let sa = (word >> 6) & 0x1F;
747 let imm = (word & 0xFFFF) as u16;
748 let funct = word & 0x3F;
749
750 let base = Decoded {
751 op: Op::Reserved,
752 rs,
753 rt,
754 rd,
755 sa,
756 imm,
757 dest: 0,
758 target: word & 0x03FF_FFFF,
759 };
760
761 // R-type: the operation is in `funct`, and the destination is `rd`.
762 macro_rules! r {
763 ($op:expr) => {
764 Decoded {
765 op: $op,
766 dest: rd,
767 ..base
768 }
769 };
770 }
771 // R-type shift by 32: the encoded 5-bit `sa` means `sa + 32`.
772 macro_rules! r32 {
773 ($op:expr) => {
774 Decoded {
775 op: $op,
776 dest: rd,
777 sa: sa + 32,
778 ..base
779 }
780 };
781 }
782 // I-type: the destination is `rt`.
783 macro_rules! i {
784 ($op:expr) => {
785 Decoded {
786 op: $op,
787 dest: rt,
788 ..base
789 }
790 };
791 }
792 // Writes HI/LO, so no general-register destination.
793 macro_rules! hilo {
794 ($op:expr) => {
795 Decoded { op: $op, ..base }
796 };
797 }
798
799 match opcode {
800 OP_SPECIAL => match funct {
801 0o00 => r!(Op::Sll),
802 0o02 => r!(Op::Srl),
803 0o03 => r!(Op::Sra),
804 0o04 => r!(Op::Sllv),
805 0o06 => r!(Op::Srlv),
806 0o07 => r!(Op::Srav),
807 0o20 => r!(Op::Mfhi),
808 0o21 => hilo!(Op::Mthi),
809 0o22 => r!(Op::Mflo),
810 0o23 => hilo!(Op::Mtlo),
811 0o24 => r!(Op::Dsllv),
812 0o26 => r!(Op::Dsrlv),
813 0o27 => r!(Op::Dsrav),
814 0o30 => hilo!(Op::Mult),
815 0o31 => hilo!(Op::Multu),
816 0o32 => hilo!(Op::Div),
817 0o33 => hilo!(Op::Divu),
818 0o34 => hilo!(Op::Dmult),
819 0o35 => hilo!(Op::Dmultu),
820 0o36 => hilo!(Op::Ddiv),
821 0o37 => hilo!(Op::Ddivu),
822 0o40 => r!(Op::Add),
823 0o41 => r!(Op::Addu),
824 0o42 => r!(Op::Sub),
825 0o43 => r!(Op::Subu),
826 0o44 => r!(Op::And),
827 0o45 => r!(Op::Or),
828 0o46 => r!(Op::Xor),
829 0o47 => r!(Op::Nor),
830 0o52 => r!(Op::Slt),
831 0o53 => r!(Op::Sltu),
832 0o54 => r!(Op::Dadd),
833 0o55 => r!(Op::Daddu),
834 0o56 => r!(Op::Dsub),
835 0o57 => r!(Op::Dsubu),
836 0o10 => Decoded { op: Op::Jr, ..base },
837 0o11 => r!(Op::Jalr),
838 0o14 => Decoded {
839 op: Op::Syscall,
840 ..base
841 },
842 0o15 => Decoded {
843 op: Op::Break,
844 ..base
845 },
846 0o17 => Decoded {
847 op: Op::Sync,
848 ..base
849 },
850 0o60 => Decoded {
851 op: Op::Tge,
852 ..base
853 },
854 0o61 => Decoded {
855 op: Op::Tgeu,
856 ..base
857 },
858 0o62 => Decoded {
859 op: Op::Tlt,
860 ..base
861 },
862 0o63 => Decoded {
863 op: Op::Tltu,
864 ..base
865 },
866 0o64 => Decoded {
867 op: Op::Teq,
868 ..base
869 },
870 0o66 => Decoded {
871 op: Op::Tne,
872 ..base
873 },
874 0o70 => r!(Op::Dsll),
875 0o72 => r!(Op::Dsrl),
876 0o73 => r!(Op::Dsra),
877 0o74 => r32!(Op::Dsll32),
878 0o76 => r32!(Op::Dsrl32),
879 0o77 => r32!(Op::Dsra32),
880 _ => base,
881 },
882 OP_ADDI => i!(Op::Addi),
883 OP_ADDIU => i!(Op::Addiu),
884 OP_SLTI => i!(Op::Slti),
885 OP_SLTIU => i!(Op::Sltiu),
886 OP_ANDI => i!(Op::Andi),
887 OP_ORI => i!(Op::Ori),
888 OP_XORI => i!(Op::Xori),
889 OP_LUI => i!(Op::Lui),
890 OP_DADDI => i!(Op::Daddi),
891 OP_DADDIU => i!(Op::Daddiu),
892
893 // Loads write `rt`; stores read it and write memory, so they have no
894 // register destination.
895 OP_LB => i!(Op::Lb),
896 OP_LBU => i!(Op::Lbu),
897 OP_LH => i!(Op::Lh),
898 OP_LHU => i!(Op::Lhu),
899 OP_LW => i!(Op::Lw),
900 OP_LWU => i!(Op::Lwu),
901 OP_LD => i!(Op::Ld),
902 OP_LWL => i!(Op::Lwl),
903 OP_LWR => i!(Op::Lwr),
904 OP_LDL => i!(Op::Ldl),
905 OP_LDR => i!(Op::Ldr),
906 // LL/SC write `rt`, so they take the `i!` (destination = rt) shape even
907 // though SC also stores. A store form with a destination is unusual
908 // enough that giving SC the store shape here is the natural mistake:
909 // the success flag would then never reach the register file.
910 // COP0. The form is in `rs`; `rd` names the COP0 register, and for the
911 // move-from forms `rt` is the GPR destination.
912 OP_COP0 => {
913 let rs = ((word >> 21) & 31) as u8;
914 match rs {
915 0o00 => Decoded {
916 op: Op::Mfc0,
917 dest: ((word >> 16) & 31) as u8,
918 ..base
919 },
920 0o01 => Decoded {
921 op: Op::Dmfc0,
922 dest: ((word >> 16) & 31) as u8,
923 ..base
924 },
925 // The move-TO forms write COP0, not a GPR, so `dest` stays 0 --
926 // giving them a GPR destination would corrupt the register the
927 // instruction reads its value from.
928 0o04 => Decoded {
929 op: Op::Mtc0,
930 ..base
931 },
932 0o05 => Decoded {
933 op: Op::Dmtc0,
934 ..base
935 },
936 // rs bit 4 set: the CP0 "CO" forms -- TLBR/TLBWI/TLBWR/TLBP
937 // and ERET, distinguished by the `funct` field. Only ERET is
938 // implemented; the TLB forms arrive with T-12-004 and stay
939 // `Reserved` until then rather than decoding to a no-op.
940 rs if rs & 0o20 != 0 => match word & 0o77 {
941 0o01 => Decoded {
942 op: Op::Tlbr,
943 ..base
944 },
945 0o02 => Decoded {
946 op: Op::Tlbwi,
947 ..base
948 },
949 0o06 => Decoded {
950 op: Op::Tlbwr,
951 ..base
952 },
953 0o10 => Decoded {
954 op: Op::Tlbp,
955 ..base
956 },
957 0o30 => Decoded {
958 op: Op::Eret,
959 ..base
960 },
961 // funct 0x20-0x3F: the emux extension space, inert on
962 // hardware. See `Op::Cop0Extension`.
963 0o40..=0o77 => Decoded {
964 op: Op::Cop0Extension,
965 // EMUX uses its OWN field positions, not the MIPS ones:
966 // `rd` at 24:20, `rt` at 19:15, `code` at 14:6 (see
967 // n64-systemtest `src/emux.rs::encode_*`). They are
968 // unpacked into the standard slots here so `execute`
969 // stays a pure function of `Decoded`:
970 // rs <- the EMUX `rd` register (xlog's pointer)
971 // rt <- the EMUX `rt` register (xlog's length)
972 // dest <- the EMUX `rd` register (xdetect's result)
973 // imm <- the 9-bit `code`
974 rs: ((word >> 20) & 0x1F) as u8,
975 rt: ((word >> 15) & 0x1F) as u8,
976 dest: ((word >> 20) & 0x1F) as u8,
977 imm: ((word >> 6) & 0x1FF) as u16,
978 // sa <- the CO `funct`, so `execute` can dispatch
979 sa: word & 0o77,
980 ..base
981 },
982 _ => base,
983 },
984 _ => base,
985 }
986 }
987 // COP2. Every encoding is valid on the VR4300, so the usability check
988 // in EX decides between executing and Coprocessor Unusable. No COP2
989 // operation is implemented, which is why one arm covers the opcode.
990 OP_COP2 => {
991 // `DCFC2` (3) and `DCTC2` (7) are not implemented; every other COP2
992 // encoding retires silently. See `Op::Cop2ReservedControl`.
993 let rs = (word >> 21) & 31;
994 match rs {
995 0o03 | 0o07 => Decoded {
996 op: Op::Cop2ReservedControl,
997 ..base
998 },
999 // The move-FROM forms write a GPR, so they need `dest`.
1000 0o00 => Decoded {
1001 op: Op::Mfc2,
1002 dest: ((word >> 16) & 31) as u8,
1003 ..base
1004 },
1005 0o01 => Decoded {
1006 op: Op::Dmfc2,
1007 dest: ((word >> 16) & 31) as u8,
1008 ..base
1009 },
1010 // `MTC2` and `DMTC2` behave identically -- see `Op::Mtc2`.
1011 0o04 | 0o05 => Decoded {
1012 op: Op::Mtc2,
1013 ..base
1014 },
1015 _ => Decoded {
1016 op: Op::Cop2,
1017 ..base
1018 },
1019 }
1020 }
1021 // COP1. The CONTROL moves (T-12-006), the DATA moves (T-13-001) and the
1022 // S/D arithmetic below are implemented; the remaining formats and the
1023 // conversions are not, and the FP load/store forms have their own
1024 // primary opcodes. Everything unhandled decodes to `Cop1Unimplemented`
1025 // rather than `Reserved`, because the encodings are valid and must raise
1026 // Coprocessor Unusable, not Reserved Instruction.
1027 OP_COP1 => {
1028 let rs = ((word >> 21) & 31) as u8;
1029 match rs {
1030 // The move-FROM forms write a GPR; the move-TO forms write an
1031 // FPR, so `dest` (a GPR index) stays 0 for those or they would
1032 // clobber the register they read their value from.
1033 0o00 => Decoded {
1034 op: Op::Mfc1,
1035 dest: ((word >> 16) & 31) as u8,
1036 ..base
1037 },
1038 0o01 => Decoded {
1039 op: Op::Dmfc1,
1040 dest: ((word >> 16) & 31) as u8,
1041 ..base
1042 },
1043 0o02 => Decoded {
1044 op: Op::Cfc1,
1045 dest: ((word >> 16) & 31) as u8,
1046 ..base
1047 },
1048 // Format 16 = single, 17 = double. `funct` 0..=3 are
1049 // ADD/SUB/MUL/DIV, 5..=7 are ABS/MOV/NEG, and `funct` 4 is
1050 // `SQRT` (wired to `pipeline::fp_sqrt`, T-13-005) — all decode to
1051 // `Op::FpArith`. Everything above 7 (the conversions and
1052 // `C.cond.fmt`) is handled by its own decode arms.
1053 //
1054 // **`MOV` matters far more than its size suggests.** It is
1055 // funct 6, so admitting only `<= 3` made every `MOV.fmt` a
1056 // silent no-op — and the compiler emits one for each FP
1057 // argument and each FP return value. A single n64-systemtest
1058 // FP thunk contains three, so its operands were stale and its
1059 // result never left the callee. That accounted for the whole
1060 // `Result after <op>` failure block, which had been read as an
1061 // FPU arithmetic fault for nine rounds (ledger C-10).
1062 // The whole S/D funct space that exists is now wired:
1063 //
1064 // | `funct` | Operation |
1065 // | --- | --- |
1066 // | `0..=3` | `ADD` / `SUB` / `MUL` / `DIV` |
1067 // | `4` | `SQRT` |
1068 // | `5..=7` | `ABS` / `MOV` / `NEG` |
1069 // | `0o10..=0o17` | `ROUND`/`TRUNC`/`CEIL`/`FLOOR` to `.L` then `.W` |
1070 // | `0o40`/`0o41`/`0o44`/`0o45` | `CVT.S` / `CVT.D` / `CVT.W` / `CVT.L` |
1071 // | `0o60..=0o77` | `C.cond.fmt`, the low 4 bits being the condition |
1072 0o20 | 0o21
1073 if matches!(
1074 word & 0o77,
1075 0..=7 | 0o10..=0o17 | 0o40 | 0o41 | 0o44 | 0o45 | 0o60..=0o77
1076 ) =>
1077 {
1078 Decoded {
1079 op: Op::FpArith,
1080 ..base
1081 }
1082 }
1083 // The **integer** source formats, `.W` (20) and `.L` (21).
1084 //
1085 // Easy to miss: `CVT.S.W` carries its source format in the same
1086 // `fmt` field, so a decoder that only admits 16/17 leaves every
1087 // integer-to-float conversion a silent no-op — the same shape of
1088 // gap that made `MOV.fmt` cost nine rounds. Only `CVT.S` and
1089 // `CVT.D` are defined from these formats; converting an integer
1090 // to an integer is **not an instruction**.
1091 //
1092 // The to-integer functs are admitted here anyway, so that they
1093 // reach the arithmetic path and raise *Unimplemented Operation*
1094 // there. Leaving them to the `Cop1Unimplemented` fallthrough
1095 // retires them silently, which is what n64-systemtest caught:
1096 // `CVT.W.W`, `CVT.L.W` and their `.L`-source siblings expect an
1097 // exception and saw none.
1098 0o24 | 0o25 if matches!(word & 0o77, 0o10..=0o17 | 0o40 | 0o41 | 0o44 | 0o45) => {
1099 Decoded {
1100 op: Op::FpArith,
1101 ..base
1102 }
1103 }
1104 // `BC1` — branch on the FP condition. `rs = 0o10` selects the
1105 // branch family; bits 17:16 are `nd:tf`, so the four encodings
1106 // are TF (true/false) crossed with likely/not.
1107 //
1108 // `imm` carries the offset, as for every other branch, so the
1109 // target arithmetic in `exec` is shared rather than duplicated.
1110 0o10 => Decoded {
1111 op: match (word >> 16) & 0b11 {
1112 0b00 => Op::Bc1f,
1113 0b01 => Op::Bc1t,
1114 0b10 => Op::Bc1fl,
1115 _ => Op::Bc1tl,
1116 },
1117 ..base
1118 },
1119 0o04 => Decoded {
1120 op: Op::Mtc1,
1121 ..base
1122 },
1123 0o05 => Decoded {
1124 op: Op::Dmtc1,
1125 ..base
1126 },
1127 0o06 => Decoded {
1128 op: Op::Ctc1,
1129 ..base
1130 },
1131 // `DCFC1` (3) and `DCTC1` (7): the doubleword control moves,
1132 // which this processor does not implement. See
1133 // `Op::Cop1ReservedControl` -- they trap rather than no-op.
1134 0o03 | 0o07 => Decoded {
1135 op: Op::Cop1ReservedControl,
1136 ..base
1137 },
1138 _ => Decoded {
1139 op: Op::Cop1Unimplemented,
1140 ..base
1141 },
1142 }
1143 }
1144 // CACHE writes no register: `rt` is the operation selector, not a
1145 // destination. Giving it the `i!` shape would clobber a GPR chosen by
1146 // the cache-op encoding, which is a spectacularly confusing bug.
1147 OP_CACHE => Decoded {
1148 op: Op::Cache,
1149 ..base
1150 },
1151 // The FP load/store forms. `rt` names an FPR, not a GPR, so `dest`
1152 // stays 0 -- giving them a GPR destination corrupts an integer register.
1153 OP_LWC1 => Decoded {
1154 op: Op::Lwc1,
1155 ..base
1156 },
1157 OP_LDC1 => Decoded {
1158 op: Op::Ldc1,
1159 ..base
1160 },
1161 OP_SWC1 => Decoded {
1162 op: Op::Swc1,
1163 ..base
1164 },
1165 OP_SDC1 => Decoded {
1166 op: Op::Sdc1,
1167 ..base
1168 },
1169 OP_LL => i!(Op::Ll),
1170 OP_LLD => i!(Op::Lld),
1171 OP_SC => i!(Op::Sc),
1172 OP_SCD => i!(Op::Scd),
1173 OP_SB => Decoded { op: Op::Sb, ..base },
1174 OP_SH => Decoded { op: Op::Sh, ..base },
1175 OP_SW => Decoded { op: Op::Sw, ..base },
1176 OP_SD => Decoded { op: Op::Sd, ..base },
1177 OP_SWL => Decoded {
1178 op: Op::Swl,
1179 ..base
1180 },
1181 OP_SWR => Decoded {
1182 op: Op::Swr,
1183 ..base
1184 },
1185 OP_SDL => Decoded {
1186 op: Op::Sdl,
1187 ..base
1188 },
1189 OP_SDR => Decoded {
1190 op: Op::Sdr,
1191 ..base
1192 },
1193
1194 // Jumps and branches write no general register except the linking forms,
1195 // which target $31 (or `rd` for JALR).
1196 OP_J => Decoded { op: Op::J, ..base },
1197 OP_JAL => Decoded {
1198 op: Op::Jal,
1199 dest: 31,
1200 ..base
1201 },
1202 OP_BEQ => Decoded {
1203 op: Op::Beq,
1204 ..base
1205 },
1206 OP_BNE => Decoded {
1207 op: Op::Bne,
1208 ..base
1209 },
1210 OP_BLEZ => Decoded {
1211 op: Op::Blez,
1212 ..base
1213 },
1214 OP_BGTZ => Decoded {
1215 op: Op::Bgtz,
1216 ..base
1217 },
1218 OP_BEQL => Decoded {
1219 op: Op::Beql,
1220 ..base
1221 },
1222 OP_BNEL => Decoded {
1223 op: Op::Bnel,
1224 ..base
1225 },
1226 OP_BLEZL => Decoded {
1227 op: Op::Blezl,
1228 ..base
1229 },
1230 OP_BGTZL => Decoded {
1231 op: Op::Bgtzl,
1232 ..base
1233 },
1234
1235 // REGIMM: the `rt` field selects the operation, not a register.
1236 OP_REGIMM => {
1237 let linking = matches!(rt, 0o20..=0o23);
1238 let op = match rt {
1239 0o00 => Op::Bltz,
1240 0o01 => Op::Bgez,
1241 0o02 => Op::Bltzl,
1242 0o03 => Op::Bgezl,
1243 0o10 => Op::Tgei,
1244 0o11 => Op::Tgeiu,
1245 0o12 => Op::Tlti,
1246 0o13 => Op::Tltiu,
1247 0o14 => Op::Teqi,
1248 0o16 => Op::Tnei,
1249 0o20 => Op::Bltzal,
1250 0o21 => Op::Bgezal,
1251 0o22 => Op::Bltzall,
1252 0o23 => Op::Bgezall,
1253 _ => Op::Reserved,
1254 };
1255 Decoded {
1256 op,
1257 dest: if linking { 31 } else { 0 },
1258 ..base
1259 }
1260 }
1261 _ => base,
1262 }
1263}
1264
1265#[cfg(test)]
1266mod tests {
1267 use super::*;
1268
1269 /// Assemble an R-type word, so the tests read as assembly rather than hex.
1270 const fn r(funct: u32, rs: u32, rt: u32, rd: u32, sa: u32) -> u32 {
1271 (rs << 21) | (rt << 16) | (rd << 11) | (sa << 6) | funct
1272 }
1273 /// Assemble an I-type word.
1274 const fn i(opcode: u32, rs: u32, rt: u32, imm: u16) -> u32 {
1275 (opcode << 26) | (rs << 21) | (rt << 16) | imm as u32
1276 }
1277
1278 #[test]
1279 fn fields_land_in_the_right_places() {
1280 // ADD $t0($8), $s0($16), $s1($17)
1281 let d = decode(r(0o40, 16, 17, 8, 0));
1282 assert_eq!(d.op, Op::Add);
1283 assert_eq!((d.rs, d.rt, d.rd, d.dest), (16, 17, 8, 8));
1284 // ADDI $t0, $s0, -1 -- rt is the DESTINATION for I-type, not a source
1285 let d = decode(i(0o10, 16, 8, 0xFFFF));
1286 assert_eq!(d.op, Op::Addi);
1287 assert_eq!((d.rs, d.rt, d.dest, d.imm), (16, 8, 8, 0xFFFF));
1288 }
1289
1290 /// `SLL $0, $0, 0` — the all-zero word — is the canonical `NOP`, and it must
1291 /// decode as a real instruction rather than as `Reserved`. Getting this wrong
1292 /// makes every padding byte raise an exception.
1293 #[test]
1294 fn the_all_zero_word_is_nop_not_reserved() {
1295 let d = decode(0);
1296 assert_eq!(d.op, Op::Sll);
1297 assert_eq!(d.dest, 0, "writes $zero, so it commits nothing");
1298 }
1299
1300 /// Decode is **total**: no 32-bit pattern may panic, and anything
1301 /// unrecognized becomes `Reserved` rather than silently acting as a `NOP`.
1302 /// A guest can execute arbitrary bytes.
1303 #[test]
1304 fn decode_is_total_over_every_opcode_and_funct() {
1305 // Every primary opcode with every SPECIAL funct, plus a sweep of the
1306 // rest of the encoding space.
1307 for opcode in 0..64u32 {
1308 for low in 0..64u32 {
1309 let word = (opcode << 26) | low;
1310 let _ = decode(word);
1311 }
1312 }
1313 for bit in 0..32 {
1314 let _ = decode(1u32 << bit);
1315 }
1316 // These must be encodings the VR4300 genuinely leaves UNASSIGNED, not
1317 // merely ones this project has not implemented yet. Primary opcodes
1318 // 0o34..0o37 are reserved on MIPS III, and SPECIAL funct 0o01 is unused.
1319 // Earlier revisions of this test used LW and then BEQ, and had to be
1320 // repointed each time that opcode landed -- which made the test track
1321 // implementation progress instead of the architecture.
1322 assert_eq!(decode(r(0o01, 1, 2, 3, 0)).op, Op::Reserved);
1323 assert_eq!(decode(i(0o35, 1, 2, 0)).op, Op::Reserved);
1324 assert_eq!(decode(i(0o36, 1, 2, 0)).op, Op::Reserved);
1325 }
1326
1327 /// The `*32` shift variants add 32 to the encoded 5-bit field, so `sa` is the
1328 /// effective amount the helpers expect.
1329 #[test]
1330 fn the_32_shift_variants_add_32_to_the_encoded_field() {
1331 assert_eq!(decode(r(0o70, 0, 1, 2, 5)).sa, 5, "DSLL keeps sa");
1332 assert_eq!(decode(r(0o74, 0, 1, 2, 5)).sa, 37, "DSLL32 adds 32");
1333 assert_eq!(decode(r(0o76, 0, 1, 2, 0)).sa, 32, "DSRL32 of 0 is 32");
1334 assert_eq!(decode(r(0o77, 0, 1, 2, 31)).sa, 63, "DSRA32 tops out at 63");
1335 }
1336
1337 /// Multiply/divide and `MTHI`/`MTLO` write `HI`/`LO`, so they have no general
1338 /// destination — and `dest = 0` must mean "nothing", not "$zero".
1339 #[test]
1340 fn hi_lo_writers_have_no_general_destination() {
1341 for funct in [0o30, 0o31, 0o32, 0o33, 0o34, 0o35, 0o36, 0o37, 0o21, 0o23] {
1342 let d = decode(r(funct, 1, 2, 3, 0));
1343 assert_eq!(d.dest, 0, "funct {funct:o} must not target rd");
1344 assert!(d.op.writes_hi_lo(), "funct {funct:o} should write HI/LO");
1345 }
1346 // MFHI/MFLO read them and DO have a destination.
1347 assert_eq!(decode(r(0o20, 0, 0, 9, 0)).dest, 9);
1348 assert!(!decode(r(0o20, 0, 0, 9, 0)).op.writes_hi_lo());
1349 }
1350
1351 /// `SC` is a store that nonetheless writes `rt`, so it must decode with a
1352 /// destination. The unit tests in `pipeline` construct `MemOp` directly and
1353 /// therefore cannot catch a decode that drops it — this one can.
1354 #[test]
1355 fn the_synchronization_pair_decodes_with_rt_as_the_destination() {
1356 // opcode, rs=1 (base), rt=9, imm=0x20
1357 let enc = |opcode: u32| (opcode << 26) | (1 << 21) | (9 << 16) | 0x20;
1358
1359 for (opcode, op) in [
1360 (0o60u32, Op::Ll),
1361 (0o64, Op::Lld),
1362 (0o70, Op::Sc),
1363 (0o74, Op::Scd),
1364 ] {
1365 let d = decode(enc(opcode));
1366 assert_eq!(d.op, op, "opcode {opcode:#o}");
1367 assert_eq!(d.rs, 1, "{op:?} base");
1368 assert_eq!(d.imm, 0x20, "{op:?} offset");
1369 assert_eq!(
1370 d.dest, 9,
1371 "{op:?} must write rt -- SC reports success there even when it stores nothing"
1372 );
1373 }
1374 }
1375
1376 /// The interlock must treat `LL`/`LLD` as loads (their value comes from
1377 /// memory) but `SC`/`SCD` as not loads (the flag does not).
1378 #[test]
1379 fn only_the_linked_loads_count_as_loads_for_the_interlock() {
1380 let enc = |opcode: u32| (opcode << 26) | (1 << 21) | (9 << 16);
1381 assert!(decode(enc(0o60)).is_load(), "LL");
1382 assert!(decode(enc(0o64)).is_load(), "LLD");
1383 assert!(!decode(enc(0o70)).is_load(), "SC is not a load");
1384 assert!(!decode(enc(0o74)).is_load(), "SCD is not a load");
1385 assert!(decode(enc(0o70)).is_store_conditional(), "SC");
1386 assert!(decode(enc(0o74)).is_store_conditional(), "SCD");
1387 assert!(!decode(enc(0o53)).is_store_conditional(), "SW is not");
1388 }
1389
1390 /// `SYNC` is a real encoding the VR4300 retires as a NOP (UM §3.1).
1391 /// Decoding it to `Reserved` raises a reserved-instruction exception on
1392 /// code that runs fine on hardware — compilers emit it.
1393 #[test]
1394 fn sync_decodes_to_a_nop_not_a_reserved_instruction() {
1395 let d = decode(0o17);
1396 assert_eq!(d.op, Op::Sync);
1397 assert_ne!(d.op, Op::Reserved, "SYNC must not raise");
1398 assert_eq!(d.dest, 0, "SYNC writes no register");
1399 }
1400
1401 /// COP0 CO `funct` 0x20-0x3F is the **emux extension range** and must retire
1402 /// as a no-op, not raise Reserved Instruction.
1403 ///
1404 /// n64-systemtest probes it from `init_allocator`, inside `entrypoint`,
1405 /// **before** `main` installs an exception handler -- so an RI here derails
1406 /// the suite before it prints a line. Decoding it to `Reserved` is exactly
1407 /// what made the suite appear to hang: the RI dispatched to an uninstalled
1408 /// `0x8000_0180`, ran zeros as `NOP`s into `.text`, and faulted there.
1409 #[test]
1410 fn cop0_co_extension_functs_are_inert_not_reserved() {
1411 // The exact word n64-systemtest executes: COP0, rs = CO, funct = 0x20.
1412 assert_eq!(decode(0x4280_0060).op, Op::Cop0Extension, "emux XDETECT");
1413 // The rest of the documented extension space.
1414 for funct in 0x20u32..=0x3F {
1415 let word = (0x10 << 26) | (0x10 << 21) | funct;
1416 assert_eq!(
1417 decode(word).op,
1418 Op::Cop0Extension,
1419 "COP0 CO funct {funct:#04X} is extension space"
1420 );
1421 }
1422 // Below 0x20 the real CO instructions and the genuinely reserved
1423 // encodings are unaffected.
1424 assert_eq!(decode((0x10 << 26) | (0x10 << 21) | 0x18).op, Op::Eret);
1425 assert_eq!(decode((0x10 << 26) | (0x10 << 21) | 0x02).op, Op::Tlbwi);
1426 assert_eq!(
1427 decode((0x10 << 26) | (0x10 << 21) | 0x1F).op,
1428 Op::Reserved,
1429 "funct 0x1F is still reserved -- the range starts at 0x20"
1430 );
1431 }
1432
1433 /// **COP2 encodings are valid**, so they must not decode to `Reserved`.
1434 ///
1435 /// With `Status.CU2` clear they raise Coprocessor Unusable (`ExcCode 11`);
1436 /// `Reserved` would raise `10`. n64-systemtest's `MFC2/MTC2/DMFC2/DMTC2`
1437 /// test saw `10` five times running, tripped its recovery limit, and
1438 /// aborted the entire run with "Exception storm detected".
1439 #[test]
1440 fn cop2_encodings_are_valid_not_reserved() {
1441 // None of these is `Reserved` -- that is the point of the test. The
1442 // moves now decode to themselves rather than to the generic `Cop2`,
1443 // and the doubleword control forms trap (ledger C-18), so each is
1444 // named rather than lumped together.
1445 for (rs, want) in [
1446 (0o00u32, Op::Mfc2),
1447 (0o01, Op::Dmfc2),
1448 (0o02, Op::Cop2), // CFC2 -- still a no-op
1449 (0o04, Op::Mtc2),
1450 (0o05, Op::Mtc2), // DMTC2 behaves identically to MTC2
1451 (0o06, Op::Cop2), // CTC2 -- still a no-op
1452 (0o03, Op::Cop2ReservedControl),
1453 (0o07, Op::Cop2ReservedControl),
1454 ] {
1455 let word = (0o22 << 26) | (rs << 21);
1456 assert_eq!(decode(word).op, want, "COP2 rs={rs:#o}");
1457 assert_ne!(decode(word).op, Op::Reserved, "COP2 rs={rs:#o}");
1458 }
1459 // The move-FROM forms must carry `dest`, or they write GPR 0.
1460 assert_eq!(decode((0o22 << 26) | (7 << 16)).dest, 7, "MFC2 dest = rt");
1461 }
1462
1463 /// COP1 S/D arithmetic decodes to [`Op::FpArith`], not `Cop1Unimplemented`.
1464 ///
1465 /// The FPU has been implemented in `fpu.rs` since Sprint 3, but nothing
1466 /// decoded to it, so the whole unit was unreachable from an instruction
1467 /// stream — which is why COP1 accounted for 85% of n64-systemtest's
1468 /// failures.
1469 #[test]
1470 fn cop1_single_and_double_arithmetic_decode_to_fp_arith() {
1471 // COP1, fmt, ft, fs, fd, funct
1472 let enc = |fmt: u32, funct: u32| {
1473 (0o21 << 26) | (fmt << 21) | (2 << 16) | (3 << 11) | (4 << 6) | funct
1474 };
1475 for fmt in [0o20u32, 0o21] {
1476 for funct in 0..=3u32 {
1477 let d = decode(enc(fmt, funct));
1478 assert_eq!(d.op, Op::FpArith, "fmt {fmt:#o} funct {funct}");
1479 assert_eq!(d.rs, fmt as u8, "rs carries the format");
1480 assert_eq!(d.rt, 2, "rt = ft");
1481 assert_eq!(d.rd, 3, "rd = fs");
1482 assert_eq!(d.sa, 4, "sa = fd");
1483 }
1484 }
1485 // funct 4 is `SQRT`, a different operation -- it must not be swept
1486 // into the arithmetic range as a wrong ADD.
1487 assert_eq!(decode(enc(0o20, 4)).op, Op::FpArith);
1488 }
1489
1490 /// **The compares and conversions must decode.** They are implemented in
1491 /// `fpu.rs` and were unreachable for the same reason `MOV` was: the decode
1492 /// arm admitted only `funct 0..=3` and `5..=7`.
1493 ///
1494 /// Enumerated rather than spot-checked. The failure mode here is a *gap* in
1495 /// a range, and a gap is exactly what a single representative encoding does
1496 /// not find.
1497 #[test]
1498 fn the_compares_and_conversions_decode_rather_than_no_op() {
1499 let enc = |fmt: u32, funct: u32| {
1500 (0o21 << 26) | (fmt << 21) | (2 << 16) | (3 << 11) | (4 << 6) | funct
1501 };
1502 for fmt in [0o20u32, 0o21] {
1503 // ROUND/TRUNC/CEIL/FLOOR to .L (8..=11) then to .W (12..=15).
1504 for funct in 0o10..=0o17u32 {
1505 assert_eq!(
1506 decode(enc(fmt, funct)).op,
1507 Op::FpArith,
1508 "fmt {fmt:#o} funct {funct:#o}"
1509 );
1510 }
1511 // CVT.S / CVT.D / CVT.W / CVT.L.
1512 for funct in [0o40u32, 0o41, 0o44, 0o45] {
1513 assert_eq!(
1514 decode(enc(fmt, funct)).op,
1515 Op::FpArith,
1516 "CVT funct {funct:#o}"
1517 );
1518 }
1519 // All sixteen C.cond.fmt forms.
1520 for funct in 0o60..=0o77u32 {
1521 assert_eq!(
1522 decode(enc(fmt, funct)).op,
1523 Op::FpArith,
1524 "C.cond funct {funct:#o}"
1525 );
1526 }
1527 }
1528 // The INTEGER source formats. Easy to miss, because `CVT.S.W` carries
1529 // its source format in the same field as `.S`/`.D` — a decoder that
1530 // admits only 16/17 leaves every integer-to-float conversion a no-op.
1531 for fmt in [0o24u32, 0o25] {
1532 for funct in [0o40u32, 0o41] {
1533 assert_eq!(
1534 decode(enc(fmt, funct)).op,
1535 Op::FpArith,
1536 "fmt {fmt:#o} funct {funct:#o}"
1537 );
1538 }
1539 }
1540 // `SQRT` (funct 4) is wired too, as of T-13-005 — it is a distinct
1541 // operation, so this also guards against it being swept into the
1542 // arithmetic range and silently becoming an `ADD`.
1543 let d = decode(enc(0o20, 4));
1544 assert_eq!(d.op, Op::FpArith, "SQRT decodes");
1545 assert_eq!(d.rd, 3, "and carries fs");
1546 }
1547
1548 /// **`MOV.fmt` (funct 6) must decode.** With the arm admitting only
1549 /// `funct <= 3` it did not, and executed as a silent no-op.
1550 ///
1551 /// That is not a cosmetic gap. The compiler emits `MOV.fmt` for every FP
1552 /// argument and every FP return value, so a no-op left callees reading
1553 /// stale operands and callers reading a register the callee never wrote.
1554 /// It cost the whole `Result after <op>` block in n64-systemtest and nine
1555 /// rounds of investigation aimed at the FPU (ledger C-10) — the arithmetic
1556 /// was correct the entire time.
1557 ///
1558 /// ABS (5) and NEG (7) share the arm and are covered here for the same
1559 /// reason: nothing fails when a move quietly does nothing.
1560 #[test]
1561 fn abs_mov_and_neg_decode_rather_than_silently_doing_nothing() {
1562 let enc = |fmt: u32, funct: u32| (0o21 << 26) | (fmt << 21) | (3 << 11) | (4 << 6) | funct;
1563 for fmt in [0o20u32, 0o21] {
1564 for funct in 5..=7u32 {
1565 let d = decode(enc(fmt, funct));
1566 assert_eq!(
1567 d.op,
1568 Op::FpArith,
1569 "fmt {fmt:#o} funct {funct} must not be a no-op"
1570 );
1571 assert_eq!(d.rd, 3, "rd = fs");
1572 assert_eq!(d.sa, 4, "sa = fd");
1573 }
1574 }
1575 // The exact encoding the correlated capture found in the delay slot of
1576 // the failing test's `jr $ra`: `MOV.S $f0, $f4`.
1577 let d = decode(0x4600_2006);
1578 assert_eq!(d.op, Op::FpArith, "MOV.S $f0, $f4 must decode");
1579 assert_eq!((d.rs, d.rd, d.sa), (0o20, 4, 0), "fmt=S, fs=4, fd=0");
1580 }
1581}