pub struct Gsu { /* private fields */ }Expand description
The Super FX GSU core — registers, status, caches, and the ROM/RAM buffer latches.
The board owns the ROM/RAM bytes and supplies them via GsuMem on each run; this struct is
the pure register/state machine (so the whole thing is Clone for save-states).
Implementations§
Source§impl Gsu
impl Gsu
Sourcepub fn new() -> Self
pub fn new() -> Self
Power-on a GSU core (ares GSU::power + SuperFX::power).
§Panics
Never in practice: the only fallible step is sizing the fixed 512-byte cache buffer, which cannot fail for the constant length.
Sourcepub const fn owns_rom(&self) -> bool
pub const fn owns_rom(&self) -> bool
Whether the GSU currently owns the ROM bus (Go + SCMR ROM-on).
Sourcepub const fn owns_ram(&self) -> bool
pub const fn owns_ram(&self) -> bool
Whether the GSU currently owns the RAM bus (Go + SCMR RAM-on).
Sourcepub const fn irq_pending(&self) -> bool
pub const fn irq_pending(&self) -> bool
Whether the GSU is asserting its IRQ line (SFR IRQ flag, gated by CFGR IRQ-mask).
Sourcepub const fn instructions(&self) -> u64
pub const fn instructions(&self) -> u64
The number of instructions executed since power-on.
Sourcepub const fn registers(&self) -> [u16; 16]
pub const fn registers(&self) -> [u16; 16]
The R0-R15 general-purpose register file (R15 is also the program counter).
Sourcepub fn step_one(&mut self, mem: &mut GsuMem<'_>) -> u32
pub fn step_one(&mut self, mem: &mut GsuMem<'_>) -> u32
Drain one bus-access clock checkpoint (ares SuperFX::step/Thread::synchronize
granularity — sfc/coprocessor/superfx/timing.cpp) and report its clock count, running
as many further GSU instructions as needed to produce one. The board’s host loop calls
this in a loop, folding each returned delta into the master clock immediately, so the
PPU/NMI/APU advance in step with the GSU at true per-access granularity instead of
crediting an entire instruction — or an entire multi-instruction burst — all at once
(which let a two-pass render, e.g. a screen split into a left-half then a right-half
Go burst, desync the two halves’ notion of “now”). Each instruction still runs to
completion eagerly inside Gsu::main_step — only the reporting of its accesses to
the caller is paced one at a time, which is externally indistinguishable from true
per-access interleaving because nothing GSU-side reads back caller/Bus state
mid-instruction. Returns 0 once Go clears and there is nothing left queued.
Sourcepub fn tick(&mut self, mem: &mut GsuMem<'_>)
pub fn tick(&mut self, mem: &mut GsuMem<'_>)
Advance by exactly one master clock (ares SuperFX::main, scheduled by its Thread at
native master-clock granularity — sfc/coprocessor/superfx/superfx.cpp). The board’s
host loop calls this from inside the Bus’s own per-master-tick loop, unconditionally,
every single tick — the same place the PPU dot and the APU’s SMP-cycle release happen —
so the GSU genuinely interleaves with the CPU’s own instruction stream instead of a Go
burst draining to completion “atomically” inside the one bus write that armed it (which
left the CPU unable to do any of its own work, or service a second Go burst, until the
first fully finished). owed paces Gsu::step_one’s per-access checkpoints out one
master clock at a time: the instruction’s own side effects still land eagerly the moment
its checkpoint is pulled (no full deferred-commit — see the field doc), but the next
GSU instruction can no longer start until the real number of elapsed master clocks the
current one costs has actually passed, letting the CPU run freely in between.
Sourcepub fn run_until_stopped(&mut self, mem: &mut GsuMem<'_>)
pub fn run_until_stopped(&mut self, mem: &mut GsuMem<'_>)
Run the GSU to completion: step instructions while Go is set, capped against a runaway
program. Called by the board the instant the CPU sets Go (the DSP-1 run_until_rqm
analogue). Returns when the program executes STOP (Go clears) or the cap trips.
Prefer Gsu::step_one driven by the board’s host loop when master-clock-accurate
interleaving matters (e.g. a screen render split across multiple Go bursts within one
displayed frame) — this method is for callers that only need the final result.
Sourcepub fn read_register(&mut self, address: u16) -> u8
pub fn read_register(&mut self, address: u16) -> u8
Host read of a GSU register (address = $0000-$02FF window offset; ares readIO).
Sourcepub fn write_register(&mut self, address: u16, data: u8) -> bool
pub fn write_register(&mut self, address: u16, data: u8) -> bool
Host write of a GSU register. Returns true if this write set the Go flag (0->1), so the
board knows to run the GSU to completion (ares writeIO).
Sourcepub fn save_state(&self, w: &mut SaveWriter)
pub fn save_state(&self, w: &mut SaveWriter)
Write this core’s full mutable state — every register, the status/control fields, both
bus-buffer latches, the opcode cache, the plot pixel cache, the liveness counters, and the
in-flight per-access checkpoint queue (pending_clocks/pending_idx/owed) — into a
"GSU0" section. There is no firmware/ROM byte here to exclude: the GSU’s program lives in
the cart’s own ROM, which System::save_state captures separately (docs/adr/0003). The
checkpoint queue matters because Self::tick-driven (master-clock-interleaved) execution
can leave a Go burst genuinely mid-flight at any save point, unlike the run-to-completion
Self::run_until_stopped path, which always drains it back to empty first.
Sourcepub fn load_state(
&mut self,
r: &mut SaveReader<'_>,
) -> Result<(), SaveStateError>
pub fn load_state( &mut self, r: &mut SaveReader<'_>, ) -> Result<(), SaveStateError>
The inverse of Self::save_state.
§Errors
SaveStateError on truncated/corrupt input, a section with unconsumed trailing bytes, or
SaveStateError::Invalid if the saved pending_clocks length exceeds
MAX_SAVED_PENDING_CLOCKS or pending_idx exceeds the restored queue’s length
(both would otherwise let a corrupted save-state desync Self::step_one’s cursor).
sreg/dreg are masked to 4 bits — they index the 16-entry register file directly.