Skip to main content

rustysnes_core/
sa1_bus.rs

1//! The SA-1 CPU's bus adapter — the bridge that lets `rustysnes-core` step the second 65C816.
2//!
3//! The one-directional crate graph forbids `rustysnes-cart` from depending on `rustysnes-cpu`, so
4//! the SA-1 *system* (registers, Super-MMC banking, BW-RAM, I-RAM, arithmetic unit, DMA, timer)
5//! lives in [`rustysnes_cart::coproc::sa1`] and exposes the SA-1 CPU's memory view through the
6//! [`rustysnes_cart::Board`] second-CPU hooks. This adapter wraps the cart board behind the
7//! [`rustysnes_cpu::Bus`] trait so the second [`rustysnes_cpu::Cpu`] (owned by the scheduler) can
8//! borrow it for an instruction.
9//!
10//! Unlike the main bus this adapter does **not** advance the master clock: the SA-1's own timing is
11//! driven by the scheduler, which charges the SA-1 H/V timer from the instruction's returned cycle
12//! count (`docs/scheduler.md` §SA-1). Interrupt/reset vector redirection (the SA-1 uses its own
13//! CRV/CIV/CNV vectors, not the ROM `$FFEx` vectors) is handled inside the board's
14//! [`rustysnes_cart::Board::second_cpu_read`].
15
16use rustysnes_cart::Board;
17use rustysnes_cpu::Bus as CpuBus;
18
19/// A [`rustysnes_cpu::Bus`] view over the SA-1 board's second-CPU memory map.
20pub(crate) struct Sa1Bus<'a> {
21    /// The cart board carrying the SA-1 system state.
22    pub board: &'a mut dyn Board,
23}
24
25impl CpuBus for Sa1Bus<'_> {
26    fn read24(&mut self, addr24: u32) -> u8 {
27        self.board.second_cpu_read(addr24)
28    }
29
30    fn write24(&mut self, addr24: u32, val: u8) {
31        self.board.second_cpu_write(addr24, val);
32    }
33
34    fn poll_nmi(&mut self) -> bool {
35        self.board.second_cpu_poll_nmi()
36    }
37
38    fn poll_irq(&mut self) -> bool {
39        self.board.second_cpu_poll_irq()
40    }
41
42    // `advance` is intentionally the default no-op: the SA-1's timebase is charged by the
43    // scheduler from the instruction's returned cycle count, not per bus access.
44}