Skip to main content

rustyn64_cpu/
alu.rs

1//! The VR4300 integer ALU as pure functions (T-11-002).
2//!
3//! Kept free of pipeline and register-file state on purpose: every rule here is
4//! a property of the *arithmetic*, so it can be tested exhaustively without
5//! constructing a machine. The pipeline calls these from `EX`.
6//!
7//! # The two rules that dominate MIPS III
8//!
9//! **32-bit results are sign-extended into the 64-bit register.** Every `*W`-class
10//! operation produces a 32-bit value that is then sign-extended to 64 bits before
11//! it reaches the register file. Skipping this is the single most common source
12//! of MIPS III emulator bugs, because it is invisible until a program compares or
13//! branches on the upper half.
14//!
15//! **The documented errata are reproduced, not corrected.** `SRA`/`SRAV` and the
16//! `MULT`/`DIV` sign-extension bugs are real hardware behavior that software can
17//! observe and depend on. Implementing them "correctly" per the manual is the bug
18//! — see [`sra`] and [`mult`]. Each is pinned by a test that fails if it is
19//! "fixed", so the intent survives a well-meaning future reader.
20
21use crate::Exception;
22use serde::{Deserialize, Serialize};
23
24/// Sign-extend a 32-bit result into the 64-bit register file.
25///
26/// The MIPS III rule for every `*W` operation. Named rather than inlined so that
27/// call sites read as an explicit statement of intent.
28#[must_use]
29pub const fn sext32(v: u32) -> u64 {
30    v as i32 as i64 as u64
31}
32
33// ---------------------------------------------------------------- arithmetic
34
35/// `ADD` — 32-bit signed add, **traps on overflow**.
36///
37/// # Errors
38/// [`Exception::Overflow`] when the signed 32-bit addition overflows. The
39/// register is left unmodified in that case (the trap precedes the write-back).
40pub const fn add(a: u64, b: u64) -> Result<u64, Exception> {
41    let (r, ovf) = (a as i32).overflowing_add(b as i32);
42    if ovf {
43        Err(Exception::Overflow)
44    } else {
45        Ok(sext32(r as u32))
46    }
47}
48
49/// `ADDU` — 32-bit add, no trap. The `U` means "unchecked", not "unsigned":
50/// the result is still sign-extended as a signed 32-bit value.
51#[must_use]
52pub const fn addu(a: u64, b: u64) -> u64 {
53    sext32((a as u32).wrapping_add(b as u32))
54}
55
56/// `SUB` — 32-bit signed subtract, **traps on overflow**.
57///
58/// # Errors
59/// [`Exception::Overflow`] on signed 32-bit overflow.
60pub const fn sub(a: u64, b: u64) -> Result<u64, Exception> {
61    let (r, ovf) = (a as i32).overflowing_sub(b as i32);
62    if ovf {
63        Err(Exception::Overflow)
64    } else {
65        Ok(sext32(r as u32))
66    }
67}
68
69/// `SUBU` — 32-bit subtract, no trap.
70#[must_use]
71pub const fn subu(a: u64, b: u64) -> u64 {
72    sext32((a as u32).wrapping_sub(b as u32))
73}
74
75/// `DADD` — 64-bit signed add, **traps on overflow**.
76///
77/// # Errors
78/// [`Exception::Overflow`] on signed 64-bit overflow.
79pub const fn dadd(a: u64, b: u64) -> Result<u64, Exception> {
80    let (r, ovf) = (a as i64).overflowing_add(b as i64);
81    if ovf {
82        Err(Exception::Overflow)
83    } else {
84        Ok(r as u64)
85    }
86}
87
88/// `DADDU` — 64-bit add, no trap.
89#[must_use]
90pub const fn daddu(a: u64, b: u64) -> u64 {
91    a.wrapping_add(b)
92}
93
94/// `DSUB` — 64-bit signed subtract, **traps on overflow**.
95///
96/// # Errors
97/// [`Exception::Overflow`] on signed 64-bit overflow.
98pub const fn dsub(a: u64, b: u64) -> Result<u64, Exception> {
99    let (r, ovf) = (a as i64).overflowing_sub(b as i64);
100    if ovf {
101        Err(Exception::Overflow)
102    } else {
103        Ok(r as u64)
104    }
105}
106
107/// `DSUBU` — 64-bit subtract, no trap.
108#[must_use]
109pub const fn dsubu(a: u64, b: u64) -> u64 {
110    a.wrapping_sub(b)
111}
112
113/// `SLT` — set on less than, signed 64-bit comparison.
114#[must_use]
115pub const fn slt(a: u64, b: u64) -> u64 {
116    ((a as i64) < (b as i64)) as u64
117}
118
119/// `SLTU` — set on less than, unsigned 64-bit comparison.
120#[must_use]
121pub const fn sltu(a: u64, b: u64) -> u64 {
122    (a < b) as u64
123}
124
125// ------------------------------------------------------------------ logical
126
127// The logical family operates on the full 64 bits and needs no sign extension:
128// the operands are already 64-bit register values and the result is too. This is
129// the one family where the 32-bit/64-bit distinction does not arise.
130
131/// `AND` — bitwise and, full 64-bit.
132#[must_use]
133pub const fn and(a: u64, b: u64) -> u64 {
134    a & b
135}
136
137/// `OR` — bitwise or, full 64-bit.
138#[must_use]
139pub const fn or(a: u64, b: u64) -> u64 {
140    a | b
141}
142
143/// `XOR` — bitwise exclusive or, full 64-bit.
144#[must_use]
145pub const fn xor(a: u64, b: u64) -> u64 {
146    a ^ b
147}
148
149/// `NOR` — bitwise nor, full 64-bit. MIPS has no `NOT`; `NOR rd, rs, $0` is it.
150#[must_use]
151pub const fn nor(a: u64, b: u64) -> u64 {
152    !(a | b)
153}
154
155/// `LUI` — load upper immediate.
156///
157/// The 16-bit immediate is placed in bits 31..16 and the **32-bit** result is
158/// then sign-extended, so a `LUI` of `0x8000` produces `0xFFFF_FFFF_8000_0000`
159/// rather than `0x0000_0000_8000_0000`.
160#[must_use]
161pub const fn lui(imm: u16) -> u64 {
162    sext32((imm as u32) << 16)
163}
164
165// ------------------------------------------------------------------- shifts
166
167/// `SLL` — 32-bit shift left logical, result sign-extended.
168///
169/// Note `SLL $0, $0, 0` is the canonical `NOP`.
170#[must_use]
171pub const fn sll(v: u64, sa: u32) -> u64 {
172    sext32((v as u32) << (sa & 31))
173}
174
175/// `SRL` — 32-bit shift right logical, result sign-extended.
176#[must_use]
177pub const fn srl(v: u64, sa: u32) -> u64 {
178    sext32((v as u32) >> (sa & 31))
179}
180
181/// `SRA` — 32-bit shift right arithmetic. **Reproduces the VR4300 erratum.**
182///
183/// The processor manual says the low 32 bits are filled with copies of bit 31 and
184/// bit 31 is then sign-extended into the upper half. **Hardware does not do
185/// that.** In practice the most significant bits are filled from the *upper 32
186/// bits of the register* first, and the new bit 31 is then sign-extended — which
187/// leaks 64-bit state that should be inaccessible, in both 32- and 64-bit mode.
188///
189/// ```text
190/// manual:   rd = (uint64_t)(int32_t)((int32_t)rt >> sa)
191/// hardware: rd = (uint64_t)(int32_t)((int64_t)rt >> sa)
192/// ```
193///
194/// With `rt = 0x0123456789ABCDEF`, `sa = 16`, the manual predicts
195/// `0xFFFFFFFFFFFF89AB`; hardware gives `0x00000000456789AB`.
196///
197/// This is **not** a bug to fix. It is present on more consoles than the
198/// multiplication erratum and is not known to have ever been corrected, so
199/// software can depend on it. `sra_reproduces_the_vr4300_erratum` fails if it is
200/// "corrected". Source: `n64brew_wiki/markdown/VR4300.md` § Known Bugs.
201#[must_use]
202pub const fn sra(v: u64, sa: u32) -> u64 {
203    // The 64-bit shift, then truncate-and-sign-extend, is the erratum.
204    sext32(((v as i64) >> (sa & 31)) as u32)
205}
206
207/// `DSLL` — 64-bit shift left logical.
208///
209/// `sa` is the **effective** shift amount, `0..64`, masked to 6 bits. The
210/// encoding splits that range across two opcodes — `DSLL` carries a 5-bit field
211/// for `0..32` and `DSLL32` adds 32 to it for `32..64` — but that split belongs
212/// to decode, not here. Pass the effective amount; do not pre-mask to 5 bits, or
213/// every `*32` variant silently becomes its non-`32` counterpart.
214#[must_use]
215pub const fn dsll(v: u64, sa: u32) -> u64 {
216    v << (sa & 63)
217}
218
219/// `DSRL` — 64-bit shift right logical. `sa` is the effective `0..64` amount;
220/// see [`dsll`] on the `DSRL`/`DSRL32` encoding split.
221#[must_use]
222pub const fn dsrl(v: u64, sa: u32) -> u64 {
223    v >> (sa & 63)
224}
225
226/// `DSRA` — 64-bit shift right arithmetic. `sa` is the effective `0..64` amount;
227/// see [`dsll`] on the `DSRA`/`DSRA32` encoding split.
228///
229/// Not affected by the `SRA` erratum:
230/// it is already a 64-bit shift, so there is no truncation for the bug to
231/// manifest through.
232#[must_use]
233pub const fn dsra(v: u64, sa: u32) -> u64 {
234    ((v as i64) >> (sa & 63)) as u64
235}
236
237// --------------------------------------------------------- multiply / divide
238
239/// The `HI`/`LO` register pair, written by every multiply and divide.
240#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
241pub struct HiLo {
242    /// `HI` — the high half, or the division remainder.
243    pub hi: u64,
244    /// `LO` — the low half, or the division quotient.
245    pub lo: u64,
246}
247
248/// `MULT` — 32-bit signed multiply. **Reproduces the VR4300 sign-extension erratum.**
249///
250/// When the inputs are not properly sign-extended 32-bit values, `MULT` behaves as
251/// a **64-bit by 35-bit** signed multiply: the second operand is sign-extended on
252/// **bit 34** before a 64-bit multiplication, and the first is taken as a full
253/// 64-bit value.
254///
255/// For well-formed inputs (both properly sign-extended 32-bit values) this reduces
256/// to the expected 32×32 signed multiply, which is why the erratum is invisible to
257/// ordinary compiler output and only surfaces with hand-written or miscompiled
258/// code. Results are for processor revision 2.2
259/// (`n64brew_wiki/markdown/VR4300.md` § Known Bugs).
260#[must_use]
261pub const fn mult(a: u64, b: u64) -> HiLo {
262    // Sign-extend the second operand on bit 34 (a 35-bit signed value).
263    let b35 = ((b << 29) as i64) >> 29;
264    let product = (a as i64).wrapping_mul(b35);
265    HiLo {
266        hi: sext32((product >> 32) as u32),
267        lo: sext32(product as u32),
268    }
269}
270
271/// `MULTU` — 32-bit unsigned multiply. Both operands are taken as their low 32
272/// bits, zero-extended; no erratum applies.
273#[must_use]
274pub const fn multu(a: u64, b: u64) -> HiLo {
275    let product = (a as u32 as u64).wrapping_mul(b as u32 as u64);
276    HiLo {
277        hi: sext32((product >> 32) as u32),
278        lo: sext32(product as u32),
279    }
280}
281
282/// `DMULT` — 64-bit signed multiply, full 128-bit result.
283#[must_use]
284pub const fn dmult(a: u64, b: u64) -> HiLo {
285    let product = (a as i64 as i128).wrapping_mul(b as i64 as i128);
286    HiLo {
287        hi: (product >> 64) as u64,
288        lo: product as u64,
289    }
290}
291
292/// `DMULTU` — 64-bit unsigned multiply, full 128-bit result.
293#[must_use]
294pub const fn dmultu(a: u64, b: u64) -> HiLo {
295    let product = (a as u128).wrapping_mul(b as u128);
296    HiLo {
297        hi: (product >> 64) as u64,
298        lo: product as u64,
299    }
300}
301
302/// `DIV` — 32-bit signed divide. **Reproduces the VR4300 sign-extension erratum.**
303///
304/// Acts as a **32-bit by 35-bit** signed division: the dividend is sign-extended
305/// on bit 31, the divisor on **bit 34**, before a 64-bit division.
306///
307/// # The unknown case
308///
309/// When bits 63 and 31 of the divisor **differ**, the quotient written to `LO` is
310/// documented as incorrect and *"it is currently unclear how the outputs of this
311/// last case are arrived at"* — unknown even to N64brew. `HI` is at least
312/// well-defined: `remainder = (int32_t)(dividend - quotient * divisor)`, computed
313/// in 64-bit.
314///
315/// This implementation performs the 32×35 division in that case too, which is a
316/// **guess**, and it is recorded as such in `docs/accuracy-ledger.md`. It must be
317/// characterized against hardware rather than left to look authoritative.
318///
319/// Divide-by-zero is architecturally *undefined* on MIPS; the values below follow
320/// the conventional interpretation and also need hardware confirmation.
321#[must_use]
322pub const fn div(dividend: u64, divisor: u64) -> HiLo {
323    let n = dividend as i32 as i64;
324    // Sign-extend the divisor on bit 34 (a 35-bit signed value) -- the erratum.
325    let d = ((divisor << 29) as i64) >> 29;
326    if d == 0 {
327        // Undefined per the architecture; conventional emulator behavior.
328        return HiLo {
329            lo: if n < 0 { 1 } else { u64::MAX },
330            hi: sext32(n as u32),
331        };
332    }
333    // i64::MIN / -1 overflows; MIPS defines the result as the dividend.
334    if n == i32::MIN as i64 && d == -1 {
335        return HiLo {
336            lo: sext32(n as u32),
337            hi: 0,
338        };
339    }
340    HiLo {
341        lo: sext32(n.wrapping_div(d) as u32),
342        hi: sext32(n.wrapping_rem(d) as u32),
343    }
344}
345
346/// `DIVU` — 32-bit unsigned divide. No erratum applies.
347#[must_use]
348pub const fn divu(dividend: u64, divisor: u64) -> HiLo {
349    let n = dividend as u32;
350    let d = divisor as u32;
351    if d == 0 {
352        return HiLo {
353            lo: sext32(u32::MAX),
354            hi: sext32(n),
355        };
356    }
357    HiLo {
358        lo: sext32(n / d),
359        hi: sext32(n % d),
360    }
361}
362
363/// `DDIV` — 64-bit signed divide.
364#[must_use]
365pub const fn ddiv(dividend: u64, divisor: u64) -> HiLo {
366    let n = dividend as i64;
367    let d = divisor as i64;
368    if d == 0 {
369        return HiLo {
370            lo: if n < 0 { 1 } else { u64::MAX },
371            hi: n as u64,
372        };
373    }
374    if n == i64::MIN && d == -1 {
375        return HiLo {
376            lo: n as u64,
377            hi: 0,
378        };
379    }
380    HiLo {
381        lo: n.wrapping_div(d) as u64,
382        hi: n.wrapping_rem(d) as u64,
383    }
384}
385
386/// `DDIVU` — 64-bit unsigned divide.
387#[must_use]
388pub const fn ddivu(dividend: u64, divisor: u64) -> HiLo {
389    if divisor == 0 {
390        return HiLo {
391            lo: u64::MAX,
392            hi: dividend,
393        };
394    }
395    HiLo {
396        lo: dividend / divisor,
397        hi: dividend % divisor,
398    }
399}
400
401/// Pipeline stall in `PCycle`s for a multiply or divide (UM Table 3-12).
402///
403/// These **stall the entire pipeline** — they are not background operations that
404/// complete while other instructions issue. `MULT` 5, `DIV` 37, `DMULT` 8,
405/// `DDIV` 69, with the unsigned forms costing the same as the signed.
406#[must_use]
407pub const fn muldiv_stall_cycles(op: MulDiv) -> u32 {
408    match op {
409        MulDiv::Mult | MulDiv::Multu => 5,
410        MulDiv::Div | MulDiv::Divu => 37,
411        MulDiv::Dmult | MulDiv::Dmultu => 8,
412        MulDiv::Ddiv | MulDiv::Ddivu => 69,
413    }
414}
415
416/// The multiply/divide family, for cost lookup.
417#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
418pub enum MulDiv {
419    /// `MULT` — 32-bit signed multiply.
420    Mult,
421    /// `MULTU` — 32-bit unsigned multiply.
422    Multu,
423    /// `DIV` — 32-bit signed divide.
424    Div,
425    /// `DIVU` — 32-bit unsigned divide.
426    Divu,
427    /// `DMULT` — 64-bit signed multiply.
428    Dmult,
429    /// `DMULTU` — 64-bit unsigned multiply.
430    Dmultu,
431    /// `DDIV` — 64-bit signed divide.
432    Ddiv,
433    /// `DDIVU` — 64-bit unsigned divide.
434    Ddivu,
435}
436
437/// How many instructions after `MFHI`/`MFLO` must not write `HI`/`LO`.
438///
439/// A **non-interlocked** hazard: the hardware does not stall, it produces a wrong
440/// result. From `n64brew_wiki/markdown/MIPS III instructions.md` § Hazards:
441/// *"The `mfhi` and `mflo` instructions will produce incorrect results if any of
442/// the two following instructions modify the `HI` and `LO` registers."*
443///
444/// Modeling this as a stall would be wrong in both directions: it would add
445/// timing that hardware does not have, and it would hide the incorrect result
446/// that software can actually observe.
447pub const MFHI_MFLO_HAZARD_INSTRUCTIONS: u32 = 2;
448
449#[cfg(test)]
450mod tests {
451    use super::*;
452
453    // ------------------------------------------------------- sign extension
454
455    /// The rule that dominates MIPS III: every 32-bit result reaches the register
456    /// file sign-extended. Missing it is invisible until software inspects the
457    /// upper half, which is what makes it such a common bug.
458    #[test]
459    fn every_32_bit_result_is_sign_extended() {
460        // A result with bit 31 set must fill the whole upper half with ones.
461        assert_eq!(addu(0x8000_0000, 0), 0xFFFF_FFFF_8000_0000);
462        assert_eq!(subu(0, 1), 0xFFFF_FFFF_FFFF_FFFF);
463        assert_eq!(sll(1, 31), 0xFFFF_FFFF_8000_0000);
464        assert_eq!(srl(0xFFFF_FFFF, 0), 0xFFFF_FFFF_FFFF_FFFF);
465        // ...and a positive one must leave it clear.
466        assert_eq!(addu(0x7FFF_FFFF, 0), 0x0000_0000_7FFF_FFFF);
467        // 32-bit ops ignore the upper half of their inputs entirely.
468        assert_eq!(addu(0xDEAD_BEEF_0000_0001, 1), 2);
469    }
470
471    // ------------------------------------------------------------ arithmetic
472
473    #[test]
474    fn add_traps_only_on_signed_32_bit_overflow() {
475        assert_eq!(add(1, 2), Ok(3));
476        assert_eq!(add(0x7FFF_FFFF, 1), Err(Exception::Overflow));
477        // The unchecked form wraps instead of trapping.
478        assert_eq!(addu(0x7FFF_FFFF, 1), 0xFFFF_FFFF_8000_0000);
479        // Negative overflow traps too.
480        assert_eq!(
481            add(sext32(0x8000_0000), sext32(0xFFFF_FFFF)),
482            Err(Exception::Overflow)
483        );
484        // Carry out of bit 31 without signed overflow does NOT trap.
485        assert_eq!(add(sext32(0xFFFF_FFFF), 1), Ok(0));
486    }
487
488    #[test]
489    fn sub_traps_only_on_signed_32_bit_overflow() {
490        assert_eq!(sub(3, 1), Ok(2));
491        assert_eq!(sub(sext32(0x8000_0000), 1), Err(Exception::Overflow));
492        assert_eq!(subu(sext32(0x8000_0000), 1), 0x0000_0000_7FFF_FFFF);
493        assert_eq!(sub(0, 1), Ok(0xFFFF_FFFF_FFFF_FFFF));
494    }
495
496    #[test]
497    fn the_64_bit_forms_trap_at_64_bit_boundaries() {
498        assert_eq!(dadd(1, 2), Ok(3));
499        assert_eq!(dadd(i64::MAX as u64, 1), Err(Exception::Overflow));
500        assert_eq!(daddu(i64::MAX as u64, 1), 0x8000_0000_0000_0000);
501        assert_eq!(dsub(i64::MIN as u64, 1), Err(Exception::Overflow));
502        assert_eq!(dsubu(i64::MIN as u64, 1), 0x7FFF_FFFF_FFFF_FFFF);
503        // A value that overflows 32-bit ADD is fine for DADD.
504        assert_eq!(dadd(0x7FFF_FFFF, 1), Ok(0x8000_0000));
505    }
506
507    #[test]
508    fn set_on_less_than_compares_at_64_bits() {
509        assert_eq!(slt(u64::MAX, 0), 1, "-1 < 0 signed");
510        assert_eq!(sltu(u64::MAX, 0), 0, "max > 0 unsigned");
511        assert_eq!(slt(0, 1), 1);
512        assert_eq!(sltu(0, 1), 1);
513    }
514
515    #[test]
516    fn the_logical_family_is_full_width_and_needs_no_sign_extension() {
517        assert_eq!(
518            and(0xFFFF_0000_FFFF_0000, 0x0F0F_0F0F_0F0F_0F0F),
519            0x0F0F_0000_0F0F_0000
520        );
521        assert_eq!(
522            or(0xFFFF_0000_0000_0000, 0x0000_0000_0000_FFFF),
523            0xFFFF_0000_0000_FFFF
524        );
525        assert_eq!(xor(u64::MAX, u64::MAX), 0);
526        // NOR with $zero is how MIPS spells NOT.
527        assert_eq!(nor(0x0000_0000_0000_00FF, 0), 0xFFFF_FFFF_FFFF_FF00);
528        // The upper half participates -- these are not 32-bit operations.
529        assert_eq!(and(u64::MAX, 0xFFFF_FFFF_FFFF_FFFF), u64::MAX);
530    }
531
532    /// `LUI` is a 32-bit operation, so its result IS sign-extended -- a `LUI` of
533    /// 0x8000 fills the upper half with ones. Missing this is a classic bug,
534    /// because `LUI`+`ORI` address construction then silently breaks above 2 GiB.
535    #[test]
536    fn lui_sign_extends_its_32_bit_result() {
537        assert_eq!(lui(0x8000), 0xFFFF_FFFF_8000_0000);
538        assert_eq!(lui(0x7FFF), 0x0000_0000_7FFF_0000);
539        assert_eq!(lui(0), 0);
540    }
541
542    // ---------------------------------------------------------------- shifts
543
544    /// **The SRA erratum.** This test fails if someone "corrects" `sra` to match
545    /// the processor manual. That correction is the bug: hardware leaks the upper
546    /// 32 bits, on every console, and software can depend on it.
547    ///
548    /// Source: `n64brew_wiki/markdown/VR4300.md` § Known Bugs.
549    #[test]
550    fn sra_reproduces_the_vr4300_erratum() {
551        // The worked example from the wiki.
552        let rt = 0x0123_4567_89AB_CDEF;
553        assert_eq!(
554            sra(rt, 16),
555            0x0000_0000_4567_89AB,
556            "SRA must leak the upper 32 bits (the erratum), not sign-extend bit 31"
557        );
558        // What the manual claims, and what must NOT happen:
559        let manual = sext32(((rt as u32) as i32 >> 16) as u32);
560        assert_eq!(manual, 0xFFFF_FFFF_FFFF_89AB);
561        assert_ne!(
562            sra(rt, 16),
563            manual,
564            "the manual's behavior is not hardware's"
565        );
566
567        // With a properly sign-extended input the erratum is invisible, which is
568        // why ordinary compiler output never trips over it.
569        let clean = sext32(0x89AB_CDEF);
570        assert_eq!(sra(clean, 16), sext32(0xFFFF_89AB));
571    }
572
573    #[test]
574    fn shift_amounts_are_masked_to_the_operand_width() {
575        // 32-bit shifts mask to 5 bits, 64-bit to 6.
576        assert_eq!(sll(1, 32), sll(1, 0));
577        assert_eq!(dsll(1, 64), dsll(1, 0));
578        assert_eq!(srl(0x8000_0000, 33), srl(0x8000_0000, 1));
579    }
580
581    /// The `*32` opcode variants are decode's business: they add 32 to the
582    /// encoded 5-bit field and call the same helper. This pins that mapping, so
583    /// the doc claim is enforced rather than merely asserted — pre-masking to 5
584    /// bits here would silently turn every `*32` form into its counterpart.
585    #[test]
586    fn the_32_variants_are_the_same_helper_with_32_added() {
587        let v = 0x0123_4567_89AB_CDEF;
588        for encoded in 0..32u32 {
589            // DSLL32 sa=n  ==  a 64-bit shift of (n + 32)
590            assert_eq!(dsll(v, encoded + 32), v << (encoded + 32));
591            assert_eq!(dsrl(v, encoded + 32), v >> (encoded + 32));
592            assert_eq!(dsra(v, encoded + 32), ((v as i64) >> (encoded + 32)) as u64);
593        }
594        // And the two halves are genuinely different operations.
595        assert_ne!(dsll(v, 1), dsll(v, 33));
596    }
597
598    #[test]
599    fn the_64_bit_shifts_do_not_truncate() {
600        assert_eq!(dsll(1, 63), 0x8000_0000_0000_0000);
601        assert_eq!(dsrl(0x8000_0000_0000_0000, 63), 1);
602        assert_eq!(dsra(0x8000_0000_0000_0000, 63), u64::MAX);
603        // DSRA is a true 64-bit shift, so the SRA erratum cannot manifest.
604        assert_eq!(dsra(0x0123_4567_89AB_CDEF, 16), 0x0000_0123_4567_89AB);
605    }
606
607    // ----------------------------------------------------- multiply / divide
608
609    #[test]
610    fn multiply_writes_hi_lo_with_sign_extended_halves() {
611        let r = mult(sext32(0x0001_0000), sext32(0x0001_0000));
612        assert_eq!((r.hi, r.lo), (1, 0), "0x10000^2 = 0x1_0000_0000");
613        // Negative operands.
614        let r = mult(sext32(0xFFFF_FFFF), sext32(2));
615        assert_eq!(r.lo, sext32(0xFFFF_FFFE), "-1 * 2 = -2");
616        assert_eq!(r.hi, u64::MAX, "the high half sign-extends too");
617        // Unsigned does not sign-extend the operands.
618        let r = multu(0xFFFF_FFFF, 2);
619        assert_eq!((r.hi, r.lo), (1, sext32(0xFFFF_FFFE)));
620    }
621
622    /// **The MULT erratum**: with inputs that are not properly sign-extended
623    /// 32-bit values, `MULT` acts as a 64-bit by *35-bit* signed multiply.
624    #[test]
625    fn mult_reproduces_the_35_bit_sign_extension_erratum() {
626        // Bit 34 set in the second operand: hardware sign-extends from there, so
627        // the value is treated as negative even though bit 31 is clear.
628        let b = 0x0000_0004_0000_0000; // bit 34 set
629        let got = mult(1, b);
630        let naive = mult(1, 0); // what a 32-bit-only reading would give
631        assert_ne!(
632            got, naive,
633            "the erratum must make bit 34 of the second operand significant"
634        );
635        // For well-formed 32-bit inputs the erratum is invisible.
636        let clean = mult(sext32(7), sext32(6));
637        assert_eq!(clean.lo, 42);
638        assert_eq!(clean.hi, 0);
639    }
640
641    /// **The DIV erratum**: like `MULT`, `DIV` sign-extends its *divisor* on bit
642    /// **34**, so it behaves as a 32-bit by 35-bit signed division rather than
643    /// 32x32. Fails if someone "corrects" it to a plain 32-bit division.
644    ///
645    /// Source: `n64brew_wiki/markdown/VR4300.md` § Known Bugs, revision 2.2.
646    #[test]
647    fn div_reproduces_the_35_bit_divisor_sign_extension_erratum() {
648        // Bit 34 set in the divisor: hardware sign-extends from there, so the
649        // divisor is NEGATIVE despite bit 31 being clear. A 32-bit-only reading
650        // would treat the low 32 bits (zero) as the divisor and divide by zero.
651        let divisor = 0x0000_0004_0000_0000u64; // bit 34
652        let got = div(sext32(100), divisor);
653        let naive = div(sext32(100), 0); // what a 32-bit reading gives
654        assert_ne!(
655            got, naive,
656            "bit 34 of the divisor must be significant -- that IS the erratum"
657        );
658
659        // For well-formed sign-extended 32-bit inputs the erratum is invisible,
660        // which is why ordinary compiler output never trips over it.
661        let clean = div(sext32(100), sext32(7));
662        assert_eq!((clean.lo, clean.hi), (14, 2));
663        let negative = div(sext32(100), sext32(0xFFFF_FFF9)); // 100 / -7
664        assert_eq!(negative.lo, sext32(0xFFFF_FFF2), "-14");
665    }
666
667    /// `SRA` against **pre-computed** hardware values.
668    ///
669    /// Deliberately not `assert_eq!(sra(v, n), <the expression sra uses>)` — that
670    /// restates the implementation and can only catch a *different* one, never a
671    /// wrong one. These constants were derived independently from the erratum's
672    /// definition: 64-bit arithmetic shift, truncate to 32, sign-extend.
673    ///
674    /// The `SRAV` *instruction path* is pinned separately in `exec`, because a
675    /// helper-level test cannot see `Op::Srav` stop routing through this helper.
676    #[test]
677    fn sra_matches_precomputed_hardware_values() {
678        let rt = 0x0123_4567_89AB_CDEF;
679        assert_eq!(sra(rt, 1), 0xFFFF_FFFF_C4D5_E6F7, "sa=1");
680        assert_eq!(sra(rt, 8), 0x0000_0000_6789_ABCD, "sa=8");
681        assert_eq!(
682            sra(rt, 16),
683            0x0000_0000_4567_89AB,
684            "sa=16, the wiki's example"
685        );
686        assert_eq!(sra(rt, 31), 0x0000_0000_0246_8ACF, "sa=31");
687        // The amount masks to 5 bits, so 48 is 16.
688        assert_eq!(sra(rt, 48), sra(rt, 16), "sa masks to 5 bits");
689    }
690
691    #[test]
692    fn divide_writes_quotient_to_lo_and_remainder_to_hi() {
693        let r = div(sext32(17), sext32(5));
694        assert_eq!((r.lo, r.hi), (3, 2));
695        // MIPS truncates toward zero, so a negative dividend gives a negative
696        // remainder -- not the Euclidean result.
697        let r = div(sext32(0xFFFF_FFEF), sext32(5)); // -17 / 5
698        assert_eq!((r.lo, r.hi), (sext32(0xFFFF_FFFD), sext32(0xFFFF_FFFE)));
699        let r = divu(17, 5);
700        assert_eq!((r.lo, r.hi), (3, 2));
701    }
702
703    #[test]
704    fn divide_by_zero_does_not_panic() {
705        // Architecturally undefined; the values are conventional and flagged in
706        // docs/accuracy-ledger.md as needing hardware confirmation. What is NOT
707        // negotiable is that it must not panic -- a guest program can do this.
708        let r = div(sext32(5), 0);
709        assert_eq!(r.hi, sext32(5), "HI carries the dividend");
710        let _ = divu(5, 0);
711        let _ = ddiv(5, 0);
712        let _ = ddivu(5, 0);
713    }
714
715    #[test]
716    fn the_signed_divide_overflow_case_does_not_panic() {
717        // i32::MIN / -1 has no representable result. MIPS defines it as the
718        // dividend; in Rust the raw division would panic.
719        let r = div(sext32(0x8000_0000), sext32(0xFFFF_FFFF));
720        assert_eq!((r.lo, r.hi), (sext32(0x8000_0000), 0));
721        let r = ddiv(i64::MIN as u64, u64::MAX);
722        assert_eq!((r.lo, r.hi), (i64::MIN as u64, 0));
723    }
724
725    #[test]
726    fn the_64_bit_multiplies_keep_the_full_128_bit_product() {
727        let r = dmultu(u64::MAX, u64::MAX);
728        assert_eq!((r.hi, r.lo), (0xFFFF_FFFF_FFFF_FFFE, 1));
729        let r = dmult(u64::MAX, u64::MAX); // -1 * -1
730        assert_eq!((r.hi, r.lo), (0, 1));
731        let r = ddivu(u64::MAX, 2);
732        assert_eq!(r.lo, 0x7FFF_FFFF_FFFF_FFFF);
733    }
734
735    /// The documented pipeline stalls (UM Table 3-12). These are full-pipeline
736    /// stalls, not background operations.
737    #[test]
738    fn muldiv_stalls_match_the_manual() {
739        assert_eq!(muldiv_stall_cycles(MulDiv::Mult), 5);
740        assert_eq!(muldiv_stall_cycles(MulDiv::Multu), 5);
741        assert_eq!(muldiv_stall_cycles(MulDiv::Div), 37);
742        assert_eq!(muldiv_stall_cycles(MulDiv::Divu), 37);
743        assert_eq!(muldiv_stall_cycles(MulDiv::Dmult), 8);
744        assert_eq!(muldiv_stall_cycles(MulDiv::Dmultu), 8);
745        assert_eq!(muldiv_stall_cycles(MulDiv::Ddiv), 69);
746        assert_eq!(muldiv_stall_cycles(MulDiv::Ddivu), 69);
747    }
748
749    #[test]
750    fn the_mfhi_mflo_hazard_is_two_instructions_and_not_a_stall() {
751        // Documented as a non-interlocked hazard producing a wrong result, so the
752        // only thing to assert here is the window. Modeling it as a stall would
753        // add timing hardware does not have AND hide the observable wrong value.
754        assert_eq!(MFHI_MFLO_HAZARD_INSTRUCTIONS, 2);
755    }
756}