Skip to main content

rustyn64_cpu/
cop0.rs

1//! COP0 — the VR4300 system control coprocessor (T-12-001).
2//!
3//! The register file only: widths, writable-bit masks, and the four access
4//! instructions. The *behavior* the registers drive — exception dispatch
5//! (T-12-002), interrupts (T-12-003), address translation (T-12-004) — reads
6//! this module rather than living in it.
7//!
8//! # Why this is table-driven
9//!
10//! Almost every accuracy rule here is of the form "register N is 64 bits wide"
11//! or "bits 23:16 of `Config` are hardwired to `0b00000110`". Those are *data*.
12//! Written as data they can be asserted directly against the manual, one test
13//! per table; written as `match` arms they become 32 places to forget one.
14//!
15//! Two rules in particular cannot be derived and must simply be right:
16//!
17//! - **Which eight registers are 64 bits wide** ([`WIDE`]). There is no pattern.
18//!   Getting one wrong is invisible until 64-bit software runs, and then
19//!   presents as a truncated address rather than as a register bug.
20//! - **Which bits accept writes** ([`WRITE_MASK`]). Hardware silently discards
21//!   the rest; software reads back what it wrote *minus those bits*, and a
22//!   too-permissive mask makes an emulator pass tests real hardware fails.
23//!
24//! # Reset state
25//!
26//! UM §6.4.4 (p. 183) defines only a handful of fields at cold reset and calls
27//! the rest **undefined**: `Index`, `EntryHi`/`EntryLo*`/`PageMask`, `LLAddr`,
28//! `TagLo`/`TagHi`, `WatchLo`/`WatchHi`, and most of `Status`. Undefined is not
29//! license to be non-deterministic — ADR 0004 requires a reproducible machine —
30//! so undefined fields are a documented zero, not entropy.
31
32use crate::decode::Decoded;
33use serde::{Deserialize, Serialize};
34
35/// COP0 register numbers, by name.
36///
37/// Named constants rather than an enum: the encoding carries a raw 5-bit field
38/// and every value 0..=31 is expressible, so an enum would need a fallible
39/// conversion on the hot path for no benefit.
40pub mod reg {
41    /// TLB entry index for `TLBR`/`TLBWI`.
42    pub const INDEX: u8 = 0;
43    /// Free-running TLB replacement counter.
44    pub const RANDOM: u8 = 1;
45    /// Even-page TLB entry.
46    pub const ENTRY_LO0: u8 = 2;
47    /// Odd-page TLB entry.
48    pub const ENTRY_LO1: u8 = 3;
49    /// Page-table base plus the faulting VPN2 (32-bit refill).
50    pub const CONTEXT: u8 = 4;
51    /// TLB page-size mask.
52    pub const PAGE_MASK: u8 = 5;
53    /// Number of wired (unreplaceable) TLB entries.
54    pub const WIRED: u8 = 6;
55    /// Faulting virtual address.
56    pub const BAD_VADDR: u8 = 8;
57    /// Free-running cycle counter, incrementing at half `PClock`.
58    pub const COUNT: u8 = 9;
59    /// TLB VPN2 + ASID.
60    pub const ENTRY_HI: u8 = 10;
61    /// Timer-interrupt comparand.
62    pub const COMPARE: u8 = 11;
63    /// Processor status and mode.
64    pub const STATUS: u8 = 12;
65    /// Exception cause.
66    pub const CAUSE: u8 = 13;
67    /// Exception program counter.
68    pub const EPC: u8 = 14;
69    /// Processor revision identifier.
70    pub const PRID: u8 = 15;
71    /// Cache and endianness configuration.
72    pub const CONFIG: u8 = 16;
73    /// Physical address of the last `LL` (diagnostic only).
74    pub const LL_ADDR: u8 = 17;
75    /// Watchpoint address, low.
76    pub const WATCH_LO: u8 = 18;
77    /// Watchpoint address, high.
78    pub const WATCH_HI: u8 = 19;
79    /// Page-table base plus the faulting VPN2 (64-bit refill).
80    pub const XCONTEXT: u8 = 20;
81    /// Parity error (VR4200 compatibility; unused by VR4300 hardware).
82    pub const PERR: u8 = 26;
83    /// Cache error (VR4200 compatibility; unused by VR4300 hardware).
84    pub const CACHE_ERR: u8 = 27;
85    /// Primary cache tag, low.
86    pub const TAG_LO: u8 = 28;
87    /// Primary cache tag, high.
88    pub const TAG_HI: u8 = 29;
89    /// Error exception program counter.
90    pub const ERROR_EPC: u8 = 30;
91}
92
93/// The eight registers that are **64 bits wide**; the other 24 are 32-bit.
94///
95/// A bitmask indexed by register number. From UM Table 1-2 (p. 46) and the
96/// individual register figures in §5.4 and §6.3:
97/// `EntryLo0`, `EntryLo1`, `Context`, `BadVAddr`, `EntryHi`, `EPC`, `XContext`,
98/// `ErrorEPC`.
99///
100/// There is no rule generating this list — it is exactly the registers that hold
101/// an address or a TLB entry. Pinned by `the_eight_wide_registers_are_exactly_these`.
102pub const WIDE: u32 = (1 << reg::ENTRY_LO0)
103    | (1 << reg::ENTRY_LO1)
104    | (1 << reg::CONTEXT)
105    | (1 << reg::BAD_VADDR)
106    | (1 << reg::ENTRY_HI)
107    | (1 << reg::EPC)
108    | (1 << reg::XCONTEXT)
109    | (1 << reg::ERROR_EPC);
110
111/// Is register `n` 64 bits wide?
112#[must_use]
113pub const fn is_wide(n: u8) -> bool {
114    WIDE & (1 << (n & 31)) != 0
115}
116
117/// `Config` bits 23:16, hardwired to `0b00000110` (UM Fig. 5-16, p. 152).
118const CONFIG_HARDWIRED_HI: u64 = 0b0000_0110 << 16;
119/// `Config` bits 14:4, hardwired to `0b110_0100_0110` (UM Fig. 5-16, p. 152).
120const CONFIG_HARDWIRED_LO: u64 = 0b110_0100_0110 << 4;
121
122/// Per-register writable-bit masks. A `0` bit is hardwired, reserved, or
123/// hardware-owned, and a write to it is discarded.
124///
125/// Every entry is a direct transcription of a register figure. Where a register
126/// is entirely read-only the mask is `0`, which makes "read-only" the same
127/// mechanism as "reserved bit" rather than a separate code path.
128pub const WRITE_MASK: [u64; 32] = {
129    let mut m = [0u64; 32];
130    // Index: P (31) + Index (5:0). Bit 5 is writable but ignored by a 32-entry
131    // TLB -- the manual keeps it, so we keep it (UM Fig. 5-11, p. 146).
132    m[reg::INDEX as usize] = 0x8000_003F;
133    // Random is READ-ONLY (UM §5.4.2, p. 147).
134    m[reg::RANDOM as usize] = 0;
135    // EntryLo0/1: PFN (25:6) | C (5:3) | D (2) | V (1) | G (0) accounts for
136    // bits 25:0 — but the register is writable up to bit **29**, and the top
137    // four bits read back exactly as written.
138    //
139    // Measured, not derived: n64-systemtest writes `0x0F000000` and expects it
140    // back verbatim, and its expectation for every value is `v & 0x3FFF_FFFF`
141    // (`tests/tlb/mod.rs`). Masking at the architectural field width instead
142    // silently dropped bits 29:26 on write-back.
143    m[reg::ENTRY_LO0 as usize] = 0x3FFF_FFFF;
144    m[reg::ENTRY_LO1 as usize] = 0x3FFF_FFFF;
145    // Context: only PTEBase (63:23) is software-writable; BadVPN2 (22:4) is
146    // written by hardware on a TLB exception and 3:0 are always zero.
147    m[reg::CONTEXT as usize] = 0xFFFF_FFFF_FF80_0000;
148    // PageMask: MASK (24:13).
149    m[reg::PAGE_MASK as usize] = 0x01FF_E000;
150    m[reg::WIRED as usize] = 0x3F;
151    // BadVAddr is READ-ONLY (UM §6.3.2, p. 164).
152    m[reg::BAD_VADDR as usize] = 0;
153    m[reg::COUNT as usize] = 0xFFFF_FFFF;
154    // EntryHi: R (63:62) | VPN2 (39:13) | ASID (7:0). Fill (61:40) is
155    // write-ignored and reads zero (UM Fig. 5-10, p. 144).
156    m[reg::ENTRY_HI as usize] = 0xC000_00FF_FFFF_E0FF;
157    m[reg::COMPARE as usize] = 0xFFFF_FFFF;
158    // Status: everything except DS.TS (21), which is read-only, and bits 23 and
159    // 19, which are hardwired zero (UM Fig. 6-6, p. 167).
160    m[reg::STATUS as usize] = 0xFFFF_FFFF & !(1 << 21) & !(1 << 23) & !(1 << 19);
161    // Cause: READ-ONLY except the two software interrupt bits IP1:IP0 (9:8)
162    // (UM §6.3.6, p. 171). This is the mask that is most tempting to widen.
163    m[reg::CAUSE as usize] = 0x0000_0300;
164    m[reg::EPC as usize] = u64::MAX;
165    // PRId is READ-ONLY.
166    m[reg::PRID as usize] = 0;
167    // Config: EP (27:24) | BE (15) | CU (3) | K0 (2:0). EC (30:28) is read-only,
168    // sampled from the `DivMode` pins.
169    m[reg::CONFIG as usize] = 0x0F00_0000 | (1 << 15) | (1 << 3) | 0b111;
170    m[reg::LL_ADDR as usize] = 0xFFFF_FFFF;
171    // WatchLo: PAddr0 (31:3) | R (1) | W (0). Bit 2 is zero.
172    m[reg::WATCH_LO as usize] = 0xFFFF_FFFB;
173    // WatchHi: PAddr1 (3:0). Readable, but "the value in this area is invalid"
174    // on the VR4300, whose physical addresses are only 32 bits.
175    m[reg::WATCH_HI as usize] = 0xF;
176    // XContext: only PTEBase (63:33). R (32:31) and BadVPN2 (30:4) are
177    // hardware-written.
178    m[reg::XCONTEXT as usize] = 0xFFFF_FFFE_0000_0000;
179    // PErr: Diagnostic (7:0). Defined for VR4200 compatibility; the VR4300's
180    // hardware never uses it.
181    m[reg::PERR as usize] = 0xFF;
182    // CacheErr is READ-ONLY and reads as zero -- same compatibility story.
183    m[reg::CACHE_ERR as usize] = 0;
184    // TagLo: PTagLo (27:8) | PState (7:6).
185    m[reg::TAG_LO as usize] = 0x0FFF_FFC0;
186    // TagHi is 32 bits of reserved zero.
187    m[reg::TAG_HI as usize] = 0;
188    m[reg::ERROR_EPC as usize] = u64::MAX;
189    // Registers 7, 21..=25 and 31 are "Reserved for future use" (UM Table 1-2,
190    // p. 46) and stay 0 -- see `Cop0::write` for why that is a guess, not a fact.
191    m
192};
193
194/// Per-register **architecturally-defined** bits — everything a read can ever
195/// return non-zero.
196///
197/// Distinct from [`WRITE_MASK`], and the difference is the point: a read-only
198/// register like `BadVAddr` has a write mask of `0` and an arch mask of all-ones,
199/// while `Cause` has a two-bit write mask and a wide arch mask. Reserved bits and
200/// bits above a 32-bit register's width appear in neither.
201///
202/// Applied on **both** read and [`Cop0::set_hardware`], so a value that is not
203/// architecturally representable cannot enter the file in the first place, let
204/// alone leave it. Enforcing only on read would leave the stored state carrying
205/// bits no hardware register can hold, which the next reader of `regs` would
206/// have to know about.
207///
208/// `Config` is absent from this table: its readable value is *composed* rather
209/// than masked, and [`Cop0::read`] handles it separately.
210pub const ARCH_MASK: [u64; 32] = {
211    let mut m = [0u64; 32];
212    m[reg::INDEX as usize] = 0x8000_003F;
213    m[reg::RANDOM as usize] = 0x3F;
214    m[reg::ENTRY_LO0 as usize] = 0x3FFF_FFFF;
215    m[reg::ENTRY_LO1 as usize] = 0x3FFF_FFFF;
216    // PTEBase (63:23) | BadVPN2 (22:4); bits 3:0 are always zero.
217    m[reg::CONTEXT as usize] = 0xFFFF_FFFF_FFFF_FFF0;
218    m[reg::PAGE_MASK as usize] = 0x01FF_E000;
219    m[reg::WIRED as usize] = 0x3F;
220    // A full 64-bit virtual address.
221    m[reg::BAD_VADDR as usize] = u64::MAX;
222    m[reg::COUNT as usize] = 0xFFFF_FFFF;
223    // R (63:62) | VPN2 (39:13) | ASID (7:0). Fill (61:40) reads ZERO, which is
224    // why it is absent here rather than merely unwritable.
225    m[reg::ENTRY_HI as usize] = 0xC000_00FF_FFFF_E0FF;
226    m[reg::COMPARE as usize] = 0xFFFF_FFFF;
227    m[reg::STATUS as usize] = 0xFFFF_FFFF & !(1 << 23) & !(1 << 19);
228    // BD (31) | CE (29:28) | IP (15:8) | ExcCode (6:2). Far wider than the
229    // two-bit WRITE_MASK, because hardware writes most of it.
230    m[reg::CAUSE as usize] = 0xB000_FF7C;
231    m[reg::EPC as usize] = u64::MAX;
232    // Imp (15:8) | Rev (7:0); bits 31:16 read zero.
233    m[reg::PRID as usize] = 0xFFFF;
234    m[reg::LL_ADDR as usize] = 0xFFFF_FFFF;
235    m[reg::WATCH_LO as usize] = 0xFFFF_FFFB;
236    m[reg::WATCH_HI as usize] = 0xF;
237    // PTEBase (63:33) | R (32:31) | BadVPN2 (30:4); bits 3:0 always zero.
238    m[reg::XCONTEXT as usize] = 0xFFFF_FFFF_FFFF_FFF0;
239    // Config's READ value is composed rather than masked (see `Cop0::read`), but
240    // it still needs an entry: without one, `set_hardware` would mask it to zero.
241    // These are its non-hardwired bits: EP (27:24) | BE (15) | CU (3) | K0 (2:0).
242    m[reg::CONFIG as usize] = 0x0F00_800F;
243    m[reg::PERR as usize] = 0xFF;
244    // CacheErr, TagHi and the reserved registers read as zero.
245    m[reg::TAG_LO as usize] = 0x0FFF_FFC0;
246    m[reg::ERROR_EPC as usize] = u64::MAX;
247    m
248};
249
250/// The VR4300 system control coprocessor register file.
251#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
252pub struct Cop0 {
253    /// Raw register storage. Prefer [`Cop0::read`] / [`Cop0::write`]: they apply
254    /// the width and writable-bit rules that make the values architectural.
255    ///
256    /// `Config`'s hardwired fields are **not** stored here — they are merged in
257    /// on read, so no write path can erase them.
258    regs: [u64; 32],
259    /// The current `Count` timeline position, supplied by the scheduler.
260    ///
261    /// ADR 0006: `master_ticks` is the only incremented counter, and every other
262    /// cycle position is **derived** from it. `Count` is therefore not stored
263    /// and not incremented here — it is computed from this timeline plus an
264    /// epoch, so that a `+= 1` on `Count` never exists to drift.
265    now: u64,
266    /// `Count`'s value at its epoch, set by the last `MTC0 Count`.
267    count_epoch_value: u32,
268    /// The timeline position at that epoch.
269    ///
270    /// Together these make `Count` **affine**: guest-writable (so it needs an
271    /// offset) while still derived (so it cannot drift from the master clock).
272    count_epoch_tick: u64,
273    /// The `Count` value seen at the previous [`Cop0::timer_edge`] poll, so the
274    /// timer can fire on the transition into `Count == Compare` rather than
275    /// whenever the equality happens to hold. See `timer_edge`.
276    last_count: u32,
277
278    /// `Config.EC`, the PClock:MasterClock ratio, sampled from the `DivMode` pins
279    /// at reset and read-only thereafter.
280    ///
281    /// Held apart from `regs` precisely because it is not writable: keeping it
282    /// in the array would need a mask exception that a later edit could relax.
283    ec: u8,
284
285    /// The value of the most recent write to **any** COP0 register.
286    ///
287    /// The seven unused register numbers (7, 21..=25, 31) are not storage: they
288    /// read back this shared latch. So writing reg 7 and reading it returns
289    /// what was written — until *any other* COP0 write intervenes, after which
290    /// reg 7 reads that value instead. See [`Cop0::UNUSED`].
291    write_latch: u64,
292}
293
294impl Default for Cop0 {
295    fn default() -> Self {
296        Self::new()
297    }
298}
299
300impl Cop0 {
301    /// Cold-reset state (UM §6.4.4, p. 183; Fig. 6-16, p. 205).
302    ///
303    /// Defined by the manual: `Status.ERL` and `Status.BEV` set, `Status.TS`/
304    /// `SR`/`RP` clear, `Config.BE` set, `Config.EP` clear, `Random` = 31,
305    /// `Wired` = 0. Everything else the manual calls **undefined**, and is a
306    /// deterministic zero here (ADR 0004).
307    #[must_use]
308    pub const fn new() -> Self {
309        let mut regs = [0u64; 32];
310        // ERL (2) and BEV (22) set; TS/SR/RP already clear.
311        regs[reg::STATUS as usize] = (1 << 2) | (1 << 22);
312        // "Random is set to 31 on Cold Reset" (UM §5.4.2, p. 147).
313        regs[reg::RANDOM as usize] = 31;
314        // BE (15) set: the N64 is big-endian. K0 defaults to 0 (uncached); IPL3
315        // writes 3 during boot, which is where 0x0006E463 comes from.
316        regs[reg::CONFIG as usize] = 1 << 15;
317        // Imp = 0x0B for the VR4300 series (UM §5.4.5, p. 151), Rev = 0x22.
318        //
319        // This comment previously said the Rev field was "NOT documented for any
320        // specific part" and left it zero. That was true of the User's Manual and
321        // false of the N64brew wiki, which this project mirrors and treats as a
322        // primary hardware reference: *"retail N64 units have so far been found
323        // to report either 0x10 (1.0, early units) or 0x22 (2.2, later units),
324        // and the iQue Player reports 0x40"* (`n64brew_wiki/markdown/VR4300.md`).
325        // Ledger U-3 is superseded by C-22 -- "undocumented" is a claim about a
326        // document, and it decays.
327        //
328        // 0x22 is the later stepping, which is what `fpu::Stepping::Fixed` (the
329        // default) denotes. The two want to be selected together by a future
330        // console-revision constructor; wiring that now, with nothing able to
331        // choose `Early`, would be inert API.
332        regs[reg::PRID as usize] = 0x0B22;
333        Self {
334            regs,
335            now: 0,
336            count_epoch_value: 0,
337            count_epoch_tick: 0,
338            // Equal to the reset `Count`, so power-on is not a transition into
339            // `Count == Compare` even though both reset to zero. See
340            // `Cop0::timer_edge`.
341            last_count: 0,
342            // 0b111 = 1:1.5, which matches the N64's 62.5 MHz : 93.75 MHz and is
343            // "allowed with the 100 MHz model only" (UM Appendix A note 1,
344            // p. 628). The manual never names the N64, so this is an INFERENCE
345            // -- accuracy ledger U-6, not a documented fact.
346            ec: 0b111,
347            // Power-on value of the shared unused-register latch. The manual
348            // does not define one; ADR 0004 requires reproducibility, so it is
349            // a documented zero.
350            write_latch: 0,
351        }
352    }
353
354    /// Advance the `Count` timeline to `now` (the scheduler's `count_ticks`).
355    ///
356    /// Called once per CPU step. Note this **sets** rather than increments: the
357    /// position is derived, so a dropped or repeated call cannot desynchronize
358    /// it from the master clock the way an increment would.
359    pub const fn set_now(&mut self, now: u64) {
360        self.now = now;
361    }
362
363    /// The current `Count` timeline position.
364    #[must_use]
365    pub const fn count_now(&self) -> u64 {
366        self.now
367    }
368
369    /// `Count`, computed rather than stored.
370    #[must_use]
371    pub const fn count(&self) -> u32 {
372        self.count_epoch_value
373            .wrapping_add((self.now.wrapping_sub(self.count_epoch_tick)) as u32)
374    }
375
376    /// Has the timer fired — `Count == Compare`?
377    ///
378    /// UM §6.3.4 (p. 165). The comparison is on the *computed* `Count`, so it
379    /// stays true regardless of how the timeline was reached.
380    #[must_use]
381    pub const fn timer_matches(&self) -> bool {
382        self.count() == self.regs[reg::COMPARE as usize] as u32
383    }
384
385    /// Has the timer *just* reached `Compare` — the rising edge of the match?
386    ///
387    /// # Why the edge and not the equality
388    ///
389    /// Both `Count` and `Compare` reset to **zero**, so a plain
390    /// [`Cop0::timer_matches`] is true on the very first step and latches `IP7`
391    /// before a single instruction retires. n64-systemtest catches this exactly:
392    /// it reads `Cause` during an `AdEL` exception and expects `0x10`, while a
393    /// spuriously-latched `IP7` (bit 15) makes it `0x8010`.
394    ///
395    /// The timer fires when `Count` *becomes* equal to `Compare`, which is once
396    /// per wrap of the 32-bit counter, not continuously while they happen to be
397    /// equal. Tracking the previous value is what distinguishes the two, and at
398    /// power-on there is no transition into equality — the two simply start
399    /// there.
400    ///
401    /// # Why this asks whether `Compare` was *crossed*, not whether it is *hit*
402    ///
403    /// `Count` is derived from the master clock, so it keeps advancing while the
404    /// pipeline is stalled — this poll is not guaranteed to land on every value
405    /// the counter takes. An instantaneous `now == Compare` test therefore misses
406    /// the match whenever the gap between two polls steps over it, and a
407    /// 69-PCycle multiply interlock (UM Table 3-12) steps over it comfortably.
408    /// The failure that produces is not a *late* interrupt but a **lost** one:
409    /// `IP7` never latches, and software waiting on the timer hangs forever.
410    ///
411    /// So the question is whether `Compare` lies in the half-open interval
412    /// `(last_count, now]`, in wrapping arithmetic so a counter wrap is just
413    /// another interval. Excluding `last_count` itself preserves the edge
414    /// semantics above: sitting *on* `Compare` is not a transition into it.
415    pub const fn timer_edge(&mut self) -> bool {
416        let now = self.count();
417        let compare = self.regs[reg::COMPARE as usize] as u32;
418        // Distance traveled since the last poll, and the distance to `Compare`,
419        // both measured forward from `last_count`. `Compare` was crossed iff it
420        // is no further away than we traveled — and is not where we started.
421        let traveled = now.wrapping_sub(self.last_count);
422        let to_compare = compare.wrapping_sub(self.last_count);
423        let edge = traveled != 0 && to_compare != 0 && to_compare <= traveled;
424        self.last_count = now;
425        edge
426    }
427
428    /// Set or clear a `Cause.IP` bit.
429    ///
430    /// `bit` is 0..=7. `IP1:IP0` are software interrupts and are written through
431    /// `MTC0` instead; this is the hardware path for `IP2` (RCP) and `IP7`
432    /// (timer).
433    pub const fn set_ip(&mut self, bit: u8, on: bool) {
434        let m = 1u64 << (8 + (bit & 7));
435        let cause = self.regs[reg::CAUSE as usize];
436        self.regs[reg::CAUSE as usize] = if on { cause | m } else { cause & !m };
437    }
438
439    /// Is an interrupt currently *recognized*?
440    ///
441    /// All four conditions, and each one matters (UM §6.1 p. 160, §6.3.5 p. 168,
442    /// Fig. 14-4 p. 357):
443    ///
444    /// - `Status.IE` — the global enable.
445    /// - **`Status.EXL` clear** — a handler is not interrupted by the thing it
446    ///   is handling. This is why `EXL` implies interrupts-off without `IE`
447    ///   being touched.
448    /// - **`Status.ERL` clear** — likewise for the error path.
449    /// - `Cause.IP & Status.IM` — at least one pending *and* unmasked.
450    ///
451    /// Dropping the `EXL`/`ERL` terms is the classic version of this bug: it
452    /// works until the first interrupt arrives inside a handler, and then
453    /// re-enters it forever.
454    #[must_use]
455    pub const fn interrupt_pending(&self) -> bool {
456        let status = self.regs[reg::STATUS as usize];
457        if status & 1 == 0 {
458            return false; // IE clear
459        }
460        if status & (1 << 1) != 0 || status & (1 << 2) != 0 {
461            return false; // EXL or ERL set
462        }
463        let ip = (self.regs[reg::CAUSE as usize] >> 8) & 0xFF;
464        let im = (status >> 8) & 0xFF;
465        ip & im != 0
466    }
467
468    /// Read a register's architectural value.
469    ///
470    /// [`ARCH_MASK`] is applied here as well as on the way in, so a 32-bit
471    /// register can never return non-zero upper bits and `EntryHi.Fill` always
472    /// reads zero — regardless of how the stored value arrived. That matters
473    /// because [`Cop0::set_hardware`] exists precisely to bypass the *write*
474    /// masks, and exception dispatch will feed it raw faulting addresses.
475    #[must_use]
476    pub const fn read(&self, n: u8) -> u64 {
477        let n = n & 31;
478        // The unused registers are a single shared latch, not storage. This is
479        // checked FIRST: they have no `ARCH_MASK` entry, so falling through
480        // would return a masked zero and look like a well-behaved read-only
481        // register rather than the quirk it is.
482        if Self::is_unused(n) {
483            return self.write_latch;
484        }
485        let raw = self.regs[n as usize];
486        if n == reg::COUNT {
487            // Derived, never stored -- see `count`.
488            return self.count() as u64;
489        }
490        if n == reg::CONFIG {
491            // Composed, not masked: the hardwired fields are merged on READ
492            // rather than seeded at construction, because seeded they could be
493            // erased by a wide-enough write mask and every later read would be
494            // wrong. Merged, that is structurally impossible.
495            //
496            // The writable bits are EP (27:24) | BE (15) | CU (3) | K0 (2:0).
497            return (raw & ARCH_MASK[reg::CONFIG as usize])
498                | ((self.ec as u64) << 28)
499                | CONFIG_HARDWIRED_HI
500                | CONFIG_HARDWIRED_LO;
501        }
502        raw & ARCH_MASK[n as usize]
503    }
504
505    /// The COP0 register numbers that are *"Reserved for future use"*
506    /// (UM Table 1-2, p. 46) and behave as a **single shared write latch**
507    /// rather than as storage.
508    ///
509    /// Writing one goes nowhere. Reading one returns the value of the most
510    /// recent `MTC0`/`DMTC0` to **any** COP0 register — so a write-then-read of
511    /// register 7 returns what was written, and the same sequence with any
512    /// other COP0 write in between returns *that* value instead.
513    ///
514    /// **Measured, replacing a guess.** The manual says nothing about these, so
515    /// this implementation previously discarded writes and read zero, recorded
516    /// as accuracy-ledger **U-1**. n64-systemtest documents and exercises the
517    /// real behavior, sweeping five written values against three interposed
518    /// ones specifically so an emulator cannot pass by echoing the first.
519    pub const UNUSED: [u8; 7] = [7, 21, 22, 23, 24, 25, 31];
520
521    /// Is this one of the [`Cop0::UNUSED`] register numbers?
522    #[must_use]
523    pub const fn is_unused(n: u8) -> bool {
524        matches!(n & 31, 7 | 21..=25 | 31)
525    }
526
527    /// Write a register, applying its writable-bit mask.
528    ///
529    /// Bits outside [`WRITE_MASK`] keep their previous value, which is what
530    /// hardware does and is *not* the same as writing zero to them.
531    ///
532    /// Registers 7, 21..=25 and 31 are *"Reserved for future use"* (UM Table 1-2,
533    /// p. 46) and are **not storage** — see [`Cop0::UNUSED`]. Writes to them go
534    /// nowhere; reads return the shared write latch.
535    pub const fn write(&mut self, n: u8, value: u64) {
536        let n = n & 31;
537        let mask = WRITE_MASK[n as usize];
538        self.regs[n as usize] = (self.regs[n as usize] & !mask) | (value & mask);
539        // "If the timer interrupt request is generated, either clear the IP7 bit
540        // of the Cause register or change the contents of the Compare register,
541        // to clear this interrupt" (UM §6.4.18, p. 200).
542        //
543        // IP7 is LATCHED, not a level. The existence of a documented clear is
544        // itself the evidence: a level tied to `Count == Compare` would
545        // self-clear on the next tick and would need no clearing mechanism at
546        // all. It would also LOSE a timer interrupt raised while `EXL` was set,
547        // because the handler would never see the one-tick pulse.
548        //
549        // Note the manual's first option -- writing `Cause.IP7` -- is not
550        // actually available on this part: `Cause` is read-only to software
551        // except `IP1:IP0`. Writing `Compare` is the usable path, and the one
552        // libdragon takes.
553        if n == reg::COMPARE {
554            self.set_ip(7, false);
555        }
556        // Writing Count re-bases the affine mapping rather than storing a value,
557        // so the register stays derived from the master clock (ADR 0006).
558        if n == reg::COUNT {
559            self.count_epoch_value = value as u32;
560            self.count_epoch_tick = self.now;
561        }
562        // Re-base the edge detector on either write.
563        //
564        // `timer_edge` asks whether `Compare` lies in the interval since the
565        // last poll. Both of these writes move an endpoint of that interval
566        // underneath it: an `MTC0 Count` that jumps *over* `Compare` would
567        // otherwise look like a crossing, and a new `Compare` behind the current
568        // `Count` would look like one that already happened. Neither fires on
569        // hardware — the timer fires when `Count` *counts up to* `Compare`, not
570        // when software rearranges the two. Starting the next interval here is
571        // what stops a write from manufacturing an edge.
572        if n == reg::COUNT || n == reg::COMPARE {
573            self.last_count = self.count();
574        }
575        // "Random is set to 31 whenever the Wired register is written"
576        // (UM §5.4.2, p. 147) -- a side effect, not a rule about Random itself,
577        // and easy to lose because it belongs to neither register alone.
578        if n == reg::WIRED {
579            self.regs[reg::RANDOM as usize] = 31;
580        }
581    }
582
583    /// Force a value past the writable-bit mask, for hardware-owned fields.
584    ///
585    /// Exception dispatch writes `Cause.ExcCode`, `EPC` and `BadVAddr`, all of
586    /// which are read-only or partly read-only *to software*. Routing those
587    /// through [`Cop0::write`] would require widening the masks, which would
588    /// also let `MTC0` write them — the exact bug the masks exist to prevent.
589    pub const fn set_hardware(&mut self, n: u8, value: u64) {
590        let n = n & 31;
591        // Bypasses WRITE_MASK, NOT ARCH_MASK. Hardware may write bits software
592        // cannot; it cannot write bits the register does not have. Without this,
593        // dispatch storing a raw 64-bit faulting address into `EntryHi` would
594        // put non-zero bits in `Fill`, which architecturally reads zero.
595        self.regs[n as usize] = value & ARCH_MASK[n as usize];
596    }
597
598    /// `MFC0 rt, rd` — read the low 32 bits, sign-extended into the 64-bit GPR.
599    ///
600    /// Sign-extension applies even to a register that is architecturally 64 bits
601    /// wide: `MFC0` is defined as a 32-bit move, so `MFC0` of an `EPC` whose bit
602    /// 31 is set yields a sign-extended value, not a truncated one.
603    #[must_use]
604    pub const fn mfc0(&self, n: u8) -> u64 {
605        self.read(n) as u32 as i32 as i64 as u64
606    }
607
608    /// `DMFC0 rt, rd` — read the full 64 bits.
609    ///
610    /// On a 32-bit-wide register this is the same as [`Cop0::mfc0`] except for
611    /// sign-extension: the upper half is zero rather than a copy of bit 31.
612    #[must_use]
613    pub const fn dmfc0(&self, n: u8) -> u64 {
614        self.read(n)
615    }
616
617    /// `MTC0 rd, rt` — write the low 32 bits.
618    ///
619    /// For a 64-bit register the upper half is **cleared**, not preserved: the
620    /// value written is the sign-extended 32-bit operand, which is how software
621    /// legitimately writes a KSEG0 address into a 64-bit register.
622    pub const fn mtc0(&mut self, n: u8, value: u64) {
623        let v = if is_wide(n) {
624            value as u32 as i32 as i64 as u64
625        } else {
626            value & 0xFFFF_FFFF
627        };
628        self.write_latch = v;
629        self.write(n, v);
630    }
631
632    /// `DMTC0 rd, rt` — write the full 64 bits.
633    pub const fn dmtc0(&mut self, n: u8, value: u64) {
634        self.write_latch = value;
635        self.write(n, value);
636    }
637
638    /// Advance `Random` by one instruction (UM §5.4.2, p. 147).
639    ///
640    /// *"decrements as each instruction executes"*, reloading 31 when it reaches
641    /// the `Wired` floor.
642    ///
643    /// # It is a plain 6-bit down-counter, and that matters when `Wired > 31`
644    ///
645    /// The reload fires on `cur == wired`, **not** `cur <= wired`, and the
646    /// decrement wraps 0 → 63. For the ordinary case (`Wired <= 31`) the two
647    /// readings agree: the counter walks 31 down to `Wired` either way.
648    ///
649    /// They diverge once `Wired` exceeds 31, which software can arrange because
650    /// `Wired` is six bits wide. Under `<=` the counter is immediately at or
651    /// below the floor and pins at 31 forever; under `==` it walks 31 → 0 → 63 →
652    /// `Wired`, covering the **whole** range. n64-systemtest checks exactly that,
653    /// and sampling a pinned register cannot be distinguished from sampling a
654    /// slow one without it.
655    pub const fn tick_random(&mut self) {
656        let wired = (self.regs[reg::WIRED as usize] & 0x3F) as u32;
657        let cur = (self.regs[reg::RANDOM as usize] & 0x3F) as u32;
658        self.regs[reg::RANDOM as usize] = if cur == wired {
659            31
660        } else {
661            cur.wrapping_sub(1) as u64 & 0x3F
662        };
663    }
664}
665
666/// The COP0 access forms, decoded from the `rs` field of a `COP0` instruction.
667///
668/// Split out so the decoder names them rather than the executor re-deriving them
669/// from raw bits.
670#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
671pub enum Cop0Op {
672    /// `MFC0 rt, rd` — 32-bit read, sign-extended.
673    Mfc0,
674    /// `DMFC0 rt, rd` — 64-bit read.
675    Dmfc0,
676    /// `MTC0 rt, rd` — 32-bit write.
677    Mtc0,
678    /// `DMTC0 rt, rd` — 64-bit write.
679    Dmtc0,
680}
681
682impl Cop0Op {
683    /// Decode the `rs` field of a `COP0` instruction, if it names an access form.
684    ///
685    /// Returns `None` for the TLB and `ERET` encodings (`rs` bit 4 set), which
686    /// are separate instructions handled by T-12-002 and T-12-004.
687    #[must_use]
688    pub const fn from_rs(rs: u8) -> Option<Self> {
689        match rs {
690            0o00 => Some(Self::Mfc0),
691            0o01 => Some(Self::Dmfc0),
692            0o04 => Some(Self::Mtc0),
693            0o05 => Some(Self::Dmtc0),
694            _ => None,
695        }
696    }
697}
698
699/// The `rd` field of a `COP0` instruction is the register number.
700#[must_use]
701pub const fn cop0_reg(d: Decoded) -> u8 {
702    d.rd
703}
704
705#[cfg(test)]
706mod tests {
707    use super::*;
708
709    /// The list of 64-bit registers has no generating rule, so it is asserted
710    /// element by element against UM Table 1-2 (p. 46). Getting one wrong is
711    /// invisible until 64-bit software runs.
712    #[test]
713    fn the_eight_wide_registers_are_exactly_these() {
714        let wide = [
715            reg::ENTRY_LO0,
716            reg::ENTRY_LO1,
717            reg::CONTEXT,
718            reg::BAD_VADDR,
719            reg::ENTRY_HI,
720            reg::EPC,
721            reg::XCONTEXT,
722            reg::ERROR_EPC,
723        ];
724        for n in 0..32u8 {
725            assert_eq!(is_wide(n), wide.contains(&n), "register {n} width is wrong");
726        }
727        assert_eq!(wide.len(), 8, "exactly eight, not seven or nine");
728    }
729
730    /// Read-only registers must reject `MTC0`. Modeled as an all-zero write
731    /// mask so "read-only" and "reserved bit" share one mechanism.
732    #[test]
733    fn read_only_registers_reject_writes() {
734        for n in [reg::RANDOM, reg::BAD_VADDR, reg::PRID, reg::CACHE_ERR] {
735            let mut c = Cop0::new();
736            let before = c.read(n);
737            c.dmtc0(n, 0xDEAD_BEEF_DEAD_BEEF);
738            assert_eq!(c.read(n), before, "register {n} is read-only");
739        }
740    }
741
742    /// `Cause` is read-only *except* `IP1:IP0` — a mask, not a whole-register
743    /// rule, and the one most likely to be widened by mistake.
744    #[test]
745    fn cause_accepts_only_the_two_software_interrupt_bits() {
746        let mut c = Cop0::new();
747        c.dmtc0(reg::CAUSE, u64::MAX);
748        assert_eq!(
749            c.read(reg::CAUSE),
750            0x0000_0300,
751            "only IP1:IP0 (bits 9:8) are software-writable"
752        );
753    }
754
755    /// `Status.DS.TS` (bit 21) is read-only; the rest of `DS` is writable.
756    #[test]
757    fn status_ts_is_read_only_but_the_rest_of_ds_is_not() {
758        let mut c = Cop0::new();
759        c.dmtc0(reg::STATUS, u64::MAX);
760        let s = c.read(reg::STATUS);
761        assert_eq!(s & (1 << 21), 0, "TS must not be software-settable");
762        assert_ne!(s & (1 << 22), 0, "BEV in the same field must be settable");
763        assert_eq!(s & (1 << 23), 0, "bit 23 is hardwired zero");
764        assert_eq!(s & (1 << 19), 0, "bit 19 is hardwired zero");
765    }
766
767    /// `Config`'s hardwired fields survive any write, because they are merged on
768    /// read rather than stored. Seeding them instead would let a wide mask erase
769    /// them permanently.
770    #[test]
771    fn config_hardwired_fields_survive_a_hostile_write() {
772        let mut c = Cop0::new();
773        c.dmtc0(reg::CONFIG, 0);
774        let v = c.read(reg::CONFIG);
775        assert_eq!(
776            (v >> 16) & 0xFF,
777            0b0000_0110,
778            "bits 23:16 are hardwired (UM Fig. 5-16)"
779        );
780        assert_eq!(
781            (v >> 4) & 0x7FF,
782            0b110_0100_0110,
783            "bits 14:4 are hardwired (UM Fig. 5-16)"
784        );
785        assert_eq!(
786            (v >> 28) & 0b111,
787            0b111,
788            "EC is read-only from `DivMode` pins"
789        );
790    }
791
792    /// Independent cross-check: the real N64 IPL boot values must decompose
793    /// exactly against these layouts. This is the cheapest evidence available
794    /// that the field positions are right, and it comes from outside the manual.
795    #[test]
796    fn the_real_ipl_boot_values_round_trip() {
797        let mut c = Cop0::new();
798        // IPL3 leaves Config = 0x0006E463: BE=1, K0=3, EP=0, CU=0, and both
799        // hardwired fields. Our EC is 0b111 rather than the 0b000 in that
800        // capture, because EC is read-only and the captured value is what IPL
801        // *wrote*, not what it read back -- so compare with EC masked out.
802        c.mtc0(reg::CONFIG, 0x0006_E463);
803        let got = c.read(reg::CONFIG) & !0x7000_0000;
804        assert_eq!(
805            got, 0x0006_E463,
806            "Config must round-trip the real IPL value outside EC"
807        );
808
809        // Status = 0x34000000 is CU1|CU0|FR.
810        c.mtc0(reg::STATUS, 0x3400_0000);
811        assert_eq!(c.read(reg::STATUS), 0x3400_0000);
812    }
813
814    /// Writing `Wired` forces `Random` to 31 — a side effect belonging to
815    /// neither register alone, and easy to lose for exactly that reason.
816    #[test]
817    fn writing_wired_reloads_random() {
818        let mut c = Cop0::new();
819        c.tick_random();
820        c.tick_random();
821        assert_ne!(c.read(reg::RANDOM), 31, "Random moved off its reset value");
822        c.mtc0(reg::WIRED, 4);
823        assert_eq!(c.read(reg::RANDOM), 31, "writing Wired reloads Random");
824    }
825
826    /// `Random` decrements per instruction and wraps at the `Wired` floor.
827    #[test]
828    fn random_decrements_and_floors_at_wired() {
829        let mut c = Cop0::new();
830        c.mtc0(reg::WIRED, 28);
831        assert_eq!(c.read(reg::RANDOM), 31);
832        // Wired itself IS a legal value -- it is the first non-wired entry, and
833        // TLBWR must be able to select it. The range is [Wired, 31] inclusive,
834        // so the wrap happens after 28, not after 29.
835        for expect in [30, 29, 28, 31, 30, 29] {
836            c.tick_random();
837            assert_eq!(c.read(reg::RANDOM), expect, "range is [Wired, 31]");
838        }
839    }
840
841    /// `EntryHi.Fill` (61:40) is write-ignored and reads zero.
842    #[test]
843    fn entryhi_fill_is_write_ignored() {
844        let mut c = Cop0::new();
845        c.dmtc0(reg::ENTRY_HI, u64::MAX);
846        let v = c.read(reg::ENTRY_HI);
847        assert_eq!((v >> 40) & 0x3F_FFFF, 0, "Fill reads zero");
848        assert_eq!((v >> 62) & 0b11, 0b11, "R is writable");
849        assert_eq!(v & 0xFF, 0xFF, "ASID is writable");
850        assert_eq!((v >> 13) & 0x7FF_FFFF, 0x7FF_FFFF, "VPN2 is writable");
851        assert_eq!((v >> 8) & 0x1F, 0, "bits 12:8 are hardwired zero");
852    }
853
854    /// `MFC0` sign-extends; `DMFC0` does not. The difference is visible on any
855    /// register holding a KSEG0 address, which is most of the interesting ones.
856    #[test]
857    fn mfc0_sign_extends_where_dmfc0_does_not() {
858        let mut c = Cop0::new();
859        c.dmtc0(reg::EPC, 0x8000_0180);
860        assert_eq!(c.mfc0(reg::EPC), 0xFFFF_FFFF_8000_0180);
861        assert_eq!(c.dmfc0(reg::EPC), 0x8000_0180);
862    }
863
864    /// `MTC0` to a 64-bit register sign-extends rather than preserving the upper
865    /// half — that is how software writes a KSEG0 address with one instruction.
866    #[test]
867    fn mtc0_to_a_wide_register_sign_extends() {
868        let mut c = Cop0::new();
869        c.dmtc0(reg::EPC, 0x1234_5678_9ABC_DEF0);
870        c.mtc0(reg::EPC, 0x8000_0180);
871        assert_eq!(
872            c.read(reg::EPC),
873            0xFFFF_FFFF_8000_0180,
874            "the old upper half must not survive"
875        );
876    }
877
878    /// `set_hardware` bypasses the masks, because exception dispatch writes
879    /// fields that are read-only to software. If this went through `write`, the
880    /// masks would have to be widened and `MTC0` could reach them too.
881    #[test]
882    fn hardware_writes_bypass_the_software_masks() {
883        let mut c = Cop0::new();
884        c.dmtc0(reg::BAD_VADDR, 0x1234);
885        assert_eq!(c.read(reg::BAD_VADDR), 0, "software cannot write BadVAddr");
886        c.set_hardware(reg::BAD_VADDR, 0x1234);
887        assert_eq!(c.read(reg::BAD_VADDR), 0x1234, "hardware can");
888    }
889
890    /// Cold-reset state, as far as the manual defines it (UM §6.4.4, p. 183).
891    #[test]
892    fn cold_reset_matches_the_documented_fields() {
893        let c = Cop0::new();
894        let s = c.read(reg::STATUS);
895        assert_ne!(s & (1 << 2), 0, "ERL set");
896        assert_ne!(s & (1 << 22), 0, "BEV set");
897        assert_eq!(s & (1 << 21), 0, "TS clear");
898        assert_eq!(s & (1 << 20), 0, "SR clear");
899        assert_eq!(s & (1 << 27), 0, "RP clear");
900        assert_eq!(c.read(reg::RANDOM), 31, "Random = 31");
901        assert_eq!(c.read(reg::WIRED), 0, "Wired = 0");
902        assert_ne!(c.read(reg::CONFIG) & (1 << 15), 0, "Config.BE set");
903        assert_eq!((c.read(reg::PRID) >> 8) & 0xFF, 0x0B, "PRId.Imp = 0x0B");
904    }
905
906    /// **The reserved registers are a shared write latch, not storage.**
907    ///
908    /// This replaces a pinned *guess*: the manual documents an absence, so the
909    /// implementation used to discard writes and read zero (ledger U-1), and
910    /// this test pinned that choice. n64-systemtest documents and exercises the
911    /// real behavior, so the guess is now evidence.
912    ///
913    /// The second half is the part a naive implementation fails: an intervening
914    /// write to *any other* COP0 register changes what the reserved register
915    /// reads back. Storing the value per-register passes the first assertion
916    /// and fails this one, which is why the oracle sweeps interposed values.
917    #[test]
918    fn the_reserved_registers_are_a_shared_write_latch() {
919        for n in Cop0::UNUSED {
920            let mut c = Cop0::new();
921            c.mtc0(n, 0x1317_1A1E);
922            assert_eq!(
923                c.read(n) & 0xFFFF_FFFF,
924                0x1317_1A1E,
925                "reg {n} reads back its own write"
926            );
927
928            // Any other COP0 write takes over the latch -- `Compare` here, as
929            // the suite uses.
930            c.mtc0(reg::COMPARE, 0x8BAD_F00D);
931            assert_eq!(
932                c.read(n) & 0xFFFF_FFFF,
933                0x8BAD_F00D,
934                "reg {n} follows the latch, so it is not storage"
935            );
936            // ...and the real register still holds its own value.
937            assert_eq!(c.read(reg::COMPARE) & 0xFFFF_FFFF, 0x8BAD_F00D);
938        }
939    }
940
941    /// The unused set is exactly 7, 21..=25 and 31 — no more and no fewer. A
942    /// range that swept in a real register would make it read the latch instead
943    /// of its own value.
944    #[test]
945    fn only_the_seven_documented_numbers_are_unused() {
946        for n in 0..32u8 {
947            assert_eq!(
948                Cop0::is_unused(n),
949                Cop0::UNUSED.contains(&n),
950                "register {n}"
951            );
952        }
953        for n in [reg::COMPARE, reg::STATUS, reg::CAUSE, reg::EPC, reg::PRID] {
954            assert!(!Cop0::is_unused(n), "{n} is a real register");
955        }
956    }
957
958    /// The access-form decode, including that TLB/`ERET` encodings are not
959    /// access forms and must not be silently treated as one.
960    #[test]
961    fn cop0_access_forms_decode_and_tlb_forms_do_not() {
962        assert_eq!(Cop0Op::from_rs(0o00), Some(Cop0Op::Mfc0));
963        assert_eq!(Cop0Op::from_rs(0o01), Some(Cop0Op::Dmfc0));
964        assert_eq!(Cop0Op::from_rs(0o04), Some(Cop0Op::Mtc0));
965        assert_eq!(Cop0Op::from_rs(0o05), Some(Cop0Op::Dmtc0));
966        // rs bit 4 set: TLBR/TLBWI/TLBWR/TLBP/ERET -- T-12-002 and T-12-004.
967        assert_eq!(Cop0Op::from_rs(0o20), None);
968        assert_eq!(
969            Cop0Op::from_rs(0o02),
970            None,
971            "unassigned rs is not an access"
972        );
973    }
974
975    /// Writes must preserve unmasked bits rather than zeroing them -- hardware
976    /// discards the write to those bits, which is not the same thing.
977    #[test]
978    fn masked_off_bits_keep_their_previous_value() {
979        let mut c = Cop0::new();
980        // set_hardware itself applies ARCH_MASK, so this stores Cause's
981        // architectural bits (BD | CE | IP | ExcCode), not all 32.
982        c.set_hardware(reg::CAUSE, 0xFFFF_FFFF);
983        assert_eq!(c.read(reg::CAUSE), ARCH_MASK[reg::CAUSE as usize]);
984        c.dmtc0(reg::CAUSE, 0);
985        assert_eq!(
986            c.read(reg::CAUSE),
987            ARCH_MASK[reg::CAUSE as usize] & !0x300,
988            "only IP1:IP0 were cleared; every other architectural bit survived"
989        );
990    }
991
992    /// `set_hardware` must mask on the way **in**, so the stored state never
993    /// holds bits no hardware register has.
994    ///
995    /// Asserted against `regs` directly rather than through `read`, because
996    /// `read` masks too — a read-back test passes with either enforcement point
997    /// alone and so pins neither. Found by mutation testing: removing either one
998    /// individually was invisible until these two tests existed.
999    #[test]
1000    fn set_hardware_masks_on_the_way_in_not_only_on_the_way_out() {
1001        let mut c = Cop0::new();
1002        c.set_hardware(reg::CAUSE, u64::MAX);
1003        assert_eq!(
1004            c.regs[reg::CAUSE as usize],
1005            ARCH_MASK[reg::CAUSE as usize],
1006            "stored state must already be architectural"
1007        );
1008        c.set_hardware(reg::ENTRY_HI, u64::MAX);
1009        assert_eq!(
1010            (c.regs[reg::ENTRY_HI as usize] >> 40) & 0x3F_FFFF,
1011            0,
1012            "EntryHi.Fill is not even stored"
1013        );
1014    }
1015
1016    /// `read` must mask on the way **out**, independently of how the value was
1017    /// stored. Poked straight into `regs` for the same reason as above: going
1018    /// through `set_hardware` would let that mask do the work instead.
1019    #[test]
1020    fn read_masks_on_the_way_out_even_if_storage_is_corrupt() {
1021        let mut c = Cop0::new();
1022        // Not `Count`: that one is derived rather than stored, so poking its
1023        // backing slot proves nothing.
1024        c.regs[reg::COMPARE as usize] = u64::MAX;
1025        assert_eq!(c.read(reg::COMPARE), 0xFFFF_FFFF, "32 bits, not 64");
1026        c.regs[reg::ENTRY_HI as usize] = u64::MAX;
1027        assert_eq!(
1028            (c.read(reg::ENTRY_HI) >> 40) & 0x3F_FFFF,
1029            0,
1030            "Fill reads zero even from corrupt storage"
1031        );
1032    }
1033
1034    /// `set_hardware` on `Config` must not erase it. `Config`'s read value is
1035    /// composed rather than masked, so it is the one register whose `ARCH_MASK`
1036    /// entry is easy to leave at zero — which would silently wipe it.
1037    #[test]
1038    fn set_hardware_does_not_wipe_config() {
1039        let mut c = Cop0::new();
1040        c.set_hardware(reg::CONFIG, 0x0006_E463);
1041        let v = c.read(reg::CONFIG);
1042        assert_ne!(v & (1 << 15), 0, "BE survived");
1043        assert_eq!(v & 0b111, 3, "K0 survived");
1044        assert_eq!((v >> 16) & 0xFF, 0b0000_0110, "hardwired field still there");
1045    }
1046
1047    /// A 32-bit register must never return non-zero upper bits, no matter how
1048    /// the value got in. `set_hardware` deliberately bypasses the *write* masks,
1049    /// so without an architectural mask it would be a hole straight into the
1050    /// stored state — and exception dispatch (T-12-002) feeds it raw addresses.
1051    #[test]
1052    fn a_32_bit_register_cannot_hold_upper_bits_even_via_set_hardware() {
1053        for n in [
1054            reg::COUNT,
1055            reg::COMPARE,
1056            reg::STATUS,
1057            reg::CAUSE,
1058            reg::LL_ADDR,
1059        ] {
1060            let mut c = Cop0::new();
1061            c.set_hardware(n, u64::MAX);
1062            assert_eq!(
1063                c.read(n) >> 32,
1064                0,
1065                "register {n} is 32 bits wide; its upper half must read zero"
1066            );
1067            assert_eq!(c.dmfc0(n) >> 32, 0, "and DMFC0 must not expose them either");
1068        }
1069    }
1070
1071    /// `EntryHi.Fill` reads zero architecturally, not merely "is unwritable by
1072    /// MTC0". Dispatch storing a raw 64-bit faulting address is exactly the path
1073    /// that would otherwise put bits there.
1074    #[test]
1075    fn entryhi_fill_reads_zero_even_when_hardware_writes_a_raw_address() {
1076        let mut c = Cop0::new();
1077        c.set_hardware(reg::ENTRY_HI, 0xFFFF_FFFF_FFFF_FFFF);
1078        assert_eq!(
1079            (c.read(reg::ENTRY_HI) >> 40) & 0x3F_FFFF,
1080            0,
1081            "Fill (61:40) reads zero regardless of the writer"
1082        );
1083        assert_eq!((c.read(reg::ENTRY_HI) >> 62) & 0b11, 0b11, "R survives");
1084    }
1085
1086    /// The two masks are different things and the difference is load-bearing:
1087    /// a writable bit that is not architecturally present would be storable and
1088    /// unreadable, and a read-only register needs a wide arch mask with a zero
1089    /// write mask.
1090    #[test]
1091    fn every_writable_bit_is_also_an_architectural_bit() {
1092        for n in 0..32u8 {
1093            let w = WRITE_MASK[n as usize];
1094            let a = ARCH_MASK[n as usize];
1095            assert_eq!(
1096                w & !a,
1097                0,
1098                "register {n} has writable bits that are not architectural"
1099            );
1100        }
1101        // And the converse must NOT hold, or the two tables would be redundant.
1102        assert_ne!(
1103            ARCH_MASK[reg::CAUSE as usize],
1104            WRITE_MASK[reg::CAUSE as usize],
1105            "Cause is mostly hardware-written"
1106        );
1107        assert_eq!(WRITE_MASK[reg::BAD_VADDR as usize], 0);
1108        assert_ne!(ARCH_MASK[reg::BAD_VADDR as usize], 0);
1109    }
1110
1111    /// `Count` is **derived**, not stored: ADR 0006 permits exactly one
1112    /// incremented counter in the core, and it is `master_ticks`.
1113    #[test]
1114    fn count_is_derived_from_the_timeline_not_incremented() {
1115        let mut c = Cop0::new();
1116        c.set_now(0);
1117        assert_eq!(c.read(reg::COUNT), 0);
1118        c.set_now(100);
1119        assert_eq!(c.read(reg::COUNT), 100, "follows the timeline with no += 1");
1120        // Skipping the timeline forward must not lose anything -- an increment
1121        // would, which is the whole reason this is derived.
1122        c.set_now(1_000_000);
1123        assert_eq!(c.read(reg::COUNT), 1_000_000);
1124    }
1125
1126    /// ...and still guest-writable, which is what makes it *affine* rather than
1127    /// simply derived. A write re-bases the epoch; it does not store a value
1128    /// that then drifts.
1129    #[test]
1130    fn writing_count_rebases_the_epoch_rather_than_storing() {
1131        let mut c = Cop0::new();
1132        c.set_now(500);
1133        c.mtc0(reg::COUNT, 42);
1134        assert_eq!(c.read(reg::COUNT), 42, "reads back what was written");
1135        c.set_now(510);
1136        assert_eq!(
1137            c.read(reg::COUNT),
1138            52,
1139            "and then advances with the timeline from there"
1140        );
1141    }
1142
1143    /// The timer fires on `Count == Compare` (UM §6.3.4, p. 165).
1144    #[test]
1145    fn the_timer_matches_when_count_reaches_compare() {
1146        let mut c = Cop0::new();
1147        c.set_now(0);
1148        c.mtc0(reg::COMPARE, 5);
1149        assert!(!c.timer_matches());
1150        c.set_now(5);
1151        assert!(c.timer_matches());
1152        c.set_now(6);
1153        assert!(!c.timer_matches(), "it is an equality, not a threshold");
1154    }
1155
1156    /// **The timer must not fire at power-on.** `Count` and `Compare` both reset
1157    /// to zero, so an equality test latches `IP7` before a single instruction
1158    /// retires -- which n64-systemtest catches as `Cause = 0x8010` instead of
1159    /// `0x10` during an `AdEL` exception.
1160    #[test]
1161    fn the_timer_does_not_fire_at_power_on_despite_count_equalling_compare() {
1162        let mut c = Cop0::new();
1163        assert_eq!(c.count(), 0);
1164        assert_eq!(c.read(reg::COMPARE), 0, "both reset to zero");
1165        assert!(
1166            c.timer_matches(),
1167            "the equality genuinely holds -- that is the trap"
1168        );
1169        assert!(
1170            !c.timer_edge(),
1171            "but there is no TRANSITION into it, so the timer must not fire"
1172        );
1173    }
1174
1175    /// The timer fires on the transition into `Count == Compare`, once, and not
1176    /// again while the equality merely persists.
1177    #[test]
1178    fn the_timer_fires_on_the_edge_and_only_once() {
1179        let mut c = Cop0::new();
1180        c.mtc0(reg::COMPARE, 3);
1181        assert!(!c.timer_edge(), "Count = 0, Compare = 3");
1182        c.set_now(3);
1183        assert!(c.timer_edge(), "Count reached Compare");
1184        assert!(
1185            !c.timer_edge(),
1186            "the equality still holds, but the edge has passed"
1187        );
1188    }
1189
1190    /// **A poll that steps over `Compare` still sees the crossing.**
1191    ///
1192    /// `Count` is derived from the master clock, so the gap between two polls is
1193    /// whatever the pipeline did in between — a stall makes it dozens of cycles.
1194    /// Asking "is `Count == Compare` now?" loses the match entirely; asking "was
1195    /// `Compare` crossed since the last poll?" does not.
1196    #[test]
1197    fn the_timer_edge_is_detected_even_when_the_poll_steps_over_compare() {
1198        let mut c = Cop0::new();
1199        c.mtc0(reg::COMPARE, 40);
1200        assert!(!c.timer_edge());
1201        // One poll, jumping clean over 40.
1202        c.set_now(80);
1203        assert!(
1204            c.timer_edge(),
1205            "Compare lies inside the interval the poll skipped"
1206        );
1207        c.set_now(120);
1208        assert!(!c.timer_edge(), "and it does not fire again afterwards");
1209    }
1210
1211    /// **Software cannot manufacture a timer edge by writing `Count`.**
1212    ///
1213    /// The timer fires when `Count` counts up to `Compare`. An `MTC0 Count` that
1214    /// jumps straight over `Compare` moves an endpoint of the detector's
1215    /// interval, and looks exactly like a crossing to an implementation that
1216    /// does not re-base on the write.
1217    #[test]
1218    fn writing_count_over_compare_does_not_fire_the_timer() {
1219        let mut c = Cop0::new();
1220        c.mtc0(reg::COMPARE, 40);
1221        assert!(!c.timer_edge());
1222        c.mtc0(reg::COUNT, 80); // jumped past Compare without counting to it
1223        assert!(
1224            !c.timer_edge(),
1225            "a write is not a count — no edge was crossed"
1226        );
1227        // Counting up to the next match still works.
1228        c.mtc0(reg::COMPARE, 100);
1229        c.set_now(c.count_now() + 20); // Count 80 -> 100
1230        assert!(c.timer_edge(), "counting up to Compare still fires");
1231    }
1232
1233    /// The same for `Compare`: setting it *behind* the current `Count` must not
1234    /// look like a crossing that already happened.
1235    ///
1236    /// The gap before the write is deliberate and load-bearing. If `Count` were
1237    /// polled immediately beforehand the detector's interval would already be
1238    /// empty, and the test would pass whether or not the write re-bases —
1239    /// converging success and failure paths onto the same behavior and proving
1240    /// nothing.
1241    #[test]
1242    fn writing_compare_behind_count_does_not_fire_the_timer() {
1243        let mut c = Cop0::new();
1244        // Count advances to 100 with NO intervening poll, so the pending
1245        // interval is (0, 100] when the write lands.
1246        c.set_now(100);
1247        c.mtc0(reg::COMPARE, 50);
1248        assert!(
1249            !c.timer_edge(),
1250            "Compare was placed behind Count; nothing ever counted up to it"
1251        );
1252    }
1253}