Skip to main content

rustyn64_cpu/
fpu.rs

1//! FPU arithmetic (T-13-002).
2//!
3//! Pure functions over IEEE-754 values, with the VR4300's `FCSR` semantics
4//! layered on top. Kept separate from [`crate::fpr`] (the register file) and
5//! [`crate::cop1`] (the control registers) so each can be tested without the
6//! others.
7//!
8//! # Rounding
9//!
10//! `FCSR.RM` selects the mode (UM §7.2.4): 0 nearest-even, 1 toward zero,
11//! 2 toward +∞, 3 toward −∞.
12//!
13//! **Every operation here honors it**, arithmetic included. The four
14//! arithmetic operations are adapters over [`crate::softfloat`], which takes
15//! the mode as a parameter and rounds exactly once; the conversions take a
16//! [`Rounding`] directly.
17//!
18//! This paragraph has been wrong in both directions and is worth reading
19//! carefully before it is edited again. It first claimed the modes applied
20//! throughout while the arithmetic ignored them; it was then corrected to say
21//! the arithmetic could not honor them, which became false when the soft-float
22//! path landed. See `docs/engineering-lessons.md` §3.3c — the rule is that a
23//! comment asserting *what the code does* goes stale silently, because no test
24//! fails when it is wrong.
25//!
26//! # What is deliberately absent
27//!
28//! The **FP multiplication erratum** is not here. It is a property of specific
29//! early console revisions (`n64brew_wiki/markdown/VR4300.md`) and belongs with
30//! the revision model, not with the arithmetic; implementing it inline would
31//! make every multiply on every console wrong.
32
33// Two lints are allowed for this module, both because the thing they warn about
34// is the thing being modeled:
35//
36//   * `cast_precision_loss` -- an FPU's conversion instructions exist precisely
37//     to lose precision in a defined way. The loss is the behavior, and it is
38//     reported through the Inexact flag rather than avoided.
39//   * `float_cmp` -- `C.EQ` is IEEE equality and the exactness checks are exact
40//     by definition. An epsilon here would make the emulator report relations
41//     and flags the hardware does not.
42#![allow(clippy::cast_precision_loss, clippy::float_cmp)]
43
44use serde::{Deserialize, Serialize};
45
46/// Which VR4300 stepping a console carries.
47///
48/// The only behavior that currently depends on it is the **FP multiplication
49/// erratum**, and that dependency is why this is modeled as console state
50/// rather than folded into `mul`: an unconditional erratum would make every
51/// multiply on *every* console wrong, and most consoles do not have it.
52#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
53pub enum Stepping {
54    /// A later stepping, with the multiplication erratum **fixed**.
55    ///
56    /// The default, because it is the majority of hardware and because the
57    /// erratum's output is undocumented — see [`Stepping::has_mul_erratum`].
58    #[default]
59    Fixed,
60    /// An early stepping carrying the erratum: NUS-01 and NUS-02 (Japan only)
61    /// and NUS-03 (the first US revision).
62    Early,
63}
64
65impl Stepping {
66    /// Does this stepping carry the FP multiplication erratum?
67    ///
68    /// # Why the erratum is not implemented
69    ///
70    /// The trigger is documented (`n64brew_wiki/markdown/VR4300.md`): a
71    /// multiply whose *preceding* multiply had a NaN, zero or infinity operand
72    /// *"may produce unexpected results"*. GCC's `-mfix4300` works around it by
73    /// inserting two `nop`s after every `MUL.S`/`MUL.D`/`MULT`.
74    ///
75    /// **What the corrupted output actually is has never been characterized** —
76    /// our own `ref-docs/2026-07-20-vr4300-timing-supplement.md` lists it under
77    /// undocumented constants, with only trigger conditions known. So the
78    /// erratum can be *detected* here but not *reproduced*, and inventing a
79    /// plausible wrong value would be exactly the fitted-constant failure
80    /// `docs/accuracy-ledger.md` forbids: every later result built on it would
81    /// stop being evidence.
82    ///
83    /// Accuracy-ledger **U-7**. Selecting [`Stepping::Early`] therefore changes
84    /// nothing yet — it exists so that when the output *is* characterized, the
85    /// switch is already in the right place rather than threaded through
86    /// afterwards.
87    #[must_use]
88    pub const fn has_mul_erratum(self) -> bool {
89        matches!(self, Self::Early)
90    }
91}
92
93/// Would the erratum's trigger condition fire for this multiply?
94///
95/// True when an operand of the **previous** multiply was a NaN, zero or
96/// infinity. Exposed so a future characterization has a tested trigger to hang
97/// the corrupted output on, and so a trace can flag affected instructions today.
98#[must_use]
99pub fn mul_erratum_triggers(prev_a: f64, prev_b: f64) -> bool {
100    let suspicious = |v: f64| v.is_nan() || v == 0.0 || v.is_infinite();
101    suspicious(prev_a) || suspicious(prev_b)
102}
103
104/// The `FCSR` cause/flag bits an operation can raise (UM §7.2.2).
105///
106/// # Completeness
107///
108/// All five are modeled for the four arithmetic operations, which compute
109/// through [`crate::softfloat`] and so have the exact pre-rounding result
110/// available. `inexact` and `underflow` were the two that previously never set
111/// for ordinary rounding — accuracy-ledger C-11 — and detecting them is
112/// precisely why that module exists.
113///
114/// Still **not** produced anywhere: the unmaskable *unimplemented-operation*
115/// cause (bit 17), which the VR4300 raises for subnormal operands and results.
116/// It is not an IEEE exception and is not part of this struct; see
117/// [`CAUSE_UNIMPLEMENTED`].
118///
119/// Returned rather than written, because `FCSR` belongs to
120/// [`crate::cop1::Cop1Control`] and an arithmetic helper that reached into it
121/// would need to own it.
122// Five bools, one per IEEE exception. Clippy suggests a bitflags type; the
123// architectural field IS five independent conditions that can co-occur (an
124// overflow is also inexact), and naming them costs nothing at this size.
125#[allow(clippy::struct_excessive_bools)]
126#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
127pub struct Flags {
128    /// Invalid operation — a signaling NaN, or an undefined form like `0 × ∞`.
129    pub invalid: bool,
130    /// Division by zero, with a finite non-zero numerator.
131    pub div_by_zero: bool,
132    /// The result overflowed the format's range.
133    pub overflow: bool,
134    /// The result underflowed to a subnormal or zero.
135    pub underflow: bool,
136    /// The result was not exactly representable.
137    pub inexact: bool,
138}
139
140impl Flags {
141    /// No exceptions raised.
142    pub const NONE: Self = Self {
143        invalid: false,
144        div_by_zero: false,
145        overflow: false,
146        underflow: false,
147        inexact: false,
148    };
149
150    /// Just the invalid-operation flag.
151    pub const INVALID: Self = Self {
152        invalid: true,
153        ..Self::NONE
154    };
155
156    /// Pack into the `FCSR` **Cause** bits 16..=12 and **Flags** bits 6..=2.
157    ///
158    /// Both at once because hardware sets them together: `Cause` is what the
159    /// current operation raised, `Flags` is the sticky accumulation.
160    ///
161    /// **Bit 17 (`Unimplemented Operation`) is not produced here** — it is not
162    /// an IEEE exception, and it is raised on its own by the conversions via
163    /// [`CAUSE_UNIMPLEMENTED`]. Documenting the range as 17..=12 implied this
164    /// helper could set it, which it cannot.
165    #[must_use]
166    pub const fn to_fcsr_bits(self) -> u32 {
167        let mut cause = 0u32;
168        let mut flags = 0u32;
169        if self.invalid {
170            cause |= 1 << 16;
171            flags |= 1 << 6;
172        }
173        if self.div_by_zero {
174            cause |= 1 << 15;
175            flags |= 1 << 5;
176        }
177        if self.overflow {
178            cause |= 1 << 14;
179            flags |= 1 << 4;
180        }
181        if self.underflow {
182            cause |= 1 << 13;
183            flags |= 1 << 3;
184        }
185        if self.inexact {
186            cause |= 1 << 12;
187            flags |= 1 << 2;
188        }
189        cause | flags
190    }
191}
192
193/// The rounding mode from `FCSR.RM` (UM §7.2.4).
194#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
195pub enum Rounding {
196    /// Round to nearest, ties to even. The IEEE default and `FCSR.RM = 0`.
197    Nearest,
198    /// Toward zero (truncate).
199    TowardZero,
200    /// Toward +∞.
201    TowardPlusInf,
202    /// Toward −∞.
203    TowardMinusInf,
204}
205
206impl Rounding {
207    /// Decode `FCSR.RM` (bits 1..=0).
208    #[must_use]
209    pub const fn from_rm(rm: u8) -> Self {
210        match rm & 0b11 {
211            1 => Self::TowardZero,
212            2 => Self::TowardPlusInf,
213            3 => Self::TowardMinusInf,
214            _ => Self::Nearest,
215        }
216    }
217}
218
219/// A result plus the flags producing it raised.
220///
221/// Deliberately **not** `Eq`: `T` is a float, and `NaN != NaN`. Deriving `Eq`
222/// would assert a reflexivity that FP values do not have.
223#[allow(
224    clippy::derived_hash_with_manual_eq,
225    clippy::derive_partial_eq_without_eq
226)]
227#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
228pub struct Outcome<T> {
229    /// The computed value.
230    pub value: T,
231    /// What the operation raised.
232    pub flags: Flags,
233}
234
235impl<T> Outcome<T> {
236    /// Did the operation underflow?
237    #[must_use]
238    pub const fn underflowed(&self) -> bool {
239        self.flags.underflow
240    }
241}
242
243/// Is this `f32` a **signaling** NaN *as the VR4300 classifies one*?
244///
245/// # The convention is inverted from IEEE-754:2008
246///
247/// IEEE-754:2008 says the significand's MSB **set** means *quiet*. The VR4300
248/// predates that edition and uses the **legacy MIPS convention**, where the
249/// significand MSB **set** means *signaling*. So `0x7FC0_0000` — the pattern
250/// every modern language calls a quiet NaN, and what Rust's `f32::NAN` is — is
251/// a **signaling** NaN to this processor, and raises Invalid.
252///
253/// # How this was established
254///
255/// Not from a manual: from n64-systemtest's own expectations, which name their
256/// constants by the IEEE convention and then assert the opposite behavior.
257/// For a non-signaling compare (`C.EQ`, `C.F`, …) it expects
258/// `QUIET_NAN_START_32` (`0x7FC0_0000`, MSB set) to raise Invalid and
259/// `SIGNALING_NAN_END_32` (`0x7FBF_FFFF`, MSB clear) to raise nothing. The
260/// signaling compare forms (`C.SF`, `C.SEQ`, …) raise Invalid for both, which
261/// is the ordinary IEEE rule for those forms and so does not distinguish them.
262///
263/// The corroboration that makes this more than a curve fit: the VR4300's own
264/// default NaN result is `0x7FBF_FFFF`, MSB **clear**. Under IEEE that would be
265/// a processor whose invalid-operation result is a *signaling* NaN — absurd,
266/// since it would re-trap on first use. Under this convention it is exactly
267/// what it should be, a quiet one.
268///
269/// Accuracy ledger **C-12**.
270#[must_use]
271pub const fn is_snan_f32(v: f32) -> bool {
272    let b = v.to_bits();
273    // Exponent all ones, significand MSB SET. No payload check is needed: the
274    // MSB being set already makes the significand non-zero, so this cannot
275    // catch an infinity.
276    b & 0x7F80_0000 == 0x7F80_0000 && b & 0x0040_0000 != 0
277}
278
279/// Is this `f64` a **signaling** NaN as the VR4300 classifies one?
280///
281/// See [`is_snan_f32`] — the convention is inverted from IEEE-754:2008.
282#[must_use]
283pub const fn is_snan_f64(v: f64) -> bool {
284    let b = v.to_bits();
285    b & 0x7FF0_0000_0000_0000 == 0x7FF0_0000_0000_0000 && b & 0x0008_0000_0000_0000 != 0
286}
287
288/// Is this a **subnormal** — a non-zero value with a zero exponent field?
289///
290/// Load-bearing on the VR4300 in a way it is not on most FPUs: this processor
291/// **cannot compute with subnormals at all**. A subnormal operand, or a
292/// subnormal result with `FCSR.FS` clear, raises the unmaskable
293/// *unimplemented operation* cause rather than producing a number. See
294/// [`arith_unimplemented_s`].
295#[must_use]
296pub const fn is_subnormal_f32(v: f32) -> bool {
297    let b = v.to_bits();
298    b & 0x7F80_0000 == 0 && b & 0x007F_FFFF != 0
299}
300
301/// Is this `f64` a subnormal? See [`is_subnormal_f32`].
302#[must_use]
303pub const fn is_subnormal_f64(v: f64) -> bool {
304    let b = v.to_bits();
305    b & 0x7FF0_0000_0000_0000 == 0 && b & 0x000F_FFFF_FFFF_FFFF != 0
306}
307
308/// Is this a NaN the VR4300's arithmetic cannot handle?
309///
310/// A NaN with the significand MSB **clear** — quiet by this processor's own
311/// inverted convention (ledger C-12). It cannot propagate one in hardware, so
312/// arithmetic on it raises *unimplemented operation*.
313///
314/// The complementary case is the opposite of what IEEE would lead you to
315/// expect: an MSB-**set** NaN is *signaling* here and raises the ordinary
316/// Invalid, which [`is_snan_f32`] covers. Both NaN classes trap — they trap
317/// **differently**, and swapping them is invisible until `FCSR` is read back.
318#[must_use]
319pub const fn is_unimplemented_nan_f32(v: f32) -> bool {
320    let b = v.to_bits();
321    b & 0x7F80_0000 == 0x7F80_0000 && b & 0x007F_FFFF != 0 && b & 0x0040_0000 == 0
322}
323
324/// See [`is_unimplemented_nan_f32`].
325#[must_use]
326pub const fn is_unimplemented_nan_f64(v: f64) -> bool {
327    let b = v.to_bits();
328    b & 0x7FF0_0000_0000_0000 == 0x7FF0_0000_0000_0000
329        && b & 0x000F_FFFF_FFFF_FFFF != 0
330        && b & 0x0008_0000_0000_0000 == 0
331}
332
333/// Do these arithmetic operands force *unimplemented operation* before the
334/// operation is even attempted?
335///
336/// **Operand subnormality wins over everything**, including a NaN that would
337/// otherwise raise Invalid: n64-systemtest pairs a quiet NaN with a subnormal
338/// and expects unimplemented, not invalid.
339///
340/// Applies to `ADD`/`SUB`/`MUL`/`DIV` only. **Compares are exempt** — the
341/// suite's compare tests expect a subnormal operand to compare as an ordinary
342/// number with no flags at all. Worth stating, because "this FPU cannot do
343/// subnormals" sounds like it should be universal and is not.
344#[must_use]
345pub const fn arith_unimplemented_s(a: f32, b: f32) -> bool {
346    is_subnormal_f32(a)
347        || is_subnormal_f32(b)
348        || is_unimplemented_nan_f32(a)
349        || is_unimplemented_nan_f32(b)
350}
351
352/// See [`arith_unimplemented_s`].
353#[must_use]
354pub const fn arith_unimplemented_d(a: f64, b: f64) -> bool {
355    is_subnormal_f64(a)
356        || is_subnormal_f64(b)
357        || is_unimplemented_nan_f64(a)
358        || is_unimplemented_nan_f64(b)
359}
360
361/// Flush a subnormal result to zero or to the smallest normal, per `FCSR.FS`.
362///
363/// **Which one depends on the rounding mode**, and that is the part most easily
364/// got wrong: "flush to zero" is the whole story only for round-to-nearest and
365/// toward-zero. A directed mode that rounds *away* from zero must deliver the
366/// smallest **normal** of that sign instead, because zero is on the wrong side
367/// of the true result.
368///
369/// n64-systemtest pins all four: a tiny negative result gives `-0` under
370/// nearest, toward-zero and toward-`+inf`, and `-f32::MIN_POSITIVE` under
371/// toward-`-inf`.
372#[must_use]
373pub fn flush_subnormal_f32(v: f32, mode: Rounding) -> f32 {
374    let negative = v.is_sign_negative();
375    if flushes_away_from_zero(negative, mode) {
376        if negative {
377            -f32::MIN_POSITIVE
378        } else {
379            f32::MIN_POSITIVE
380        }
381    } else if negative {
382        -0.0
383    } else {
384        0.0
385    }
386}
387
388/// See [`flush_subnormal_f32`].
389#[must_use]
390pub fn flush_subnormal_f64(v: f64, mode: Rounding) -> f64 {
391    let negative = v.is_sign_negative();
392    if flushes_away_from_zero(negative, mode) {
393        if negative {
394            -f64::MIN_POSITIVE
395        } else {
396            f64::MIN_POSITIVE
397        }
398    } else if negative {
399        -0.0
400    } else {
401        0.0
402    }
403}
404
405/// Does the rounding mode push this sign away from zero?
406const fn flushes_away_from_zero(negative: bool, mode: Rounding) -> bool {
407    match mode {
408        Rounding::Nearest | Rounding::TowardZero => false,
409        Rounding::TowardPlusInf => !negative,
410        Rounding::TowardMinusInf => negative,
411    }
412}
413
414/// `ADD.S`, `SUB.S`, `MUL.S`, `DIV.S` and their `.D` counterparts.
415///
416/// Each is a thin adapter over [`crate::softfloat`], which computes the
417/// correctly-rounded result **and** the exact IEEE exception flags in one pass.
418///
419/// # Why these are not `a + b`
420///
421/// The native operators produce the right value and discard everything else:
422/// there is no way to ask them whether the operation was inexact, whether it
423/// underflowed, or what a directed rounding mode would have given. The VR4300
424/// reports all three through `FCSR`, so an FPU built on `+`/`-`/`*`/`/` is
425/// bit-exact on values and silently wrong on flags — which is where accuracy
426/// ledger **C-11** found it, with `inexact` set only as a side effect of
427/// overflow and `underflow` never set at all.
428///
429/// The soft-float path is checked against those same native operators as an
430/// independent oracle: in round-to-nearest its results must be bit-identical
431/// across a large pseudo-random corpus in both formats. See
432/// `softfloat::tests`.
433fn arith_s(a: f32, b: f32, op: u8, mode: Rounding) -> Outcome<f32> {
434    use crate::softfloat::{self, F32};
435    let (a_bits, b_bits) = (u64::from(a.to_bits()), u64::from(b.to_bits()));
436    let out = match op {
437        0 => softfloat::add(a_bits, b_bits, F32, mode),
438        1 => softfloat::sub(a_bits, b_bits, F32, mode),
439        2 => softfloat::mul(a_bits, b_bits, F32, mode),
440        _ => softfloat::div(a_bits, b_bits, F32, mode),
441    };
442    Outcome {
443        value: f32::from_bits(out.bits as u32),
444        flags: out.flags,
445    }
446}
447
448fn arith_d(a: f64, b: f64, op: u8, mode: Rounding) -> Outcome<f64> {
449    use crate::softfloat::{self, F64};
450    let (a_bits, b_bits) = (a.to_bits(), b.to_bits());
451    let out = match op {
452        0 => softfloat::add(a_bits, b_bits, F64, mode),
453        1 => softfloat::sub(a_bits, b_bits, F64, mode),
454        2 => softfloat::mul(a_bits, b_bits, F64, mode),
455        _ => softfloat::div(a_bits, b_bits, F64, mode),
456    };
457    Outcome {
458        value: f64::from_bits(out.bits),
459        flags: out.flags,
460    }
461}
462
463/// `ADD.S`.
464#[must_use]
465pub fn add_s(a: f32, b: f32, mode: Rounding) -> Outcome<f32> {
466    arith_s(a, b, 0, mode)
467}
468
469/// `SUB.S`.
470#[must_use]
471pub fn sub_s(a: f32, b: f32, mode: Rounding) -> Outcome<f32> {
472    arith_s(a, b, 1, mode)
473}
474
475/// `MUL.S`.
476///
477/// **Does not model the VR4300 multiplication erratum** — see the module docs.
478#[must_use]
479pub fn mul_s(a: f32, b: f32, mode: Rounding) -> Outcome<f32> {
480    arith_s(a, b, 2, mode)
481}
482
483/// `DIV.S`.
484#[must_use]
485pub fn div_s(a: f32, b: f32, mode: Rounding) -> Outcome<f32> {
486    arith_s(a, b, 3, mode)
487}
488
489/// `ADD.D`.
490#[must_use]
491pub fn add_d(a: f64, b: f64, mode: Rounding) -> Outcome<f64> {
492    arith_d(a, b, 0, mode)
493}
494
495/// `SUB.D`.
496#[must_use]
497pub fn sub_d(a: f64, b: f64, mode: Rounding) -> Outcome<f64> {
498    arith_d(a, b, 1, mode)
499}
500
501/// `MUL.D`.
502#[must_use]
503pub fn mul_d(a: f64, b: f64, mode: Rounding) -> Outcome<f64> {
504    arith_d(a, b, 2, mode)
505}
506
507/// `DIV.D`.
508#[must_use]
509pub fn div_d(a: f64, b: f64, mode: Rounding) -> Outcome<f64> {
510    arith_d(a, b, 3, mode)
511}
512
513/// `ABS.S` — clears the sign bit.
514///
515/// Written as an explicit bit clear rather than `f32::abs`. The two are
516/// **equivalent**, including for NaN payloads — mutation testing confirmed
517/// swapping them changes nothing — so this is a readability choice, not a
518/// correctness one: the hardware operation *is* a bit clear, and saying so makes
519/// the NaN-payload behavior obvious instead of something to look up.
520#[must_use]
521pub const fn abs_s(a: f32) -> f32 {
522    f32::from_bits(a.to_bits() & 0x7FFF_FFFF)
523}
524
525/// `NEG.S` — flips the sign bit.
526#[must_use]
527pub const fn neg_s(a: f32) -> f32 {
528    f32::from_bits(a.to_bits() ^ 0x8000_0000)
529}
530
531/// `ABS.D`.
532#[must_use]
533pub const fn abs_d(a: f64) -> f64 {
534    f64::from_bits(a.to_bits() & 0x7FFF_FFFF_FFFF_FFFF)
535}
536
537/// `NEG.D`.
538#[must_use]
539pub const fn neg_d(a: f64) -> f64 {
540    f64::from_bits(a.to_bits() ^ 0x8000_0000_0000_0000)
541}
542
543/// The outcome of an ordered comparison: which of the three mutually exclusive
544/// relations holds.
545///
546/// Named rather than three bools, because exactly one is true and a bool triple
547/// makes the impossible states representable.
548#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
549pub enum Relation {
550    /// `fs < ft`.
551    Less,
552    /// `fs == ft`.
553    Equal,
554    /// `fs > ft`.
555    Greater,
556    /// At least one operand is a NaN, so no ordering exists.
557    Unordered,
558}
559
560/// `C.cond.fmt` — compare two single-precision values.
561///
562/// # The condition encoding
563///
564/// The 4-bit `cond` field is **systematic**, not sixteen unrelated mnemonics
565/// (UM Table 7-11):
566///
567/// | Bit | Meaning |
568/// | --- | --- |
569/// | 3 | raise Invalid when the operands are **unordered** (the signaling forms) |
570/// | 2 | true when `fs < ft` |
571/// | 1 | true when `fs == ft` |
572/// | 0 | true when the operands are **unordered** |
573///
574/// So `C.EQ` is `cond = 2`, `C.OLT` is `4`, `C.OLE` is `6`, `C.UN` is `1`, and
575/// each signaling variant is its ordinary form plus 8. Writing the sixteen
576/// mnemonics as sixteen cases invites getting one wrong; deriving them from the
577/// bits makes all sixteen correct or none.
578///
579/// Note **`Greater` appears in no bit**: `fs > ft` is simply "none of less,
580/// equal or unordered", which is why three condition bits suffice. Software
581/// tests it by branching on the complement of `C.OLE`.
582#[must_use]
583pub fn compare_s(a: f32, b: f32, cond: u8) -> Outcome<bool> {
584    let rel = relation_f32(a, b);
585    compare_result(rel, cond, is_snan_f32(a) || is_snan_f32(b))
586}
587
588/// `C.cond.fmt` — compare two double-precision values.
589#[must_use]
590pub fn compare_d(a: f64, b: f64, cond: u8) -> Outcome<bool> {
591    let rel = relation_f64(a, b);
592    compare_result(rel, cond, is_snan_f64(a) || is_snan_f64(b))
593}
594
595/// Which relation holds between two `f32`s.
596///
597/// Exact equality is **correct here and epsilon comparison would be wrong**:
598/// `C.EQ` is defined as IEEE equality, not approximate equality, so a tolerance
599/// would make the instruction report a relation the hardware does not.
600fn relation_f32(a: f32, b: f32) -> Relation {
601    if a.is_nan() || b.is_nan() {
602        Relation::Unordered
603    } else if a < b {
604        Relation::Less
605    } else if a == b {
606        Relation::Equal
607    } else {
608        Relation::Greater
609    }
610}
611
612/// Which relation holds between two `f64`s.
613///
614/// Exact equality is **correct here and epsilon comparison would be wrong**:
615/// `C.EQ` is defined as IEEE equality, not approximate equality, so a tolerance
616/// would make the instruction report a relation the hardware does not.
617fn relation_f64(a: f64, b: f64) -> Relation {
618    if a.is_nan() || b.is_nan() {
619        Relation::Unordered
620    } else if a < b {
621        Relation::Less
622    } else if a == b {
623        Relation::Equal
624    } else {
625        Relation::Greater
626    }
627}
628
629/// Apply the condition bits to a computed relation.
630fn compare_result(rel: Relation, cond: u8, snan: bool) -> Outcome<bool> {
631    let unordered = matches!(rel, Relation::Unordered);
632    let value = (matches!(rel, Relation::Less) && cond & 0b100 != 0)
633        || (matches!(rel, Relation::Equal) && cond & 0b010 != 0)
634        || (unordered && cond & 0b001 != 0);
635
636    let mut flags = Flags::NONE;
637    // Bit 3 selects the SIGNALING forms, which raise Invalid on *any*
638    // unordered comparison -- including one caused by a merely quiet NaN. That
639    // is the whole difference between `C.EQ` and `C.SEQ`, and it is why the
640    // quiet/signaling test used elsewhere is not sufficient on its own here.
641    if unordered && cond & 0b1000 != 0 {
642        flags.invalid = true;
643    }
644    // A signaling NaN operand raises Invalid whatever the condition.
645    if snan {
646        flags.invalid = true;
647    }
648    Outcome { value, flags }
649}
650
651/// `FCSR.Cause` — bits **17:12**, the field an operation replaces wholesale.
652///
653/// **UM §7.2.4, Figure 7-2** ("Control/Status Register Bit Assignments") gives
654/// `FCR31`'s layout with `Cause` spanning 17:12, six bits wide. **Figure 7-3**
655/// then names the bits, and the asymmetry below is visible in it directly:
656/// `Cause` carries `E V Z O U I` while `Enables` and `Flags` carry only
657/// `V Z O U I`.
658///
659/// **17:12, not 16:12.** That extra bit is `Cause.E`, Unimplemented Operation —
660/// part of `Cause` despite having no `Enable` bit and no sticky `Flags` twin,
661/// which makes this mask the *only* thing that ever clears it. A narrower 16:12
662/// mask left bit 17 permanently set once raised: a shipped bug in this project's
663/// history, and the reason the constant lives here rather than being written out
664/// at each site. It had reached four copies.
665///
666/// Only the five *maskable* conditions live in 16:12; that narrower range is what
667/// an enable comparison uses, and it is a different statement from this one.
668pub const CAUSE_MASK: u32 = 0x3F << 12;
669
670/// The `Unimplemented Operation` cause bit (`FCSR` bit 17).
671///
672/// Distinct from Invalid: it means *"this processor cannot do this in
673/// hardware — trap to software"*, not *"the operation is mathematically
674/// undefined"*. The VR4300 uses it for the long-integer conversion restriction
675/// below, which is a **hardware limitation**, not a numerical error.
676pub const CAUSE_UNIMPLEMENTED: u32 = 1 << 17;
677
678/// Does a 64-bit integer satisfy `CVT.[S,D].L`'s range restriction?
679///
680/// > *"When converting a long integer to a single- or double-precision
681/// > floating-point number (`CVT.[S,D].L`), bits 63:55 of the 64-bit integer
682/// > must be all zeroes or ones, otherwise the VR4300 processor raises a
683/// > floating-point instruction exception."* — UM §7.5.2
684///
685/// This is a **VR4300-specific hardware limitation**, not IEEE behavior: the
686/// value is perfectly representable, the processor simply declines. An emulator
687/// that converts it anyway produces a *correct* number where hardware traps, so
688/// software's fixup path never runs and the difference surfaces far downstream.
689#[must_use]
690pub const fn long_convertible(v: i64) -> bool {
691    let top = (v >> 55) & 0x1FF;
692    top == 0 || top == 0x1FF
693}
694
695/// `CVT.D.S` — single to double. Always exact: every `f32` is an `f64`.
696#[must_use]
697pub fn cvt_d_s(v: f32) -> Outcome<f64> {
698    Outcome {
699        value: f64::from(v),
700        flags: if is_snan_f32(v) {
701            Flags::INVALID
702        } else {
703            Flags::NONE
704        },
705    }
706}
707
708/// `CVT.S.D` — double to single. Can overflow, underflow or lose precision.
709#[must_use]
710pub fn cvt_s_d(v: f64) -> Outcome<f32> {
711    let value = v as f32;
712    let mut flags = Flags::NONE;
713    if is_snan_f64(v) {
714        flags.invalid = true;
715    }
716    if v.is_finite() {
717        if value.is_infinite() {
718            flags.overflow = true;
719            flags.inexact = true;
720        } else if value == 0.0 && v != 0.0 {
721            // A non-zero double that narrows to zero has underflowed -- the
722            // one underflow case this module can detect without the exact
723            // pre-rounding result.
724            flags.underflow = true;
725            flags.inexact = true;
726        } else if f64::from(value) != v {
727            flags.inexact = true;
728        }
729    }
730    Outcome { value, flags }
731}
732
733/// `2^63`, the bound an `i64` conversion must stay strictly below.
734const TWO_POW_63: f64 = 9_223_372_036_854_775_808.0;
735
736/// `2^52` — above this magnitude every `f64` is already an integer, so the
737/// rounding helpers can return the value untouched.
738const F64_INTEGRAL_THRESHOLD: f64 = 4_503_599_627_370_496.0;
739
740/// `|v|`, by clearing the sign bit.
741///
742/// `f64::abs` and friends live in `std`; this crate is `#![no_std]`, so the
743/// handful of float operations the FPU needs are implemented here rather than
744/// pulling in `libm` for four functions.
745const fn fabs(v: f64) -> f64 {
746    f64::from_bits(v.to_bits() & 0x7FFF_FFFF_FFFF_FFFF)
747}
748
749/// Truncate toward zero.
750fn trunc(v: f64) -> f64 {
751    if !v.is_finite() || fabs(v) >= F64_INTEGRAL_THRESHOLD {
752        // Already integral (or not a number), so there is nothing to remove.
753        return v;
754    }
755    (v as i64) as f64
756}
757
758/// Round toward −∞.
759fn floor(v: f64) -> f64 {
760    let t = trunc(v);
761    if v < 0.0 && t != v { t - 1.0 } else { t }
762}
763
764/// Round toward +∞.
765fn ceil(v: f64) -> f64 {
766    let t = trunc(v);
767    if v > 0.0 && t != v { t + 1.0 } else { t }
768}
769
770/// Round to nearest, **ties to even**.
771///
772/// Not "round half away from zero", which is what most `round` functions do and
773/// which no MIPS rounding mode selects.
774fn round_ties_even(v: f64) -> f64 {
775    if !v.is_finite() || fabs(v) >= F64_INTEGRAL_THRESHOLD {
776        return v;
777    }
778    let f = floor(v);
779    let diff = v - f;
780    if diff > 0.5 {
781        f + 1.0
782    } else if diff < 0.5 {
783        f
784    } else if (f as i64) % 2 == 0 {
785        // Exactly halfway: pick the even neighbor.
786        f
787    } else {
788        f + 1.0
789    }
790}
791
792/// Round a float to an integer under an explicit [`Rounding`] mode.
793///
794/// Split out because `CVT.W`, `ROUND.W`, `TRUNC.W`, `CEIL.W` and `FLOOR.W`
795/// differ **only** in this: `CVT` uses `FCSR.RM`, and the other four hard-code
796/// one mode each. Sharing the body means a rounding bug cannot exist in one and
797/// not the others.
798#[must_use]
799pub fn round_f64(v: f64, mode: Rounding) -> f64 {
800    match mode {
801        // `round_ties_even` rather than `round`, which rounds half AWAY from
802        // zero -- a different mode that no MIPS setting selects.
803        Rounding::Nearest => round_ties_even(v),
804        Rounding::TowardZero => trunc(v),
805        Rounding::TowardPlusInf => ceil(v),
806        Rounding::TowardMinusInf => floor(v),
807    }
808}
809
810/// Convert a float to a 32-bit integer under `mode`.
811///
812/// An out-of-range or NaN input raises **Invalid**. The value returned in that
813/// case is `i32::MAX`, which is the conventional MIPS result — and a *choice*
814/// here, since the architecture leaves it undefined when the exception is
815/// masked.
816#[must_use]
817pub fn to_i32(v: f64, mode: Rounding) -> Outcome<i32> {
818    let r = round_f64(v, mode);
819    if v.is_nan() || r < f64::from(i32::MIN) || r > f64::from(i32::MAX) {
820        return Outcome {
821            value: i32::MAX,
822            flags: Flags::INVALID,
823        };
824    }
825    let value = r as i32;
826    let mut flags = Flags::NONE;
827    if f64::from(value) != v {
828        flags.inexact = true;
829    }
830    Outcome { value, flags }
831}
832
833/// Convert a float to a 64-bit integer under `mode`.
834#[must_use]
835pub fn to_i64(v: f64, mode: Rounding) -> Outcome<i64> {
836    let r = round_f64(v, mode);
837    // The bounds are compared as f64 deliberately: `i64::MAX` is not exactly
838    // representable, so `r > i64::MAX as f64` is the correct test and
839    // `r as i64 == i64::MAX` is not.
840    if v.is_nan() || !(-TWO_POW_63..TWO_POW_63).contains(&r) {
841        return Outcome {
842            value: i64::MAX,
843            flags: Flags::INVALID,
844        };
845    }
846    let value = r as i64;
847    let mut flags = Flags::NONE;
848    if value as f64 != v {
849        flags.inexact = true;
850    }
851    Outcome { value, flags }
852}
853
854/// `fmt` for single precision, as the COP1 encoding gives it.
855pub const FMT_S: u8 = 16;
856/// `fmt` for double precision.
857pub const FMT_D: u8 = 17;
858/// `fmt` for 32-bit fixed point (`W`), the source format of `CVT.fmt.W`.
859pub const FMT_W: u8 = 20;
860/// `fmt` for 64-bit fixed point (`L`).
861pub const FMT_L: u8 = 21;
862
863/// Pipeline cycles for a COP1 arithmetic instruction — **UM Table 7-14**.
864///
865/// A direct transcription of the table, indexed by `funct` and the source
866/// `fmt`. Written as data for the same reason the COP0 tables are: every entry
867/// is a documented number with no generating rule, and a `match` over prose
868/// becomes a place to forget one.
869///
870/// | Instruction | S | D | W | L |
871/// | --- | --- | --- | --- | --- |
872/// | `ADD`/`SUB` | 3 | 3 | | |
873/// | `MUL` | 5 | 8 | | |
874/// | `DIV`/`SQRT` | 29 | 58 | | |
875/// | `ABS`/`MOV`/`NEG` | 1 | 1 | | |
876/// | `ROUND`/`TRUNC`/`CEIL`/`FLOOR` (`.W` and `.L`) | 5 | 5 | | |
877/// | `CVT.S` | — | 2 | 5 | 5 |
878/// | `CVT.D` | 1 | — | 5 | 5 |
879/// | `CVT.W`/`CVT.L` | 5 | 5 | | |
880/// | `C.cond` | 1 | 1 | | |
881///
882/// # What a `1` means, and why it is not charged
883///
884/// One cycle is what an ordinary instruction already takes, so a rate of 1 is
885/// *no* stall — `ABS`, `MOV`, `NEG` and `C.cond` cost nothing extra. Charging
886/// them one cycle would make them uniquely slow among single-cycle
887/// instructions.
888///
889/// # The `+1` for a dependent consumer is not in this table
890///
891/// The manual's note is that *"if a floating-point result for these
892/// instructions is needed by the subsequent instruction, the latency is the
893/// execution rate plus one, due to the fact that an EX-to-RF bypass is not
894/// performed"*. That extra cycle is not added here: the stall these numbers
895/// produce holds the whole pipeline, so the consumer spends its own cycle after
896/// the stall drains and arrives at rate + 1 on its own.
897///
898/// # Not modeled: the early exit
899///
900/// UM §7.5.6 and this table's own note 2 say a multicycle operation whose
901/// result is *obvious* — a zero or infinity operand, a power-of-two multiplier
902/// — completes in two cycles instead. That is documented behavior we do not
903/// yet reproduce, so trivial operands are charged the full rate and the model
904/// runs **slower** than hardware there. Accuracy ledger C-29.
905#[must_use]
906// One arm per table row, kept separate on purpose. Clippy would merge the arms
907// that happen to share a number and fold the 1s into the wildcard; that turns a
908// transcription anyone can check line-by-line against the manual into an
909// expression they have to re-derive. The rows are the documentation.
910#[allow(clippy::match_same_arms, clippy::manual_range_patterns)]
911pub const fn delay_cycles(funct: u8, fmt: u8) -> u32 {
912    let double = fmt == FMT_D;
913    match funct {
914        // ADD, SUB.
915        0o00 | 0o01 => 3,
916        // MUL.
917        0o02 => {
918            if double {
919                8
920            } else {
921                5
922            }
923        }
924        // DIV, and SQRT which costs the same.
925        0o03 | 0o04 => {
926            if double {
927                58
928            } else {
929                29
930            }
931        }
932        // ABS, MOV, NEG — one cycle, i.e. no stall.
933        0o05 | 0o06 | 0o07 => 1,
934        // ROUND.L, TRUNC.L, CEIL.L, FLOOR.L, ROUND.W, TRUNC.W, CEIL.W, FLOOR.W.
935        0o10..=0o17 => 5,
936        // CVT.S: 2 from double, 5 from a fixed-point format.
937        0o40 => {
938            if double {
939                2
940            } else {
941                5
942            }
943        }
944        // CVT.D: 1 from single, 5 from a fixed-point format.
945        0o41 => {
946            if fmt == FMT_S {
947                1
948            } else {
949                5
950            }
951        }
952        // CVT.W, CVT.L.
953        0o44 | 0o45 => 5,
954        // C.cond.fmt and anything else: one cycle.
955        _ => 1,
956    }
957}
958
959/// The stall a COP1 arithmetic instruction adds, in `PCycle`s.
960///
961/// Zero for the single-cycle operations; see [`delay_cycles`].
962#[must_use]
963pub const fn stall_cycles(funct: u8, fmt: u8) -> u32 {
964    let rate = delay_cycles(funct, fmt);
965    if rate > 1 { rate } else { 0 }
966}
967
968#[cfg(test)]
969mod tests {
970    use super::*;
971
972    /// **The VR4300 NaN convention is inverted from IEEE-754:2008**: the
973    /// significand MSB **set** means *signaling*, not quiet.
974    ///
975    /// So `0x7FC0_0001` — what every modern language calls a quiet NaN, and
976    /// what Rust produces — raises Invalid here, and `0x7F80_0001` does not.
977    /// The bit patterns are named for what they are *on this processor*, since
978    /// naming them the IEEE way is what made the original implementation
979    /// backwards. Accuracy ledger C-12.
980    #[test]
981    fn the_signaling_nan_is_the_one_with_the_significand_msb_set() {
982        let signals_here = f32::from_bits(0x7FC0_0001); // IEEE would call this quiet
983        let quiet_here = f32::from_bits(0x7F80_0001); // IEEE would call this signaling
984        assert!(
985            is_snan_f32(signals_here),
986            "MSB set is SIGNALING on the VR4300"
987        );
988        assert!(!is_snan_f32(quiet_here), "MSB clear is quiet");
989        assert!(!is_snan_f32(f32::INFINITY), "infinity is not a NaN");
990
991        assert!(add_s(signals_here, 1.0, Rounding::Nearest).flags.invalid);
992        assert!(
993            !add_s(quiet_here, 1.0, Rounding::Nearest).flags.invalid,
994            "a quiet NaN propagates quietly"
995        );
996
997        // Rust's own NaN is signaling to this processor. Stated explicitly
998        // because it is the case most likely to be reintroduced by someone
999        // "fixing" the convention back to IEEE.
1000        assert!(is_snan_f32(f32::NAN), "even f32::NAN signals here");
1001    }
1002
1003    /// The same, for doubles — the bit sits at a different position, so this is
1004    /// not a free consequence of the `f32` case.
1005    #[test]
1006    fn the_double_precision_signaling_bit_is_at_bit_51() {
1007        let signals_here = f64::from_bits(0x7FF8_0000_0000_0001);
1008        let quiet_here = f64::from_bits(0x7FF0_0000_0000_0001);
1009        assert!(is_snan_f64(signals_here));
1010        assert!(!is_snan_f64(quiet_here));
1011        assert!(add_d(signals_here, 1.0, Rounding::Nearest).flags.invalid);
1012        assert!(!add_d(quiet_here, 1.0, Rounding::Nearest).flags.invalid);
1013    }
1014
1015    /// The processor's own default NaN result must be **quiet by its own
1016    /// convention**, or every invalid operation would produce a value that
1017    /// re-traps the moment anything touches it.
1018    ///
1019    /// This is the corroboration that the inverted convention is real rather
1020    /// than a curve fit to the compare tests: `0x7FBF_FFFF` is the value
1021    /// hardware delivers, and it is only sane under the VR4300 reading.
1022    #[test]
1023    fn the_default_nan_result_is_quiet_by_the_vr4300_convention() {
1024        use crate::softfloat::{F32, F64};
1025        assert!(!is_snan_f32(f32::from_bits(F32.default_nan() as u32)));
1026        assert!(!is_snan_f64(f64::from_bits(F64.default_nan())));
1027    }
1028
1029    /// **`x/0` is `DivByZero`; `0/0` is Invalid.** They are different flags, and a
1030    /// handler distinguishes them — collapsing both into `DivByZero` reports a
1031    /// division fault for what is actually an undefined form.
1032    #[test]
1033    fn divide_by_zero_and_zero_over_zero_raise_different_flags() {
1034        let f = div_s(1.0, 0.0, Rounding::Nearest).flags;
1035        assert!(f.div_by_zero, "finite non-zero over zero");
1036        assert!(!f.invalid);
1037        assert!(!f.overflow, "infinite, but not an overflow");
1038
1039        let f = div_s(0.0, 0.0, Rounding::Nearest).flags;
1040        assert!(f.invalid, "0/0 is an undefined form");
1041        assert!(!f.div_by_zero);
1042    }
1043
1044    /// **`inf / 0` is not a division by zero.** IEEE reserves the flag for a
1045    /// *finite* non-zero numerator, because only there does a zero divisor
1046    /// create an infinity out of nothing — `inf / 0` was already infinite.
1047    ///
1048    /// The condition originally tested `!a.is_nan()`, which let infinities
1049    /// through and disagreed with the comment directly above it.
1050    #[test]
1051    fn an_infinite_numerator_over_zero_is_not_a_division_by_zero() {
1052        for a in [f32::INFINITY, f32::NEG_INFINITY] {
1053            let f = div_s(a, 0.0, Rounding::Nearest).flags;
1054            assert!(!f.div_by_zero, "{a} / 0 is not DivByZero");
1055            assert!(!f.invalid, "nor is it an undefined form");
1056        }
1057        // The finite case still is.
1058        assert!(div_s(1.0, 0.0, Rounding::Nearest).flags.div_by_zero);
1059        assert!(div_d(-2.5, 0.0, Rounding::Nearest).flags.div_by_zero);
1060        for a in [f64::INFINITY, f64::NEG_INFINITY] {
1061            assert!(!div_d(a, 0.0, Rounding::Nearest).flags.div_by_zero);
1062        }
1063    }
1064
1065    /// A double that narrows to zero has **underflowed** — the one underflow
1066    /// case detectable without the exact pre-rounding result.
1067    #[test]
1068    fn a_double_narrowing_to_zero_underflows() {
1069        let out = cvt_s_d(1e-300);
1070        assert!(out.underflowed(), "1e-300 has no f32 representation");
1071        assert!(out.flags.inexact);
1072        assert_eq!(out.value, 0.0);
1073        // A representable small value does not.
1074        assert!(!cvt_s_d(1e-30).underflowed());
1075        // ...and a genuine zero is not an underflow.
1076        assert!(!cvt_s_d(0.0).underflowed());
1077    }
1078
1079    /// A NaN produced from **non-NaN** inputs is an undefined form and raises
1080    /// Invalid — `∞ - ∞` here — even though neither operand was a NaN.
1081    #[test]
1082    fn a_nan_from_finite_or_infinite_inputs_is_invalid() {
1083        let f = sub_s(f32::INFINITY, f32::INFINITY, Rounding::Nearest).flags;
1084        assert!(f.invalid, "inf - inf is an undefined form");
1085        let f = mul_s(0.0, f32::INFINITY, Rounding::Nearest).flags;
1086        assert!(f.invalid, "0 * inf likewise");
1087    }
1088
1089    /// Overflow from finite operands sets both Overflow and Inexact.
1090    #[test]
1091    fn overflow_from_finite_operands_is_also_inexact() {
1092        let f = mul_s(f32::MAX, 2.0, Rounding::Nearest).flags;
1093        assert!(f.overflow);
1094        assert!(f.inexact, "an overflowed result is never exact");
1095        // But an infinity that was already infinite is not an overflow.
1096        assert!(!add_s(f32::INFINITY, 1.0, Rounding::Nearest).flags.overflow);
1097    }
1098
1099    /// `ABS`/`NEG` are **bit operations**, so they pass NaN payloads through
1100    /// rather than canonicalizing. `f32::abs` happens to agree, but specifying
1101    /// it as a bit clear is what makes the NaN behavior predictable.
1102    #[test]
1103    fn abs_and_neg_are_bit_operations_that_preserve_nan_payloads() {
1104        let nan = f32::from_bits(0xFF80_1234); // negative NaN with a payload
1105        assert_eq!(
1106            abs_s(nan).to_bits(),
1107            0x7F80_1234,
1108            "sign cleared, payload kept"
1109        );
1110        assert_eq!(neg_s(nan).to_bits(), 0x7F80_1234, "sign flipped");
1111        assert_eq!(
1112            neg_s(neg_s(nan)).to_bits(),
1113            nan.to_bits(),
1114            "and is an involution"
1115        );
1116        assert_eq!(abs_s(-0.0).to_bits(), 0.0f32.to_bits(), "negative zero too");
1117    }
1118
1119    /// `FCSR.RM` decodes per UM §7.2.4, and the default is nearest-even.
1120    #[test]
1121    fn the_rounding_mode_decodes_from_rm() {
1122        assert_eq!(Rounding::from_rm(0), Rounding::Nearest);
1123        assert_eq!(Rounding::from_rm(1), Rounding::TowardZero);
1124        assert_eq!(Rounding::from_rm(2), Rounding::TowardPlusInf);
1125        assert_eq!(Rounding::from_rm(3), Rounding::TowardMinusInf);
1126        assert_eq!(
1127            Rounding::from_rm(0xFC),
1128            Rounding::Nearest,
1129            "masked to 2 bits"
1130        );
1131    }
1132
1133    /// Flags map onto **both** the `Cause` and sticky `Flags` fields, because
1134    /// hardware sets them together. Writing only one leaves software unable to
1135    /// distinguish "raised now" from "raised at some point".
1136    #[test]
1137    fn flags_populate_both_the_cause_and_sticky_fields() {
1138        let bits = Flags {
1139            invalid: true,
1140            ..Flags::NONE
1141        }
1142        .to_fcsr_bits();
1143        assert_ne!(bits & (1 << 16), 0, "Cause.invalid");
1144        assert_ne!(bits & (1 << 6), 0, "Flags.invalid");
1145
1146        let bits = Flags {
1147            div_by_zero: true,
1148            inexact: true,
1149            ..Flags::NONE
1150        }
1151        .to_fcsr_bits();
1152        assert_eq!(bits, (1 << 15) | (1 << 5) | (1 << 12) | (1 << 2));
1153        assert_eq!(Flags::NONE.to_fcsr_bits(), 0);
1154    }
1155
1156    /// **Exact** arithmetic raises nothing — the flags must not be noisy, or
1157    /// software with the enables set traps constantly.
1158    ///
1159    /// # Why the operands are all dyadic
1160    ///
1161    /// This case previously included `(1e10, 1e-4)`, which is *not* exact in
1162    /// `f32` for any of the four operations. It passed only because `inexact`
1163    /// was never detected (accuracy ledger C-11), so the test was asserting the
1164    /// absence of a flag the implementation could not raise — and would have
1165    /// gone on passing however wrong the arithmetic became.
1166    ///
1167    /// Every pair below is a dyadic rational whose sum, difference, product
1168    /// **and** quotient are all exactly representable, so "no flags" is a real
1169    /// claim about the result rather than a claim about missing machinery.
1170    #[test]
1171    fn exact_arithmetic_raises_no_flags() {
1172        for (a, b) in [
1173            (1.0f32, 2.0f32),
1174            (-3.5, 0.25),
1175            (6.0, 0.5),
1176            // Large but still exact: 2^20 and 2^12, whose product 2^32 and
1177            // quotient 2^8 both land on the grid.
1178            (1_048_576.0, 4096.0),
1179        ] {
1180            for out in [
1181                add_s(a, b, Rounding::Nearest),
1182                sub_s(a, b, Rounding::Nearest),
1183                mul_s(a, b, Rounding::Nearest),
1184                div_s(a, b, Rounding::Nearest),
1185            ] {
1186                assert_eq!(out.flags, Flags::NONE, "{a} op {b} raised something");
1187            }
1188        }
1189        // Exact bit comparison, not a tolerance: these values are exactly
1190        // representable, so anything but the exact result is a bug -- and a
1191        // tolerance would hide it.
1192        assert_eq!(
1193            add_d(1.0, 2.0, Rounding::Nearest).value.to_bits(),
1194            3.0f64.to_bits()
1195        );
1196        assert_eq!(
1197            mul_d(3.0, 4.0, Rounding::Nearest).value.to_bits(),
1198            12.0f64.to_bits()
1199        );
1200    }
1201
1202    /// The condition field is **systematic**, so this checks the named mnemonics
1203    /// against the derivation rather than trusting it.
1204    #[test]
1205    fn the_named_compare_conditions_fall_out_of_the_bit_encoding() {
1206        // cond, name, expected for [1<2, 2==2, 3>2, NaN]
1207        let cases: &[(u8, &str, [bool; 4])] = &[
1208            (0, "F", [false, false, false, false]),
1209            (1, "UN", [false, false, false, true]),
1210            (2, "EQ", [false, true, false, false]),
1211            (3, "UEQ", [false, true, false, true]),
1212            (4, "OLT", [true, false, false, false]),
1213            (5, "ULT", [true, false, false, true]),
1214            (6, "OLE", [true, true, false, false]),
1215            (7, "ULE", [true, true, false, true]),
1216        ];
1217        for &(cond, name, want) in cases {
1218            let got = [
1219                compare_s(1.0, 2.0, cond).value,
1220                compare_s(2.0, 2.0, cond).value,
1221                compare_s(3.0, 2.0, cond).value,
1222                compare_s(f32::NAN, 2.0, cond).value,
1223            ];
1224            assert_eq!(got, want, "C.{name} (cond {cond})");
1225        }
1226    }
1227
1228    /// **The signaling forms raise Invalid on any unordered compare**, even for
1229    /// a *quiet* NaN. That is the entire difference between `C.EQ` and `C.SEQ`,
1230    /// and it means the quiet/signaling test used elsewhere is not sufficient
1231    /// on its own here.
1232    #[test]
1233    fn the_signaling_compare_forms_raise_on_a_quiet_nan() {
1234        // Quiet **by the VR4300 convention**: significand MSB clear (C-12).
1235        let qnan = f32::from_bits(0x7F80_0001);
1236        assert!(!is_snan_f32(qnan), "it really is quiet");
1237
1238        let out = compare_s(qnan, 1.0, 2); // C.EQ
1239        assert!(!out.value);
1240        assert!(!out.flags.invalid, "the non-signaling form stays quiet");
1241
1242        let out = compare_s(qnan, 1.0, 10); // C.SEQ
1243        assert!(!out.value, "the comparison result is unchanged");
1244        assert!(out.flags.invalid, "only the exception differs");
1245    }
1246
1247    /// A **signaling** NaN raises Invalid whatever the condition, including the
1248    /// non-signaling forms.
1249    #[test]
1250    fn a_signaling_nan_operand_raises_for_every_condition() {
1251        // Signaling **by the VR4300 convention**: significand MSB set (C-12).
1252        let snan = f32::from_bits(0x7FC0_0001);
1253        for cond in 0..16u8 {
1254            assert!(
1255                compare_s(snan, 1.0, cond).flags.invalid,
1256                "cond {cond} must raise on a signaling NaN"
1257            );
1258        }
1259    }
1260
1261    /// `Greater` appears in **no** condition bit — it is "none of less, equal or
1262    /// unordered", which is why three bits suffice for the relation.
1263    #[test]
1264    fn greater_matches_no_condition_bit() {
1265        for cond in 0..8u8 {
1266            assert!(
1267                !compare_s(3.0, 2.0, cond).value,
1268                "cond {cond}: greater matches no bit"
1269            );
1270        }
1271    }
1272
1273    /// Doubles use the same derivation, and `-0.0 == 0.0` as IEEE requires.
1274    #[test]
1275    fn double_compares_agree_and_signed_zeros_are_equal() {
1276        assert!(compare_d(1.0, 2.0, 4).value, "C.OLT");
1277        assert!(compare_d(2.0, 2.0, 2).value, "C.EQ");
1278        assert!(compare_d(-0.0, 0.0, 2).value, "-0.0 == 0.0 (IEEE)");
1279        assert!(compare_d(f64::NAN, 1.0, 1).value, "C.UN");
1280        assert!(
1281            !compare_d(f64::NAN, 1.0, 2).value,
1282            "C.EQ is false when unordered"
1283        );
1284    }
1285
1286    /// **The VR4300 long-conversion restriction** (UM §7.5.2): bits 63:55 must
1287    /// be all-zero or all-one. This is a *hardware limitation*, not IEEE
1288    /// behavior — the value is representable, the processor declines.
1289    ///
1290    /// Converting it anyway produces a correct number where hardware traps, so
1291    /// software's fixup path never runs and the divergence surfaces far
1292    /// downstream from its cause.
1293    #[test]
1294    fn cvt_from_long_rejects_values_outside_the_vr4300_range() {
1295        // Small positives and negatives: bits 63:55 uniform.
1296        for v in [0i64, 1, -1, 1 << 40, -(1 << 40), (1 << 55) - 1] {
1297            assert!(long_convertible(v), "{v} must be convertible");
1298        }
1299        // Bits 63:55 mixed -- declines.
1300        for v in [1i64 << 55, 1 << 60, i64::MAX, i64::MIN + 1] {
1301            assert!(!long_convertible(v), "{v} must be rejected");
1302        }
1303        // i64::MIN is 0x8000_0000_0000_0000, so bits 63:55 are 0b1_0000_0000 --
1304        // neither all-zero nor all-one, so it is NOT convertible. Easy to
1305        // assume otherwise from "the sign bit is set".
1306        assert!(!long_convertible(i64::MIN));
1307        // The largest magnitudes that ARE convertible sit at the 2^55 boundary.
1308        assert!(long_convertible((1i64 << 55) - 1));
1309        assert!(long_convertible(-(1i64 << 55)));
1310    }
1311
1312    /// `Unimplemented` is **not** `Invalid`: it means "this processor cannot do
1313    /// this", not "the operation is undefined". Conflating them sends the
1314    /// handler down the numerical-error path for a hardware limitation.
1315    #[test]
1316    fn unimplemented_is_a_different_cause_bit_from_invalid() {
1317        assert_eq!(CAUSE_UNIMPLEMENTED, 1 << 17);
1318        assert_ne!(
1319            CAUSE_UNIMPLEMENTED,
1320            Flags::INVALID.to_fcsr_bits() & 0x0003_F000,
1321            "distinct from the Invalid cause bit"
1322        );
1323    }
1324
1325    /// Integer-to-float rounding lives in `softfloat::from_int`, not here.
1326    ///
1327    /// This module used to carry `cvt_s_w`/`cvt_s_l`/`cvt_d_w`/`cvt_d_l`, each a
1328    /// Rust `as` cast plus a round-trip inexact check. They were **deleted**
1329    /// rather than left unused once the pipeline moved to `softfloat::from_int`:
1330    /// an `as` cast rounds to nearest-even unconditionally, so every one of them
1331    /// ignored `FCSR.RM`, and an unused function that quietly gets an operation
1332    /// wrong is the inert-API hazard `docs/engineering-lessons.md` §3.2
1333    /// describes. `addr.rs` deleted a stale `translate` for the same reason.
1334    ///
1335    /// [`long_convertible`] stays: it is the VR4300 range restriction, which is
1336    /// a separate rule from the rounding and is still consulted.
1337    #[test]
1338    fn int_to_single_rounding_is_not_in_this_module() {
1339        // A value needing more than 24 significand bits, converted toward zero.
1340        // Nearest-even would give 0x4E93_2C06; the mode must be honored.
1341        let r =
1342            crate::softfloat::from_int(1_234_567_891, crate::softfloat::F32, Rounding::TowardZero);
1343        assert_eq!(r.bits as u32, 0x4E93_2C05);
1344        assert!(r.flags.inexact);
1345    }
1346
1347    /// `CVT.S.D` can overflow; `CVT.D.S` never can, since every `f32` is an
1348    /// `f64`.
1349    #[test]
1350    fn narrowing_can_overflow_but_widening_cannot() {
1351        let out = cvt_s_d(1e300);
1352        assert!(out.flags.overflow, "1e300 has no f32 representation");
1353        assert!(out.flags.inexact);
1354        assert!(!cvt_s_d(1.0).flags.overflow);
1355        assert_eq!(cvt_d_s(1.5).value.to_bits(), 1.5f64.to_bits());
1356        assert_eq!(cvt_d_s(f32::MAX).flags, Flags::NONE, "widening is exact");
1357    }
1358
1359    /// The four rounding modes differ, and `Nearest` is **ties-to-even** — not
1360    /// `f64::round`, which rounds half away from zero and matches no MIPS mode.
1361    #[test]
1362    fn the_rounding_modes_differ_and_nearest_is_ties_to_even() {
1363        assert_eq!(round_f64(2.5, Rounding::Nearest), 2.0, "ties to EVEN");
1364        assert_eq!(round_f64(3.5, Rounding::Nearest), 4.0);
1365        assert_eq!(round_f64(2.5, Rounding::TowardZero), 2.0);
1366        assert_eq!(round_f64(2.5, Rounding::TowardPlusInf), 3.0);
1367        assert_eq!(round_f64(2.5, Rounding::TowardMinusInf), 2.0);
1368        assert_eq!(round_f64(-2.5, Rounding::TowardZero), -2.0);
1369        assert_eq!(round_f64(-2.5, Rounding::TowardMinusInf), -3.0);
1370    }
1371
1372    /// Out-of-range and NaN conversions raise Invalid rather than wrapping.
1373    #[test]
1374    fn an_out_of_range_conversion_raises_invalid() {
1375        for v in [1e30f64, -1e30, f64::NAN, f64::INFINITY] {
1376            let out = to_i32(v, Rounding::Nearest);
1377            assert!(out.flags.invalid, "{v} does not fit in an i32");
1378            assert_eq!(out.value, i32::MAX, "the conventional MIPS result");
1379        }
1380        assert_eq!(to_i32(42.0, Rounding::Nearest).value, 42);
1381        assert_eq!(to_i32(42.0, Rounding::Nearest).flags, Flags::NONE);
1382        assert!(to_i32(42.5, Rounding::TowardZero).flags.inexact);
1383    }
1384
1385    /// The 64-bit bound is compared as an `f64` because `i64::MAX` is **not
1386    /// exactly representable** — `r > i64::MAX as f64` is the correct test, and
1387    /// casting first is not.
1388    #[test]
1389    fn the_64_bit_conversion_bound_accounts_for_representability() {
1390        // 2^63 exactly: out of range, since i64::MAX is 2^63 - 1.
1391        let out = to_i64(9_223_372_036_854_775_808.0, Rounding::Nearest);
1392        assert!(out.flags.invalid, "2^63 does not fit in an i64");
1393        // Just inside.
1394        let out = to_i64(9_223_372_036_854_774_784.0, Rounding::Nearest);
1395        assert!(!out.flags.invalid);
1396        assert_eq!(to_i64(-1.0, Rounding::Nearest).value, -1);
1397    }
1398
1399    /// The FP multiplication erratum is **detectable but not reproducible**:
1400    /// the trigger is documented, the corrupted output never was.
1401    ///
1402    /// Selecting the affected stepping therefore changes no arithmetic. That is
1403    /// deliberate — inventing a plausible wrong value would be the
1404    /// fitted-constant failure the accuracy ledger forbids, and every later
1405    /// result built on it would stop being evidence.
1406    /// **UM Table 7-14, asserted row by row.** These are documented numbers with
1407    /// no generating rule, so the test is the transcription check.
1408    #[test]
1409    fn the_fpu_delay_table_matches_the_manual() {
1410        // ADD, SUB: 3 in both formats.
1411        for funct in [0o00, 0o01] {
1412            assert_eq!(delay_cycles(funct, FMT_S), 3);
1413            assert_eq!(delay_cycles(funct, FMT_D), 3);
1414        }
1415        assert_eq!(delay_cycles(0o02, FMT_S), 5, "MUL.S");
1416        assert_eq!(delay_cycles(0o02, FMT_D), 8, "MUL.D");
1417        for funct in [0o03, 0o04] {
1418            assert_eq!(delay_cycles(funct, FMT_S), 29, "DIV/SQRT.S");
1419            assert_eq!(delay_cycles(funct, FMT_D), 58, "DIV/SQRT.D");
1420        }
1421        // ABS, MOV, NEG and C.cond are single-cycle, so they add no stall.
1422        for funct in [0o05, 0o06, 0o07, 0o60, 0o62, 0o74] {
1423            assert_eq!(delay_cycles(funct, FMT_S), 1);
1424            assert_eq!(stall_cycles(funct, FMT_S), 0, "no stall for a 1-cycle op");
1425        }
1426        // The eight ROUND/TRUNC/CEIL/FLOOR forms.
1427        for funct in 0o10..=0o17 {
1428            assert_eq!(delay_cycles(funct, FMT_S), 5);
1429            assert_eq!(delay_cycles(funct, FMT_D), 5);
1430        }
1431        // The CVT rows are the only ones that differ by SOURCE format, which is
1432        // the part a uniform table would get wrong.
1433        assert_eq!(delay_cycles(0o40, FMT_D), 2, "CVT.S from double");
1434        assert_eq!(delay_cycles(0o40, FMT_W), 5, "CVT.S from word");
1435        assert_eq!(delay_cycles(0o40, FMT_L), 5, "CVT.S from long");
1436        assert_eq!(delay_cycles(0o41, FMT_S), 1, "CVT.D from single");
1437        assert_eq!(delay_cycles(0o41, FMT_W), 5, "CVT.D from word");
1438        assert_eq!(delay_cycles(0o41, FMT_L), 5, "CVT.D from long");
1439        assert_eq!(delay_cycles(0o44, FMT_S), 5, "CVT.W");
1440        assert_eq!(delay_cycles(0o45, FMT_D), 5, "CVT.L");
1441    }
1442
1443    /// A rate of 1 is an ordinary instruction, not a one-cycle penalty.
1444    #[test]
1445    fn single_cycle_fpu_ops_are_charged_nothing() {
1446        assert_eq!(stall_cycles(0o06, FMT_S), 0, "MOV.S");
1447        assert_eq!(stall_cycles(0o41, FMT_S), 0, "CVT.D.S is one cycle");
1448        assert_eq!(stall_cycles(0o02, FMT_D), 8, "but MUL.D is not");
1449    }
1450
1451    #[test]
1452    fn the_multiplication_erratum_is_modeled_but_not_invented() {
1453        assert!(!Stepping::default().has_mul_erratum(), "fixed by default");
1454        assert!(Stepping::Early.has_mul_erratum());
1455
1456        // Selecting the affected stepping changes nothing about a multiply,
1457        // because there is nothing documented to change it to.
1458        let a = mul_s(3.0, 4.0, Rounding::Nearest);
1459        assert_eq!(a.value, 12.0, "arithmetic is stepping-independent today");
1460        assert_eq!(a.flags, Flags::NONE);
1461    }
1462
1463    /// The trigger is a property of the **previous** multiply's operands: a NaN,
1464    /// zero or infinity. It is tested now so a future characterization has
1465    /// somewhere correct to attach the output.
1466    #[test]
1467    fn the_erratum_trigger_keys_off_the_previous_multiplys_operands() {
1468        for (a, b) in [
1469            (f64::NAN, 1.0),
1470            (0.0, 1.0),
1471            (1.0, -0.0),
1472            (f64::INFINITY, 1.0),
1473            (1.0, f64::NEG_INFINITY),
1474        ] {
1475            assert!(mul_erratum_triggers(a, b), "{a} * {b} arms the erratum");
1476        }
1477        assert!(!mul_erratum_triggers(2.0, 3.0), "ordinary operands do not");
1478        assert!(!mul_erratum_triggers(-1.5, 1e30));
1479    }
1480}