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}