pub trait Mapper: Send {
Show 58 methods
// Required methods
fn cpu_read(&mut self, addr: u16) -> u8;
fn cpu_write(&mut self, addr: u16, value: u8);
fn ppu_read(&mut self, addr: u16) -> u8;
fn ppu_write(&mut self, addr: u16, value: u8);
fn current_mirroring(&self) -> Mirroring;
fn save_state(&self) -> Vec<u8> ⓘ;
fn load_state(&mut self, data: &[u8]) -> Result<(), MapperError>;
// Provided methods
fn cpu_read_unmapped(&self, addr: u16) -> bool { ... }
fn notify_floating_read(&mut self, _addr: u16, _value: u8) { ... }
fn notify_ppu_register_write(&mut self, _addr: u16, _value: u8) { ... }
fn nametable_unfolded(&self) -> bool { ... }
fn cpu_read_driven_mask(&self, _addr: u16) -> u8 { ... }
fn ppu_read_sprite(&mut self, addr: u16) -> u8 { ... }
fn chr_phys(&self, _addr: u16) -> Option<u32> { ... }
fn chr_reads_are_pure(&self) -> bool { ... }
fn set_mmc3_revision_override(
&mut self,
_revision: Option<Mmc3Revision>,
) -> bool { ... }
fn nametable_fetch(&mut self, _addr: u16) -> Option<u8> { ... }
fn nametable_write(&mut self, _addr: u16, _value: u8) -> bool { ... }
fn peek_ex_attribute(&mut self, _v: u16) -> Option<ExAttribute> { ... }
fn bg_split_state(
&mut self,
_scanline_y: u16,
_coarse_x: u16,
) -> Option<BgSplitState> { ... }
fn nametable_address(&self, addr: u16) -> u16 { ... }
fn notify_a12(&mut self, _level: bool) { ... }
fn notify_a12_at_sub_dot(&mut self, level: bool, _sub_dot: u8) { ... }
fn notify_cpu_cycle(&mut self) { ... }
fn reset(&mut self) { ... }
fn notify_frame_event(&mut self, _events: MapperFrameEvents) { ... }
fn notify_scanline_start(&mut self) { ... }
fn notify_vblank(&mut self) { ... }
fn enable_vs_dual_wram(&mut self) { ... }
fn set_vs_dual_sub(&mut self) { ... }
fn drain_vs_dual_wram_writes(&mut self, dst: &mut Vec<(u16, u8)>) { ... }
fn take_vs_dual_wram_writes(&mut self) -> Vec<(u16, u8)> { ... }
fn apply_vs_dual_wram_write(&mut self, offset: u16, value: u8) { ... }
fn take_vs_dual_wram(&mut self) -> Option<Box<[u8]>> { ... }
fn set_vs_dual_wram(&mut self, wram: Box<[u8]>) { ... }
fn irq_pending(&self) -> bool { ... }
fn irq_acknowledge(&mut self) { ... }
fn mix_audio(&mut self) -> i32 { ... }
fn caps(&self) -> MapperCaps { ... }
fn has_hardwired_mirroring(&self) -> bool { ... }
fn disk_side_count(&self) -> usize { ... }
fn inserted_disk_side(&self) -> Option<usize> { ... }
fn set_disk_side(&mut self, _side: Option<usize>) { ... }
fn sram(&self) -> &[u8] ⓘ { ... }
fn sram_mut(&mut self) -> &mut [u8] ⓘ { ... }
fn save_data(&self) -> &[u8] ⓘ { ... }
fn save_data_mut(&mut self) -> &mut [u8] ⓘ { ... }
fn clear_save_data(&mut self) { ... }
fn enable_fds_trace(&mut self) { ... }
fn take_fds_trace(&mut self) -> Vec<FdsTraceRec> { ... }
fn disk_image_bytes(&self) -> Vec<u8> ⓘ { ... }
fn disk_is_dirty(&self) -> bool { ... }
fn clear_disk_dirty(&mut self) { ... }
fn set_disk_write_protected(&mut self, _protected: bool) { ... }
fn nsf_song_count(&self) -> u8 { ... }
fn nsf_current_song(&self) -> u8 { ... }
fn nsf_set_song(&mut self, _song: u8) -> bool { ... }
fn debug_info(&self) -> MapperDebugInfo { ... }
}Expand description
Trait implemented by every cartridge mapper.
Visible read/write addresses follow docs/architecture.md
§Per-memory-access fanout. The PPU bus call covers the whole
$0000-$3FFF address space because nametable mirroring is mapper-controlled
(see docs/mappers.md §Mirroring).
IRQ-emitting mappers signal pending IRQs via Mapper::irq_pending; the
CPU bus polls this on the same cycle it polls APU/external IRQs.
All trait methods are &mut self because every mapper has at least some
internal state — open-bus latch, bank registers, IRQ counters, etc. —
even on an apparent read.
Required Methods§
Sourcefn cpu_write(&mut self, addr: u16, value: u8)
fn cpu_write(&mut self, addr: u16, value: u8)
Write a byte to the CPU address space $4020-$FFFF.
Sourcefn ppu_read(&mut self, addr: u16) -> u8
fn ppu_read(&mut self, addr: u16) -> u8
Read a byte from the PPU address space $0000-$3FFF (pattern table
- nametable mirror window). Used as the BG-side / generic fetch path.
Sourcefn ppu_write(&mut self, addr: u16, value: u8)
fn ppu_write(&mut self, addr: u16, value: u8)
Write a byte to the PPU address space $0000-$3FFF.
Sourcefn current_mirroring(&self) -> Mirroring
fn current_mirroring(&self) -> Mirroring
Returns the mapper’s current effective mirroring layout.
Most mappers report the static mirroring set in the cartridge header;
mappers with runtime mirroring control (MMC1, MMC3, AxROM, …) report
the live state.
Sourcefn save_state(&self) -> Vec<u8> ⓘ
fn save_state(&self) -> Vec<u8> ⓘ
Encode the mapper’s mutable state into a tagged save-state blob.
Sourcefn load_state(&mut self, data: &[u8]) -> Result<(), MapperError>
fn load_state(&mut self, data: &[u8]) -> Result<(), MapperError>
Decode a previously Mapper::save_state blob back into the mapper.
§Errors
Returns MapperError when the blob is truncated, has the wrong
version tag, or otherwise fails internal consistency checks.
Provided Methods§
Sourcefn cpu_read_unmapped(&self, addr: u16) -> bool
fn cpu_read_unmapped(&self, addr: u16) -> bool
Returns true when addr is not wired to mapper-resident
memory — i.e. when cpu_read(addr) returns junk and the bus
should fall through to the open-bus latch instead of overwriting
it. The CPU databus is left floating in this case, so the most
recently driven byte stays visible to the next read.
Default impl covers stock NROM-class boards: the entire
$4020-$5FFF window is unmapped (no PRG-RAM, no mapper
registers), and $6000-$7FFF is unmapped exactly when the board has
no save RAM to put there (Self::sram is empty). $8000-$FFFF is
PRG-ROM and always mapped. Mappers that DO map any subset of
$4020-$5FFF (MMC5 audio + ExRAM, FME-7 IRQ control, VRC family
register banks, etc.) must override this to return false for their
mapped sub-ranges so the bus uses the real value; a board with ROM or
readable registers at $6000-$7FFF but no save RAM must override it
for that window.
The $6000-$7FFF rule is v2.7.2 (core audit §5.5). Before it, every
board without RAM there read a made-up $00 instead of open bus; a
sweep over every mapper number found 205 board variants doing so
(tests/prg_ram_window_open_bus.rs). It leans on v2.7.1’s contract that
every board holding save RAM exposes it through sram()
(tests/battery_sram_exposed.rs), and the same test fails any board
the rule would float while it actually drives data there.
This is the canonical hardware oracle for AccuracyCoin’s
CPU Behavior :: Open Bus Test 1 (LDA $5000 should read
$50, not $00).
Sourcefn notify_floating_read(&mut self, _addr: u16, _value: u8)
fn notify_floating_read(&mut self, _addr: u16, _value: u8)
A CPU read the mapper declined (Self::cpu_read_unmapped) has just
completed, and value is what floated on the bus.
Only GTROM (mapper 111) uses it: its register latches on any CPU access
to its window, so “reading from the register effectively writes the
value of open bus” (nesdev_wiki/output/GTROM.md). Called only on the
unmapped path, so PRG fetches never pay for it. Default: nothing.
Added in v2.9.6.
Sourcefn notify_ppu_register_write(&mut self, _addr: u16, _value: u8)
fn notify_ppu_register_write(&mut self, _addr: u16, _value: u8)
The CPU wrote value to the PPU register window ($2000-$3FFF), at
the undecoded address addr.
Only MMC5 (mapper 5) uses it: the chip is “known to listen to the same address
as the PPU to find out when to enable the 8x16 sprite mode”, decoding
$2000 and $2001 fully, so a write to a mirror such as $2008 is
not seen (nesdev_wiki/output/MMC5.md). The address is passed
undecoded for that reason. Called once per PPU register write, never
per cycle. Default: nothing. Added in v2.9.9.
Sourcefn nametable_unfolded(&self) -> bool
fn nametable_unfolded(&self) -> bool
Whether PPU $3000-$3EFF is independent RAM on this cartridge rather
than a mirror of $2000-$2EFF.
When true, Self::nametable_fetch and Self::nametable_write
receive $3000-$3EFF unfolded. Two boards need it: GTROM’s “bonus RAM”
(GTROM.md) and UNROM 512’s four-screen board (UNROM_512.md), whose
nametable RAM covers the whole $2000-$3EFF window. Only $2007
accesses reach $3xxx (rendering fetches never do), and the PPU asks
only for those, so rendering pays nothing. Default: false, the
console’s own mirroring. Added in v2.9.6.
Sourcefn cpu_read_driven_mask(&self, _addr: u16) -> u8
fn cpu_read_driven_mask(&self, _addr: u16) -> u8
Which data bits a mapped read in the register window ($4020-$5FFF)
actually drives; the rest float and keep the bus’s open-bus value.
A register that drives only part of the byte is common on cheap ASICs:
the Sachen SA-020A (mappers 150 / 243) returns its 3-bit registers on
D2-D0 and leaves D7-D3 floating (nesdev_wiki/INES_Mapper_150.xhtml).
Returning the undriven bits as 0 from Self::cpu_read would let the
bus latch those zeros; this mask lets it keep what was floating there
instead (core audit §4.5).
Consulted only for $4020-$5FFF, so it costs nothing on the
$6000-$FFFF fetches that dominate CPU time. Default: all 8 bits.
Sourcefn ppu_read_sprite(&mut self, addr: u16) -> u8
fn ppu_read_sprite(&mut self, addr: u16) -> u8
Read a byte from the PPU pattern-table window ($0000-$1FFF) on
behalf of a sprite tile fetch. MMC5 in 8x16 sprite mode uses a
separate set of CHR bank registers ($5120-$5127) for sprite
fetches; other mappers default to forwarding to Mapper::ppu_read.
Sourcefn chr_phys(&self, _addr: u16) -> Option<u32>
fn chr_phys(&self, _addr: u16) -> Option<u32>
HD-pack tile identity: the ABSOLUTE post-banking offset into CHR-ROM for a
pattern-space address $0000-$1FFF (Some(offset)), or None for CHR-RAM
(content-hashed instead). tile_index = offset / 16 is the key Mesen uses
for CHR-ROM <tile> replacements. Default None so an unported mapper
falls back to the content-hash path (no worse than before); the common
CHR-ROM mappers override it by exposing their internal CHR mapping.
Sourcefn chr_reads_are_pure(&self) -> bool
fn chr_reads_are_pure(&self) -> bool
v3.1.0 (T-SPRITE-LIMIT) — whether Self::ppu_read and
Self::ppu_read_sprite on $0000-$1FFF change nothing but return a
byte. true (the default) lets the PPU’s “disable sprite limit” option
make extra, display-only pattern reads on this board.
Override to false for any board whose CHR read has an effect: a latch
that switches banks on a tile (MMC2, MMC4), an IRQ counter clocked by
reads (the J.Y. ASIC), or address bits latched from the read (Bandai
96, Nanjing 163). every_board_that_claims_pure_chr_reads_has_them
checks the claim against save_state for every mapper id, so a new
impure board that keeps the default fails a test rather than letting
the option change emulation.
Sourcefn set_mmc3_revision_override(
&mut self,
_revision: Option<Mmc3Revision>,
) -> bool
fn set_mmc3_revision_override( &mut self, _revision: Option<Mmc3Revision>, ) -> bool
v3.1.0 (T-MMC3-NEC-OVERRIDE, ACC-13) — force an MMC3’s IRQ revision
(Some), or return to the one its header selected (None). Returns
whether this board is an MMC3 that applied it; every other board
ignores it (the default). Lets an iNES 1.0 dump, which cannot name its
MMC3 revision, run under the alternate (Nec) behaviour.
Sourcefn nametable_fetch(&mut self, _addr: u16) -> Option<u8>
fn nametable_fetch(&mut self, _addr: u16) -> Option<u8>
Optionally synthesize a nametable byte for addr ($2000-$3EFF).
When the mapper returns Some(v), the PPU uses v directly and
skips its CIRAM read. MMC5 uses this for “fill mode” ($5105
per-1KiB selector value 0b11), where every nametable-byte fetch
returns the fill tile ($5106) and every attribute-byte fetch
returns a 2-bit attribute ($5107) replicated 4 ways.
Default returns None (mapper does not synthesize; PPU reads CIRAM).
Sourcefn nametable_write(&mut self, _addr: u16, _value: u8) -> bool
fn nametable_write(&mut self, _addr: u16, _value: u8) -> bool
Optionally absorb a nametable write for addr ($2000-$3EFF) directly
into mapper-resident storage.
Returns true if the mapper consumed the write (PPU should NOT also
write its CIRAM). MMC5 uses this for ExRAM-mapped nametables and for
fill mode (where writes are silently dropped).
Default returns false (PPU continues with the CIRAM write path).
Sourcefn peek_ex_attribute(&mut self, _v: u16) -> Option<ExAttribute>
fn peek_ex_attribute(&mut self, _v: u16) -> Option<ExAttribute>
Optionally provide per-tile extended attribute + CHR-bank override for the BG tile currently being fetched.
v is the PPU’s loopy-v register value at NT-byte fetch time
(fetch_nt); the lower 12 bits encode the 32x30 tile coordinate.
MMC5 in $5104 mode 01 returns Some here. The PPU uses the
returned palette to override the AT byte. The mapper itself caches
the chr_bank internally so that the subsequent BG pattern fetches
(ppu_read calls during the same 8-dot fetch group) consult it
instead of the standard BG bank registers.
Default returns None (no extended attribute mode active).
Sourcefn bg_split_state(
&mut self,
_scanline_y: u16,
_coarse_x: u16,
) -> Option<BgSplitState>
fn bg_split_state( &mut self, _scanline_y: u16, _coarse_x: u16, ) -> Option<BgSplitState>
Optionally redirect a BG fetch group into a vertical split-screen “alt region”.
Called by the PPU at the NT-byte fetch boundary of each 8-dot BG
fetch group, once per tile column (32 times per visible scanline).
scanline_y is the current visible-scanline index in 0..=239 (the
pre-render line passes 0 here to keep the path branchless). coarse_x
is the loopy-v coarse-X (0..=31) for the tile about to be fetched.
Returning Some(state) instructs the PPU to use the supplied
nt_addr / at_addr / fine_y instead of those derived from v,
and instructs the mapper to internally latch the CHR bank for the
pattern fetches that immediately follow.
Default returns None.
Sourcefn nametable_address(&self, addr: u16) -> u16
fn nametable_address(&self, addr: u16) -> u16
Resolve a nametable address in $2000-$3EFF to a CIRAM offset in
0..0x800. The PPU owns the 2 KiB CIRAM and uses this hook to apply
per-mapper mirroring without giving the mapper direct access to the
console-side VRAM.
Default impl applies the mirroring reported by Mapper::current_mirroring
via crate::Mirroring::physical_bank. Mappers with on-cart 4-screen
VRAM (Gauntlet, Rad Racer II) can override this; the lockstep bus
will trampoline the read/write back through the mapper for any offset
outside 0..0x800 if needed (Phase 4).
Sourcefn notify_a12(&mut self, _level: bool)
fn notify_a12(&mut self, _level: bool)
Notify of a PPU A12 line transition. Default no-op; MMC3 / MMC5 override this for IRQ counter clocking.
Sourcefn notify_a12_at_sub_dot(&mut self, level: bool, _sub_dot: u8)
fn notify_a12_at_sub_dot(&mut self, level: bool, _sub_dot: u8)
Notify of a PPU A12 line transition with the current sub-dot of
the host CPU cycle (0 / 1 = M2-low half; 2 = M2-high half).
Default impl falls through to Self::notify_a12 so existing
mappers compile unchanged; MMC3 overrides this for the
M2-phase-aware IRQ-output propagation delay required by
mmc3_test_2/4-scanline_timing sub-test #3 (C1 step B4 successor).
Sourcefn notify_cpu_cycle(&mut self)
fn notify_cpu_cycle(&mut self)
Notify of a CPU cycle. Default no-op; VRC2/4/6, FME-7, Namco 163 override this for IRQ counter clocking.
Sourcefn reset(&mut self)
fn reset(&mut self)
The console’s RESET button (a soft reset, not a power cycle).
A cartridge sees reset only on the boards that wire the CIC’s reset
line, or /RESET itself, into their logic, so the default does nothing
and almost every board keeps its registers across a reset, exactly as
on a console. The boards that clear something are documented per page:
mapper 37’s outer latch (INES_Mapper_037.md), mapper 45’s outer
registers, NES-EVENT’s PRG lock (mapper 105), the 76-in-1 BMC’s two
registers (mapper 226, since v2.9.8), and Action 52’s register
(mapper 228). Added in v2.9.6; a power cycle rebuilds the mapper
instead and never calls this.
Sourcefn notify_frame_event(&mut self, _events: MapperFrameEvents)
fn notify_frame_event(&mut self, _events: MapperFrameEvents)
Notify the mapper of the APU frame-counter events fired on the current CPU cycle (quarter-frame envelope clock, half-frame length clock). Only on-cart audio extensions that re-use the 2A03 frame counter cadence need to handle this (MMC5 audio’s two pulse channels). Default no-op.
Sourcefn notify_scanline_start(&mut self)
fn notify_scanline_start(&mut self)
Notify the mapper that the PPU is starting a new rendered scanline.
Called by the PPU at the start of each visible scanline (and the pre-render line) before any tile fetches happen. MMC5 uses this to drive its scanline IRQ counter (which clocks at PPU cycle 4 of each rendered line — different from MMC3’s A12-edge-driven counter).
Default is a no-op; only mappers with scanline-counter IRQs override.
Sourcefn notify_vblank(&mut self)
fn notify_vblank(&mut self)
Notify the mapper that the PPU has entered vertical blank.
MMC5 uses this to clear its “in-frame” flag (bit 6 of $5204).
Default no-op.
Sourcefn enable_vs_dual_wram(&mut self)
fn enable_vs_dual_wram(&mut self)
v2.0.0 beta.5 (Vs. DualSystem): provision the board’s shared 2 KiB
work RAM at $6000-$7FFF (mirrored across the 8 KiB window — MAME
vsnes.cpp: map(0x6000, 0x67ff).mirror(0x1800).ram()). Only the
Vs. System board (mapper 99) implements this — the DualSystem
cabinets carry a 2 KiB RAM shared between the two consoles, absent
on UniSystem carts (whose $6000 window stays open bus,
byte-identically). Called by the VsDualSystem wrapper at
construction on both consoles: each console holds its own COPY, and
the wrapper converges the copies by draining the write log (below)
after every stepped instruction — MAME’s fully-shared
.share("nvram") model at soft-lockstep granularity. Default no-op.
Sourcefn set_vs_dual_sub(&mut self)
fn set_vs_dual_sub(&mut self)
v2.0.0 beta.5 (Vs. DualSystem): mark this mapper instance as the
cabinet’s SUB console — it banks the second 32 KiB PRG half and the
upper CHR pages (the two CPUs run DIFFERENT programs; MAME
balonfgt loads distinct sub-region ROMs, Mesen2 uses
prgOuter = IsVsMainConsole() ? 0 : 4). Applied by the
VsDualSystem wrapper at construction, like the bus’s sub
identity. Default no-op.
Sourcefn drain_vs_dual_wram_writes(&mut self, dst: &mut Vec<(u16, u8)>)
fn drain_vs_dual_wram_writes(&mut self, dst: &mut Vec<(u16, u8)>)
v2.0.0 beta.5 (Vs. DualSystem): drain this console’s shared-WRAM
write log — every (offset, value) the CPU wrote to the window
since the last drain, in order — by APPENDING into dst (never
clearing it first). The wrapper replays them into the partner’s
copy (Self::apply_vs_dual_wram_write), which is what makes the
RAM behave as ONE simultaneously-shared memory (MAME’s model;
nesdev also documents a $4016-bit-1 access mux, but MAME — where
the four DualSystem games verifiably run — shares the RAM
unconditionally, and Balloon Fight’s boot handshake requires the
partner to see writes made while the mux would deny it access).
Implementations MUST drain their internal log via Vec::drain
(or equivalent) rather than replacing it, so the log’s own
allocated capacity is retained across calls — pump_comms calls
this after EVERY stepped instruction on a DualSystem cart, so a
reallocating drain here is a real hot-path allocation, not a
theoretical one. Default: no-op (boards without the dual WRAM).
Sourcefn take_vs_dual_wram_writes(&mut self) -> Vec<(u16, u8)>
fn take_vs_dual_wram_writes(&mut self) -> Vec<(u16, u8)>
Convenience wrapper around Self::drain_vs_dual_wram_writes for
callers that don’t already hold a reusable buffer (diagnostics,
tests) — NOT used by the hot pump_comms path, which owns and
reuses its own scratch buffer instead.
Sourcefn apply_vs_dual_wram_write(&mut self, offset: u16, value: u8)
fn apply_vs_dual_wram_write(&mut self, offset: u16, value: u8)
v2.0.0 beta.5 (Vs. DualSystem): replay one partner-console write
into this console’s copy of the shared WRAM (see
Self::take_vs_dual_wram_writes). Does NOT re-log the write (no
echo loop). Default no-op.
Sourcefn take_vs_dual_wram(&mut self) -> Option<Box<[u8]>>
fn take_vs_dual_wram(&mut self) -> Option<Box<[u8]>>
v2.0.0 beta.5 (Vs. DualSystem): take the console’s shared-WRAM copy
(used by the wrapper’s snapshot-restore normalization — the two
copies are re-converged from one buffer after a restore). Returns
None on boards without the dual WRAM. Default None.
Sourcefn set_vs_dual_wram(&mut self, wram: Box<[u8]>)
fn set_vs_dual_wram(&mut self, wram: Box<[u8]>)
v2.0.0 beta.5 (Vs. DualSystem): install a shared-WRAM copy (the
other half of the restore normalization). Default no-op.
Sourcefn irq_pending(&self) -> bool
fn irq_pending(&self) -> bool
Returns true if the mapper is currently asserting an IRQ.
Sourcefn irq_acknowledge(&mut self)
fn irq_acknowledge(&mut self)
Acknowledge a pending IRQ. Default no-op; mappers that latch IRQ state override this.
Sourcefn mix_audio(&mut self) -> i32
fn mix_audio(&mut self) -> i32
Return one signed audio sample for mappers with on-cart audio (VRC6/7, MMC5, Sunsoft 5B, Namco 163, FDS). Default returns silence.
i32, widened from i16 in v2.2.3 (A1). The bus scales this by
/ 65536.0 into roughly the same [-0.5, 0.5] range as the APU mixer’s
own output, so the old i16 return capped a chip’s representable level
at 32767 / 65536 ≈ 0.5. That was fine for every board except the
Sunsoft 5B, whose logarithmic DAC needs ~3.6x the 2A03 pulse at full
volume — and three simultaneous full-volume tones (Gimmick!, Hebereke)
several times more. The 5B’s absolute level was therefore a documented,
deliberately un-calibrated gap purely because the return type could not
hold it. Widening the type is what unblocks it; see
docs/accuracy-ledger.md §Expansion-audio levels and SUNSOFT5B_MIX_SCALE.
Boards other than the 5B return exactly the values they always did — the widening is representational only and changes no mixed output.
Sourcefn caps(&self) -> MapperCaps
fn caps(&self) -> MapperCaps
v2.8.0 Phase 4 — the mapper’s per-CPU-cycle capability flags.
The bus fans four virtual calls out to the mapper EVERY CPU cycle
(~30 k/frame each): Self::notify_cpu_cycle, Self::mix_audio,
Self::notify_frame_event, and Self::irq_pending. For most
boards all four are the default no-ops, so the bus caches these
flags at construction and skips the dispatch entirely.
The contract is mechanical: a flag may be false ONLY when the
mapper does not override the corresponding default method (skipping
a default no-op is provably byte-identical). The default returns
MapperCaps::ALL, so an unannotated mapper keeps every dispatch —
always correct, just slower.
Sourcefn has_hardwired_mirroring(&self) -> bool
fn has_hardwired_mirroring(&self) -> bool
Whether this mapper’s nametable mirroring is hardwired by the cartridge (solder pads / the iNES header bit) rather than controlled by the mapper’s own registers at runtime.
This gates whether an external mirroring correction (e.g. a per-game
database entry, applied via Nes::set_mirroring_override) may
be honored. A static override is only meaningful for a hardwired board
whose header bit can be wrong; forcing a static mirroring onto a mapper
that switches mirroring itself (MMC1/3/5, AxROM, VRC, Sunsoft FME-7,
Namco 163, …) corrupts its rendering — e.g. AxROM‘s mid-frame
single-screen A↔B flip that draws Wizards & Warriors’ status bar, whose
GoodNES-derived game-DB row lists a spurious Horizontal that, when
force-applied, blanks the bottom of the screen and hangs the game.
Default: false (assume the mapper controls its own mirroring). This is
the safe default — a mapper that omits an annotation merely declines
a rarely-needed, cosmetic header correction; it can never break a working
game. Only the classic fixed-mirroring discrete boards (NROM, UxROM,
CNROM, GxROM, …) override this to true.
Sourcefn disk_side_count(&self) -> usize
fn disk_side_count(&self) -> usize
Number of disk sides in the inserted image (0 for non-FDS mappers).
Sourcefn inserted_disk_side(&self) -> Option<usize>
fn inserted_disk_side(&self) -> Option<usize>
The currently inserted disk side index, or None when ejected (or for
non-FDS mappers).
Sourcefn set_disk_side(&mut self, _side: Option<usize>)
fn set_disk_side(&mut self, _side: Option<usize>)
Insert disk side i (Some) or eject the disk (None). No-op for
non-FDS mappers; an out-of-range index is ignored by the FDS device.
Sourcefn sram(&self) -> &[u8] ⓘ
fn sram(&self) -> &[u8] ⓘ
Returns a reference to the mapper’s internal SRAM/PRG-RAM. By default, returns an empty slice if unsupported.
Sourcefn sram_mut(&mut self) -> &mut [u8] ⓘ
fn sram_mut(&mut self) -> &mut [u8] ⓘ
Returns a mutable reference to the mapper’s internal SRAM/PRG-RAM. By default, returns an empty mutable slice if unsupported.
Sourcefn save_data(&self) -> &[u8] ⓘ
fn save_data(&self) -> &[u8] ⓘ
The cartridge’s non-volatile data: what a battery save persists and a
power cycle keeps. For almost every board that is its battery-backed
RAM, Self::sram, and the default says so.
Self-flashable boards differ (v2.9.6). GTROM and a flashable UNROM 512
save by rewriting their own PRG flash, so their save is the flash image,
and they have no RAM at $6000 at all. Keeping the two apart is the
point. sram() goes on meaning “the RAM in the $6000 window”, which
the open-bus rule, the libretro memory map and RetroAchievements all
rely on. Only the save paths read this.
Sourcefn save_data_mut(&mut self) -> &mut [u8] ⓘ
fn save_data_mut(&mut self) -> &mut [u8] ⓘ
Mutable Self::save_data, for loading a save.
Sourcefn clear_save_data(&mut self)
fn clear_save_data(&mut self)
Return the save data to the state of a cartridge that has never been
saved to. That is zeroed RAM by default. On a flash board it is the PRG
image as loaded, since a zero-filled flash would be a ROM with no
program in it. A power-on movie calls this (power_on_for_movie).
Sourcefn enable_fds_trace(&mut self)
fn enable_fds_trace(&mut self)
Start recording the diagnostic FDS read-stream trace (off by default;
observation-only). No-op for non-FDS mappers. See crate::FdsTraceRec.
Sourcefn take_fds_trace(&mut self) -> Vec<FdsTraceRec>
fn take_fds_trace(&mut self) -> Vec<FdsTraceRec>
Drain the accumulated FDS read-stream trace records (empty for non-FDS mappers / when tracing was never enabled).
Sourcefn disk_image_bytes(&self) -> Vec<u8> ⓘ
fn disk_image_bytes(&self) -> Vec<u8> ⓘ
Re-serialize the (possibly-modified) disk image to its byte layout for host persistence. Returns an empty vector for non-FDS mappers.
Sourcefn disk_is_dirty(&self) -> bool
fn disk_is_dirty(&self) -> bool
Whether the disk image has unsaved writes. Always false for non-FDS
mappers.
Sourcefn clear_disk_dirty(&mut self)
fn clear_disk_dirty(&mut self)
Clear the disk dirty flag (a host calls this after persisting). No-op for non-FDS mappers.
Sourcefn set_disk_write_protected(&mut self, _protected: bool)
fn set_disk_write_protected(&mut self, _protected: bool)
Mark the inserted disk read-only (true) or writable (false). No-op
for non-FDS mappers.
Sourcefn nsf_song_count(&self) -> u8
fn nsf_song_count(&self) -> u8
Number of selectable songs (0 for a non-NSF mapper).
Sourcefn nsf_current_song(&self) -> u8
fn nsf_current_song(&self) -> u8
The currently-selected 0-based song (0 for a non-NSF mapper).
Sourcefn nsf_set_song(&mut self, _song: u8) -> bool
fn nsf_set_song(&mut self, _song: u8) -> bool
Select a 0-based song. Returns true if this is an NSF mapper (so the
caller knows to re-run the reset that re-vectors into the driver’s
init). Default no-op returning false.
Sourcefn debug_info(&self) -> MapperDebugInfo
fn debug_info(&self) -> MapperDebugInfo
Surface read-only debug info for the UI. Override per mapper to expose bank registers, IRQ counters, etc. Default returns a minimal entry naming the mapper id.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".