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
5//! enum lives in `rustynes-cpu` rather than `rustynes-core` because the [`Bus`]
6//! trait method [`Bus::poll_irq_at_phase`] is parameterised by it; the
7//! CPU crate stays at the top of the workspace dep graph (`rustynes-core`
8//! already depends on `rustynes-cpu`, not the other way round) and any
9//! `rustynes-core` consumer continues to import `M2Phase` 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//! [`Bus`]: crate::Bus
16//! [`Bus::poll_irq_at_phase`]: crate::Bus::poll_irq_at_phase
17
18/// Convention for the M2-phase reference relative to the CPU cycle's 3
19/// PPU dots.
20///
21/// In silicon the 6502 cycle has two halves — φ1 (M2 low; address valid;
22/// memory access) and φ2 (M2 high; data latch; interrupt sample).  The
23/// host scheduler ticks the PPU 3 dots per CPU cycle.  The convention
24/// this crate adopts:
25///
26/// * [`M2Phase::Low`] — the **first** half of the cycle: from the start
27///   of the bus's per-cycle tick through the end of PPU sub-dot 1
28///   (corresponds to silicon's φ1).
29/// * [`M2Phase::High`] — the **second** half of the cycle: from the end
30///   of PPU sub-dot 1 through end-of-cycle (corresponds to silicon's
31///   φ2).  The M2-rising boundary lives between sub-dot 1 and sub-dot 2.
32///
33/// At end-of-cycle the bus advances its cycle counter and the phase
34/// resets to [`M2Phase::Low`] for the next cycle.
35///
36/// This is the canonical reference enum used by the docs/ADR, by the
37/// IRQ-timing tracing fixture (`rustynes_core::irq_trace`), and by
38/// [`Bus::poll_irq_at_phase`].
39///
40/// [`Bus::poll_irq_at_phase`]: crate::Bus::poll_irq_at_phase
41#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub enum M2Phase {
43    /// M2 low (φ1): memory access window.
44    Low,
45    /// M2 high (φ2): IRQ/NMI sample window.
46    High,
47}
48
49impl M2Phase {
50    /// CSV-friendly single-letter abbreviation.
51    #[must_use]
52    pub const fn as_str(self) -> &'static str {
53        match self {
54            Self::Low => "L",
55            Self::High => "H",
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn m2_phase_as_str_round_trips() {
66        assert_eq!(M2Phase::Low.as_str(), "L");
67        assert_eq!(M2Phase::High.as_str(), "H");
68    }
69}