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}