pub struct Fpr { /* private fields */ }Expand description
The 32 physical floating-point general registers.
Implementations§
Source§impl Fpr
impl Fpr
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Power-on state.
The manual does not define one; ADR 0004 requires reproducibility, so it is a documented zero.
Sourcepub const fn read_s(&self, n: u8, fr: bool) -> u32
pub const fn read_s(&self, n: u8, fr: bool) -> u32
Read a 32-bit value from FPR n under the current FR view.
FR | Register n maps to |
|---|---|
| 1 | the low half of FGR n |
| 0 | FGR n & !1 — its low half for even n, its HIGH half for odd n |
The FR = 0 row is the whole subtlety, and it is not what “the pair
FGR[n+1]:FGR[n]” suggests: an odd register in half mode is the
upper 32 bits of its even partner, and odd FGRs are not addressable
at all. n64-systemtest pins it directly — after MTC1 $1 in half mode,
DMFC1(0) shows the written value in its high half while DMFC1(1) is
unchanged.
Sourcepub const fn write_s(&mut self, n: u8, fr: bool, v: u32)
pub const fn write_s(&mut self, n: u8, fr: bool, v: u32)
Write a 32-bit value to FPR n under the current FR view.
The other half of the 64-bit register is preserved — this models
MTC1/LWC1, which deposit 32 bits and leave the rest alone. An
arithmetic .S result does not behave this way; see
Fpr::write_s_arith.
See Fpr::read_s for the FR = 0 mapping.
Sourcepub const fn write_s_arith(&mut self, n: u8, _fr: bool, v: u32)
pub const fn write_s_arith(&mut self, n: u8, _fr: bool, v: u32)
Write a single-precision arithmetic result, which clears the other half of the destination rather than preserving it.
This is what separates an arithmetic write-back from MTC1.
n64-systemtest’s “Upper bits of 32 bit operation” reads the
destination back with DMFC1 after an ADD.S and expects the upper
32 bits to be zero, not the register’s previous contents.
The destination index is used as-is in both FR modes — unlike
Fpr::write_s, which under FR = 0 reaches an even partner. ADD.S $1
in half mode leaves its result in FGR1, upper half cleared, which is what
the suite reads back.
Sourcepub const fn read_s_fs(&self, n: u8, fr: bool) -> u32
pub const fn read_s_fs(&self, n: u8, fr: bool) -> u32
Read the fs operand of a floating-point arithmetic instruction.
Under FR = 0 an odd index reads its even partner’s value — the low
bit of the field is simply dropped. This is not the same mapping as
Fpr::read_s, which models MTC1/LWC1 and reaches the partner’s
high half. Two different mappings for two different instruction
classes is surprising, and it is what the hardware does.
§Why this is measured rather than looked up
The manual declines to specify it: “If the FR bit is 0, an odd-numbered register cannot be specified”, and for the arithmetic instructions “If an odd number is specified, the operation is undefined” (UM §7.5.3, §16). Undefined in the manual is still deterministic in silicon, and n64-systemtest measures it — so the ROM’s table is the oracle here, and the accuracy ledger records it as such rather than as documentation.
Sourcepub const fn read_s_ft(&self, n: u8, _fr: bool) -> u32
pub const fn read_s_ft(&self, n: u8, _fr: bool) -> u32
Read the ft operand of a floating-point arithmetic instruction.
Unlike Fpr::read_s_fs, the index is used as-is in both FR
modes: an odd ft reads the odd FGR’s own low half.
The asymmetry is the whole point of having two accessors. It is pinned by
a pair of rows that disagree under any single rule: with FR = 0,
SQRT.S $13, $31 yields sqrt(16) — so fs = 31 read FGR30 — while
ADD.S $2, $28, $31 yields -10 + -16 — so ft = 31 read FGR31. One
shared mapping cannot satisfy both.
Sourcepub const fn read_d_fs(&self, n: u8, fr: bool) -> u64
pub const fn read_d_fs(&self, n: u8, fr: bool) -> u64
Read the fs operand of a double-precision arithmetic instruction.
Sourcepub const fn read_d_ft(&self, n: u8, _fr: bool) -> u64
pub const fn read_d_ft(&self, n: u8, _fr: bool) -> u64
Read the ft operand of a double-precision arithmetic instruction.
Sourcepub const fn write_d_arith(&mut self, n: u8, _fr: bool, v: u64)
pub const fn write_d_arith(&mut self, n: u8, _fr: bool, v: u64)
Write a double-precision arithmetic result.
Like Fpr::write_s_arith, the destination index is used as-is in both
modes. ADD.D $1 under FR = 0 leaves the result in FGR1, which
n64-systemtest checks by observing that FGR1 does not keep its
preloaded value.
Sourcepub const fn read_d(&self, n: u8, fr: bool) -> u64
pub const fn read_d(&self, n: u8, fr: bool) -> u64
Read a 64-bit value from FPR n under the current FR view.
With FR = 0 the register number is forced even and the whole
64-bit FGR is the value — not an assembly of two FGRs’ low halves,
which is the shape this originally had and which disagreed with
hardware on every odd index.
Sourcepub const fn write_d(&mut self, n: u8, fr: bool, v: u64)
pub const fn write_d(&mut self, n: u8, fr: bool, v: u64)
Write a 64-bit value to FPR n under the current FR view.
Sourcepub const fn read_raw(&self, n: u8) -> u64
pub const fn read_raw(&self, n: u8) -> u64
Read a raw FGR, ignoring FR.
Not for any instruction. DMFC1 looked like a user of this and is
not: it is a formatted 64-bit access and goes through Fpr::read_d
(accuracy ledger U-7). This exists for tests and for save-state
serialization, which want the physical file.
Sourcepub const fn write_raw(&mut self, n: u8, v: u64)
pub const fn write_raw(&mut self, n: u8, v: u64)
Write a raw FGR, ignoring FR. See Fpr::read_raw.