pub trait DmaBus {
// Required methods
fn read_a(&mut self, addr: u32) -> u8;
fn write_a(&mut self, addr: u32, val: u8);
fn read_b(&mut self, addr: u8) -> u8;
fn write_b(&mut self, addr: u8, val: u8);
// Provided methods
fn step(&mut self, _clocks: u32) { ... }
fn scanline(&self) -> u16 { ... }
fn visible_height(&self) -> u16 { ... }
fn hdma_last_line(&self) -> u16 { ... }
fn set_hdma_last_line(&mut self, _line: u16) { ... }
}Expand description
Read/write split across the SNES A-bus (24-bit) and B-bus ($21xx) for the DMA controller.
Required Methods§
Sourcefn read_a(&mut self, addr: u32) -> u8
fn read_a(&mut self, addr: u32) -> u8
Read a byte from the A-bus (24-bit CPU address). Invalid A-bus regions return open bus.
Provided Methods§
Sourcefn step(&mut self, _clocks: u32)
fn step(&mut self, _clocks: u32)
Advance the master clock by clocks during a transfer, so time-dependent state that
gates the transfer — chiefly the PPU scanline that decides VRAM/CGRAM/OAM accessibility
($2118/$2119 land only in force-blank or V-blank) — tracks the transfer in lockstep,
exactly as the real bus keeps scanning while a DMA runs (ares Channel::step →
cpu.step). A big GP-DMA that starts late in active display and crosses into V-blank must
have its V-blank portion actually reach VRAM; freezing the scanline for the whole burst
would drop every write (the Star Fox framebuffer-transfer bug). Default no-op so the
controller stays unit-testable against an isolated bus with no clock.
Sourcefn scanline(&self) -> u16
fn scanline(&self) -> u16
The PPU scanline the bus is currently scanning. On real hardware HDMA preempts a running
GP-DMA at the start of every scanline; a long GP-DMA that spans scanline boundaries must
therefore let those HDMA transfers interleave (Star Fox force-blanks its framebuffer DMA
via an HDMA-driven $2100 write on the very lines the DMA is filling). crate::dma::Dma
drives that interleave itself from inside run_gp because the bus’s own per-tick HDMA path
is dormant while the bus has lent out its controller. Default u16::MAX so a clockless
unit-test bus never trips the interleave.
Sourcefn visible_height(&self) -> u16
fn visible_height(&self) -> u16
The last visible scanline HDMA services this frame (visible_height), so the in-GP-DMA
interleave knows the visible-line window. Default 0 (unit-test buses run no HDMA).
Sourcefn hdma_last_line(&self) -> u16
fn hdma_last_line(&self) -> u16
The scanline the bus last serviced HDMA for, so the in-GP-DMA interleave resumes from the
bus’s own bookkeeping and hands it back afterwards (no line runs twice, none is skipped).
Default u16::MAX.
Sourcefn set_hdma_last_line(&mut self, _line: u16)
fn set_hdma_last_line(&mut self, _line: u16)
Record that HDMA has now been serviced for line, syncing the bus’s bookkeeping with the
interleave driven from inside run_gp. Default no-op.