pub struct ExLoRom { /* private fields */ }Expand description
ExLoROM (unofficial — no dedicated $xFD5 mode value): the extended LoROM layout for
titles over 4 MiB that keep LoROM’s 32 KiB bank windowing instead of switching to HiROM’s
linear banks.
Banks $80–$FF address the first 4 MiB; banks $00–$7D address the extra (high) 4 MiB —
the same A23-inverted high/low bank split ExHiRom uses, applied to LoROM’s $8000
window instead of HiROM’s full bank.
No real ExLoROM ROM (commercial or homebrew) exists in this project’s local corpus, so this
board has no golden-framebuffer validation — see docs/adr/0003. The formula is sourced
directly from bsnes’s own runtime board database (board: EXLOROM / EXLOROM-RAM,
target-bsnes/resource/system/boards.bml: map address=00-7d:8000-ffff mask=0x808000 base=0x400000 / map address=80-ff:8000-ffff mask=0x808000 base=0x000000), decoded against
Bus::reduce’s bit-packing algorithm (sfc/memory/memory.cpp) rather than guessed — not
from the header-detection heuristic alone. See docs/cart.md §ExLoROM.
Implementations§
Trait Implementations§
Source§impl Board for ExLoRom
impl Board for ExLoRom
Source§fn name(&self) -> &'static str
fn name(&self) -> &'static str
"LoROM", "HiROM+DSP-1".Source§fn map(&self, addr24: u32) -> MappedAddr
fn map(&self, addr24: u32) -> MappedAddr
(bank << 16) | addr to its backing store. The returned
MappedAddr::Rom / MappedAddr::Sram offsets are already folded into range.Source§fn rom(&self) -> &[u8] ⓘ
fn rom(&self) -> &[u8] ⓘ
Source§fn sram(&self) -> &[u8] ⓘ
fn sram(&self) -> &[u8] ⓘ
Source§fn sram_mut(&mut self) -> &mut [u8] ⓘ
fn sram_mut(&mut self) -> &mut [u8] ⓘ
Source§fn coprocessor(&self) -> Coprocessor
fn coprocessor(&self) -> Coprocessor
Coprocessor::None).Source§fn read24(&mut self, addr24: u32) -> u8
fn read24(&mut self, addr24: u32) -> u8
Self::map over the
board’s own storage; coprocessor boards override to intercept their register windows.Source§fn write24(&mut self, addr24: u32, val: u8)
fn write24(&mut self, addr24: u32, val: u8)
Self::map to SRAM only — ROM and open bus are read-only.Source§fn coprocessor_tick(&mut self)
fn coprocessor_tick(&mut self)
advance_master, alongside the PPU dot and the APU’s
SMP-cycle release) — every single tick, unconditionally, on the coprocessor’s divisor —
so a host-driven coprocessor (Super FX/GSU) runs genuinely concurrently with the CPU’s
own subsequent instructions instead of draining an entire Go burst to completion
“atomically” inside the one bus write that armed it. This mirrors ares’s SuperFX : Thread cothread, which the scheduler interleaves with the main CPU at native
master-clock granularity (sfc/coprocessor/superfx/superfx.cpp’s Thread::create +
timing.cpp’s Thread::synchronize after every access) — the CPU can do unrelated
work, or even service a second Go burst, in between two ticks of the first one,
instead of only ever observing the coprocessor’s result after it fully finishes
(Gsu::tick doc has the detail on what is, and isn’t, deferred). Default no-op (base
LoROM/HiROM/ExHiROM have no coprocessor; DSP-n stays RQM-polled, not tick-driven).Source§fn notify_scanline(&mut self)
fn notify_scanline(&mut self)
Source§fn notify_cpu_cycle(&mut self)
fn notify_cpu_cycle(&mut self)
Source§fn notify_dma_channel(&mut self, channel: usize, address: u32, count: u16)
fn notify_dma_channel(&mut self, channel: usize, address: u32, count: u16)
channel’s $43n2-$43n6 source-address/byte-count
registers were just written, reporting the channel’s CURRENT full 24-bit source address
and 16-bit count. Default no-op. The $4300-$437F DMA register file lives in
rustysnes-core::Bus (not routed through Board::read24/write24 at all under normal
SNES addressing), so a board that needs to observe DMA setup — S-DD1’s decompression-
during-DMA hook, which snoops these exact registers on real hardware (ares
sfc/coprocessor/sdd1/sdd1.cpp dmaWrite) — has no other way to see it; this hook is
rustysnes-core’s side of that snoop, called after every relevant register write
regardless of board (cheap no-op for the other 99% of carts).Source§fn irq_pending(&self) -> bool
fn irq_pending(&self) -> bool
false. The bus ORs this with the other IRQ sources.Source§fn debug_gsu_state(&self) -> Option<([u16; 16], u16, u8)>
fn debug_gsu_state(&self) -> Option<([u16; 16], u16, u8)>
Source§fn load_firmware(&mut self, _bytes: &[u8]) -> bool
fn load_firmware(&mut self, _bytes: &[u8]) -> bool
dsp1.rom). Default false — a base
board has no firmware to load. A chip-ROM-dump coprocessor returns true once the dump is
accepted; without it the board is non-functional, never silently degraded (docs/adr/0003).Source§fn firmware_hint(&self) -> Option<&'static str>
fn firmware_hint(&self) -> Option<&'static str>
"dsp2.rom"), if the board knows
exactly which chip dump it needs. Default None — most chip-ROM-dump coprocessors accept
any same-family, same-size dump (DSP-1 accepts either dsp1.rom or dsp1b.rom), so callers
searching a firmware directory should try this exact name FIRST when present: several NEC
DSP chips share an identical firmware byte size (docs/cart.md §“the shared NEC core”), so
trying a same-sized-but-wrong-chip’s dump would silently load the wrong lookup tables/
program into the engine — this hint is what stops that ambiguity for the single-game
variants (DSP-2/4, ST010) that DO need one exact file, not a same-family candidate list.Source§fn coprocessor_host_accesses(&self) -> u64
fn coprocessor_host_accesses(&self) -> u64
0 — base boards have no coprocessor.Source§fn save_state(&self, w: &mut SaveWriter)
fn save_state(&self, w: &mut SaveWriter)
System::save_state already
captures directly, so writing nothing is correct, not merely convenient — restoring such a
board’s post-load state is already exact.Source§fn load_state(&mut self, r: &mut SaveReader<'_>) -> Result<(), SaveStateError>
fn load_state(&mut self, r: &mut SaveReader<'_>) -> Result<(), SaveStateError>
Self::save_state — restore state a matching save_state call wrote.
Default no-op, matching that default. A board overriding one MUST override the other; an
asymmetric pair would silently desync a restored coprocessor from its own register file,
which is exactly the honesty-gate failure mode docs/adr/0003/docs/adr/0006 forbid. Read moreSource§fn has_second_cpu(&self) -> bool
fn has_second_cpu(&self) -> bool
false.Source§fn second_cpu_read(&mut self, addr24: u32) -> u8
fn second_cpu_read(&mut self, addr24: u32) -> u8
Source§fn second_cpu_write(&mut self, addr24: u32, val: u8)
fn second_cpu_write(&mut self, addr24: u32, val: u8)
Source§fn second_cpu_running(&self) -> bool
fn second_cpu_running(&self) -> bool
false.Source§fn second_cpu_take_reset(&mut self) -> bool
fn second_cpu_take_reset(&mut self) -> bool
true exactly once per
edge; core then resets the second CPU. Default false.Source§fn second_cpu_poll_nmi(&mut self) -> bool
fn second_cpu_poll_nmi(&mut self) -> bool
true return). Default false.Source§fn second_cpu_poll_irq(&self) -> bool
fn second_cpu_poll_irq(&self) -> bool
I flag is clear). Default false.Source§fn second_cpu_tick(&mut self, clocks: u32)
fn second_cpu_tick(&mut self, clocks: u32)
clocks of its own master clock.
Default no-op.