Skip to main content

rustynes_cpu/
scheduler.rs

1//! Scheduler-facing types the CPU crate exposes to its bus host.
2//!
3//! Currently exposes only [`M2Phase`], the canonical reference enum for
4//! "which half of the 6502 cycle the host bus is currently in". The enum
5//! lives in `rustynes-cpu` rather than `rustynes-core` because it used to
6//! parameterise the `Bus::poll_irq_at_phase` trait method, which v2.9.8
7//! removed with the rest of the v2.7.5 deprecations (ADR 0042). It survives
8//! as the vocabulary of the IRQ-timing trace (`rustynes_core::irq_trace`),
9//! and `rustynes-core` consumers keep importing it from
10//! `rustynes_core::scheduler` via re-export.
11//!
12//! See `docs/scheduler.md` and `docs/adr/0002-irq-timing-coordination.md`
13//! for the surrounding design.
14
15/// Convention for the M2-phase reference relative to the CPU cycle's 3
16/// PPU dots.
17///
18/// In silicon the 6502 cycle has two halves — φ1 (M2 low; address valid;
19/// memory access) and φ2 (M2 high; data latch; interrupt sample).  The
20/// host scheduler ticks the PPU 3 dots per CPU cycle.  The convention
21/// this crate adopts:
22///
23/// * [`M2Phase::Low`] — the **first** half of the cycle: from the start
24///   of the bus's per-cycle tick through the end of PPU sub-dot 1
25///   (corresponds to silicon's φ1).
26/// * [`M2Phase::High`] — the **second** half of the cycle: from the end
27///   of PPU sub-dot 1 through end-of-cycle (corresponds to silicon's
28///   φ2).  The M2-rising boundary lives between sub-dot 1 and sub-dot 2.
29///
30/// At end-of-cycle the bus advances its cycle counter and the phase
31/// resets to [`M2Phase::Low`] for the next cycle.
32///
33/// This is the canonical reference enum used by the docs/ADR and by the
34/// IRQ-timing tracing fixture (`rustynes_core::irq_trace`).
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum M2Phase {
37    /// M2 low (φ1): memory access window.
38    Low,
39    /// M2 high (φ2): IRQ/NMI sample window.
40    High,
41}
42
43impl M2Phase {
44    /// CSV-friendly single-letter abbreviation.
45    #[must_use]
46    pub const fn as_str(self) -> &'static str {
47        match self {
48            Self::Low => "L",
49            Self::High => "H",
50        }
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn m2_phase_as_str_round_trips() {
60        assert_eq!(M2Phase::Low.as_str(), "L");
61        assert_eq!(M2Phase::High.as_str(), "H");
62    }
63}