pub struct LockstepBus { /* private fields */ }Expand description
Lockstep bus.
Owns the entire emulator’s mutable state. The CPU borrows &mut LockstepBus
during Cpu::step. The PPU and APU are ticked from the bus’s
cpu_read/cpu_write implementations (3 dots per CPU cycle, NTSC; APU
every CPU cycle).
Implementations§
Source§impl LockstepBus
impl LockstepBus
Sourcepub fn new(rom_bytes: &[u8]) -> Result<Self, RomError>
pub fn new(rom_bytes: &[u8]) -> Result<Self, RomError>
Construct from a parsed ROM with a default 44.1 kHz audio sample rate.
§Errors
Returns the underlying [RomError] if the bytes don’t parse.
Sourcepub fn with_sample_rate(
rom_bytes: &[u8],
sample_rate: u32,
) -> Result<Self, RomError>
pub fn with_sample_rate( rom_bytes: &[u8], sample_rate: u32, ) -> Result<Self, RomError>
Construct with an explicit audio sample rate.
§Errors
Returns the underlying [RomError] if the bytes don’t parse.
Sourcepub fn with_disk(
disk_bytes: &[u8],
bios_bytes: &[u8],
sample_rate: u32,
) -> Result<Self, RomError>
pub fn with_disk( disk_bytes: &[u8], bios_bytes: &[u8], sample_rate: u32, ) -> Result<Self, RomError>
Construct a Famicom Disk System bus from a .fds disk image and a
user-supplied 8 KiB BIOS (disksys.rom).
Parses the disk container ([rustynes_mappers::parse_fds]), constructs the
FDS device ([rustynes_mappers::Fds]) as the bus’s Box<dyn Mapper>, and
wires the bus exactly like a cartridge build (shared internal
from_cart_and_mapper). The FDS is NTSC/Famicom hardware, so the
synthetic [Cartridge] metadata reports [rustynes_mappers::Region::Ntsc].
§Errors
Returns [RomError] if the disk image is unparseable, or the BIOS is not
exactly 8 KiB.
Sourcepub fn with_nsf(nsf_bytes: &[u8], sample_rate: u32) -> Result<Self, RomError>
pub fn with_nsf(nsf_bytes: &[u8], sample_rate: u32) -> Result<Self, RomError>
Build a bus that plays an NSF music file. Parses the .nsf, builds an
[rustynes_mappers::NsfMapper] (a synthetic driver + the program image)
as the bus’s Box<dyn Mapper>, and reports synthetic NTSC cartridge
metadata (the file carries no CHR / PPU program).
§Errors
Returns [RomError::InvalidConfig] when the NSF header is malformed.
Sourcepub fn reset(&mut self)
pub fn reset(&mut self)
Reset (warm). Defers to Ppu::reset and clears DMA state. CPU is
reset by the caller.
Sourcepub fn power_cycle(&mut self)
pub fn power_cycle(&mut self)
Power-cycle. Zeroes RAM and resets all state. Caller resets the CPU.
Sourcepub fn randomize_power_on_ram(&mut self, seed: u64)
pub fn randomize_power_on_ram(&mut self, seed: u64)
Developer-mode power-on randomization (Phase 7 / T-72-005).
Fills the 2 KiB CPU work RAM and the external open-bus latch from a
deterministic xorshift64 PRNG. Real hardware powers up with
unreliable RAM (see nesdev “CPU power up state”); games that depend on
a particular post-power-on RAM pattern are buggy, and this option
surfaces such bugs the way Mesen2’s “randomize RAM on power-on” does.
The fill is seeded and deterministic — the same seed always
yields the same power-on state, so the
same seed + ROM + input ⇒ bit-identical determinism contract (and
therefore save-state round-trip and the regression oracle) is
preserved. CI and tests use the default (zeroed) path; this is opt-in
via crate::Nes::from_rom_with_power_on_seed.
CPU/PPU phase alignment and DMA get/put phase are intentionally not
randomized here: the lockstep scheduler’s phase is deterministic by
design and randomizing it is entangled with the v2.0 master-clock
scheduling refactor (see docs/audit/phase-7-assessment-2026-05-24.md).
Sourcepub fn framebuffer(&self) -> &[u8] ⓘ
pub fn framebuffer(&self) -> &[u8] ⓘ
Borrow the framebuffer (RGBA8, 256x240).
Sourcepub fn debug_set_framebuffer(&mut self, rgba: &[u8])
pub fn debug_set_framebuffer(&mut self, rgba: &[u8])
v1.7.0 “Forge” Workstream B (B3) — overwrite the RGBA8 output framebuffer
(the Lua emu:setScreenBuffer). Output-only; see
[rustynes_ppu::Ppu::debug_set_framebuffer]. debug-hooks-gated.
Sourcepub fn index_framebuffer(&self) -> &[u16]
pub fn index_framebuffer(&self) -> &[u16]
Borrow the parallel palette-index framebuffer (256x240 u16s) for the
NES_NTSC composite filter. See [rustynes_ppu::Ppu::index_framebuffer].
Sourcepub fn hd_tile_source(&self) -> &[HdTileSource]
pub fn hd_tile_source(&self) -> &[HdTileSource]
v1.2.0 C3 (hd-pack) — borrow the per-pixel HD-pack tile-source buffer.
See [rustynes_ppu::Ppu::hd_tile_source]. Output-only telemetry.
Sourcepub const fn ntsc_phase(&self) -> u8
pub const fn ntsc_phase(&self) -> u8
The per-frame NTSC composite colour phase for the NES_NTSC filter
(0..=2 on NTSC; frame parity 0..=1 on PAL/Dendy). See
[rustynes_ppu::Ppu::ntsc_phase].
Sourcepub const fn set_custom_palette(&mut self, base: Option<[[u8; 3]; 64]>)
pub const fn set_custom_palette(&mut self, base: Option<[[u8; 3]; 64]>)
v1.1.0 beta.1 — install (Some) or clear (None) a custom 64-entry base
palette from a loaded .pal file. A presentation override; None (default)
is byte-identical to the built-in palette.
Sourcepub const fn set_extra_scanlines(&mut self, lines: u16)
pub const fn set_extra_scanlines(&mut self, lines: u16)
v1.7.0 “Forge” F3 — set the PPU extra-scanlines overclock (extra idle
vblank lines per frame). 0 (default) is byte-identical to stock timing.
Sourcepub const fn extra_scanlines(&self) -> u16
pub const fn extra_scanlines(&self) -> u16
v1.7.0 F3 — the configured extra-scanline count (0 = stock).
Sourcepub const fn set_fast_dotloop(&mut self, enabled: bool)
pub const fn set_fast_dotloop(&mut self, enabled: bool)
v2.1.8 A1 — enable/disable the specialized visible-scanline fast dot
path. false (default) is byte-identical to a build without it. See
[rustynes_ppu::Ppu::set_fast_dotloop].
Sourcepub const fn fast_dotloop(&self) -> bool
pub const fn fast_dotloop(&self) -> bool
v2.1.8 A1 — whether the visible-scanline fast dot path is enabled.
Sourcepub const fn set_oam_decay(&mut self, enabled: bool)
pub const fn set_oam_decay(&mut self, enabled: bool)
v2.1.4 F2.3 — enable/disable the optional OAM-decay accuracy model.
false (default) is byte-identical to a decay-free PPU. See
[rustynes_ppu::Ppu::set_oam_decay].
Sourcepub const fn set_ppu_revision(&mut self, revision: PpuRevision)
pub const fn set_ppu_revision(&mut self, revision: PpuRevision)
v2.1.7 P5 — select the emulated 2C02 die revision, storing it so a
power-cycle re-applies it, and applying it to the live PPU now. The
default revision is byte-identical. See PpuRevision.
Sourcepub const fn ppu_revision(&self) -> PpuRevision
pub const fn ppu_revision(&self) -> PpuRevision
v2.1.7 P5 — the currently-selected 2C02 die revision.
Sourcepub const fn set_power_up_palette(&mut self, init: PaletteInit)
pub const fn set_power_up_palette(&mut self, init: PaletteInit)
v2.1.7 P5 — apply a power-up palette-RAM pattern, storing it so a
power-cycle re-applies it and writing it to the live PPU’s palette RAM
now. The default (PaletteInit::Zeroed) is byte-identical. See
PaletteInit.
Sourcepub const fn power_up_palette(&self) -> PaletteInit
pub const fn power_up_palette(&self) -> PaletteInit
v2.1.7 P5 — the currently-selected power-up palette pattern.
Sourcepub fn set_power_on_ram(&mut self, ram: PowerOnRam)
pub fn set_power_on_ram(&mut self, ram: PowerOnRam)
v2.1.7 P5 — select the power-on work-RAM fill, storing it so a
power-cycle re-applies it, and applying it to the current RAM now. The
default (crate::nes::PowerOnRam::Zeroed) is byte-identical. See crate::nes::PowerOnRam.
Sourcepub const fn power_on_ram(&self) -> PowerOnRam
pub const fn power_on_ram(&self) -> PowerOnRam
v2.1.7 P5 — the currently-selected power-on work-RAM fill.
Sourcepub const fn oam_decay_enabled(&self) -> bool
pub const fn oam_decay_enabled(&self) -> bool
v2.1.4 F2.3 — whether the optional OAM-decay model is enabled.
Sourcepub const fn set_cpu_2a03_revision(&mut self, revision: Cpu2A03Revision)
pub const fn set_cpu_2a03_revision(&mut self, revision: Cpu2A03Revision)
v2.1.7 — set the emulated 2A03 die revision (DMA “unexpected read” axis).
Cpu2A03Revision::Rp2A03G (default) is byte-identical to the pre-v2.1.7
core; Cpu2A03Revision::Rp2A03H is the opt-in later-die model. See the
Cpu2A03Revision docs + ADR 0033.
Sourcepub const fn cpu_2a03_revision(&self) -> Cpu2A03Revision
pub const fn cpu_2a03_revision(&self) -> Cpu2A03Revision
v2.1.7 — the configured 2A03 die revision (default
Cpu2A03Revision::Rp2A03G).
Sourcepub const fn region(&self) -> Region
pub const fn region(&self) -> Region
Cartridge region (NTSC / PAL / Dendy / Multi). Drives wall-clock frame pacing in the frontend and clock-divider selection inside the PPU + APU.
Sourcepub const fn prg_rom_len(&self) -> usize
pub const fn prg_rom_len(&self) -> usize
Length in bytes of the loaded cartridge’s PRG-ROM (read-only metadata).
Sourcepub const fn chr_rom_len(&self) -> usize
pub const fn chr_rom_len(&self) -> usize
Length in bytes of the loaded cartridge’s CHR-ROM (0 when the board uses CHR-RAM). Read-only metadata.
Sourcepub fn peek_cpu(&mut self, addr: u16) -> u8
pub fn peek_cpu(&mut self, addr: u16) -> u8
Direct CPU-bus probe (does not advance time). Intended for
blargg-style status polls at $6000-$7FFF and the test harness’s
mapper-resident WRAM peek. Note that this still has side effects on
PPU registers ($2002 clears VBL and toggle, $2007 reads advance
the buffer); callers should avoid touching $2000-$3FFF via peek.
Sourcepub fn add_genie_code(&mut self, code: &str) -> Result<(), GenieError>
pub fn add_genie_code(&mut self, code: &str) -> Result<(), GenieError>
Add a Game Genie code (6 or 8 characters, case-insensitive). The code
patches a PRG address ($8000-$FFFF) on the CPU read path; adding a
code at an address that already has one replaces it.
§Errors
Returns GenieError if the code string cannot be decoded.
Sourcepub fn remove_genie_code(&mut self, code: &str)
pub fn remove_genie_code(&mut self, code: &str)
Remove the active Game Genie code whose canonical (upper-case) string
matches code. No-op if no such code is active.
Sourcepub fn clear_genie_codes(&mut self)
pub fn clear_genie_codes(&mut self)
Remove all active Game Genie codes.
Sourcepub fn genie_codes(&self) -> impl Iterator<Item = &GenieCode>
pub fn genie_codes(&self) -> impl Iterator<Item = &GenieCode>
Iterate the active Game Genie codes (address-sorted).
Sourcepub fn debug_peek_cpu(&mut self, addr: u16) -> u8
pub fn debug_peek_cpu(&mut self, addr: u16) -> u8
Side-effect-free CPU bus sample for the debugger hex viewer.
Returns the bus’s view of the byte at addr without the side
effects peek_cpu / raw_cpu_read carry on PPU register space
(no VBL clear, no PPUDATA buffer advance, no open-bus update). For
PPU registers we read back the cached snapshot; for mappers we go
through cpu_read — the overwhelming majority of mappers are
idempotent on $8000-$FFFF reads, and the few that latch on read
(MMC2 in particular) document that behavior as inherent.
Takes &mut self because mapper cpu_read is &mut — but no
emulator-visible state advances. The CPU cycle counter, PPU
scheduler, and APU all stay put.
Sourcepub fn debug_peek_ppu(&mut self, addr: u16) -> u8
pub fn debug_peek_ppu(&mut self, addr: u16) -> u8
Side-effect-free PPU bus sample ($0000-$3FFF).
$0000-$1FFF -> mapper CHR, $2000-$3EFF -> nametable
(via mapper’s mirroring), $3F00-$3FFF -> palette RAM.
Sourcepub fn debug_poke_ppu(&mut self, addr: u16, value: u8)
pub fn debug_poke_ppu(&mut self, addr: u16, value: u8)
v1.7.0 “Forge” Workstream A1 — debugger writeback. The structural mirror
of Self::debug_peek_ppu: $0000-$1FFF → mapper CHR (ppu_write,
a no-op on CHR-ROM), $2000-$3EFF → nametable (mapper-absorbed, else
CIRAM via the active mirroring), $3F00-$3FFF → palette RAM.
Side-effect-free w.r.t. the run loop: it is reached only through the
gated post-frame poke path (the same caller-side, after-run_frame stage
the raw RAM cheats use), so the deterministic core run loop is unchanged
and the no-edit path is byte-identical. debug-hooks-gated.
Sourcepub const fn debug_poke_oam(&mut self, idx: u8, value: u8)
pub const fn debug_poke_oam(&mut self, idx: u8, value: u8)
v1.7.0 “Forge” Workstream A1 — debugger writeback for one OAM byte
(idx = 0..256). debug-hooks-gated; reached only through the gated
post-frame poke path, so the default build is byte-identical.
Set the buttons currently held on player port. Ports 0/1 are the
standard $4016/$4017 controllers; ports 2/3 are players 3/4 on the
Four Score adapter (only polled when Self::set_four_score is on).
The change takes effect on the next strobe edge.
§Panics
Panics if port is not in 0..=3.
Sourcepub fn set_expansion_device(&mut self, port: usize, device: Option<InputDevice>)
pub fn set_expansion_device(&mut self, port: usize, device: Option<InputDevice>)
Attach (or replace) a non-standard overlay device on port (0 =
$4016, 1 = $4017). Pass None to unplug the device and return the
port to the standard controller / Four Score path (byte-identical).
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn expansion_device(&self, port: usize) -> &Option<InputDevice>
pub const fn expansion_device(&self, port: usize) -> &Option<InputDevice>
Sourcepub const fn set_paddle(&mut self, port: usize, position: u8, fire: bool)
pub const fn set_paddle(&mut self, port: usize, position: u8, fire: bool)
Update an attached Vaus paddle’s position + fire state on port. No-op
if the attached device is not a Vaus (or no device is attached).
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn set_zapper(&mut self, port: usize, x: u16, y: u16, trigger: bool)
pub const fn set_zapper(&mut self, port: usize, x: u16, y: u16, trigger: bool)
Update an attached Zapper’s aim point + trigger on port. No-op if the
attached device is not a Zapper (or no device is attached).
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn set_power_pad(&mut self, port: usize, buttons: u16)
pub const fn set_power_pad(&mut self, port: usize, buttons: u16)
Update an attached Power Pad’s live button mask (bit i = mat button
i+1) on port. No-op if the attached device is not a Power Pad.
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn set_snes_mouse(
&mut self,
port: usize,
dx: i16,
dy: i16,
left: bool,
right: bool,
sensitivity: u8,
)
pub const fn set_snes_mouse( &mut self, port: usize, dx: i16, dy: i16, left: bool, right: bool, sensitivity: u8, )
Update an attached SNES mouse’s movement + buttons + sensitivity on
port. No-op if the attached device is not a mouse.
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn set_family_keyboard(&mut self, port: usize, keys: [u8; 9])
pub const fn set_family_keyboard(&mut self, port: usize, keys: [u8; 9])
Update an attached Family BASIC keyboard’s pressed-key bitmap on port
(one byte per matrix row). No-op if the attached device is not a keyboard.
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn set_family_trainer(&mut self, port: usize, buttons: u16)
pub const fn set_family_trainer(&mut self, port: usize, buttons: u16)
v1.3.0 Workstream F1 — update an attached Family Trainer mat’s 12-button
mask on port. No-op if the attached device is not a Family Trainer.
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn set_subor_keyboard(&mut self, port: usize, keys: [u8; 9])
pub const fn set_subor_keyboard(&mut self, port: usize, keys: [u8; 9])
v1.3.0 Workstream F1 — update an attached Subor keyboard’s pressed-key
bitmap on port. No-op if the attached device is not a Subor keyboard.
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn set_konami_hyper_shot(&mut self, port: usize, buttons: u8)
pub const fn set_konami_hyper_shot(&mut self, port: usize, buttons: u8)
v1.3.0 Workstream F1 — update an attached Konami Hyper Shot’s 4-button
mask on port. No-op if the attached device is not a Konami Hyper Shot.
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn set_bandai_hyper_shot(&mut self, port: usize, sensors: u8)
pub const fn set_bandai_hyper_shot(&mut self, port: usize, sensors: u8)
v1.3.0 Workstream F1 — update an attached Bandai Hyper Shot’s 8-sensor
mask on port. No-op if the attached device is not a Bandai Hyper Shot.
§Panics
Panics if port is not in 0..=1.
Sourcepub const fn set_mirroring_override(&mut self, m: Option<Mirroring>)
pub const fn set_mirroring_override(&mut self, m: Option<Mirroring>)
v1.1.0 beta.1 (T-110-B4) — set (Some) or clear (None) the per-game
nametable mirroring override. A frontend load-time correction; None
(default) defers to the mapper (byte-identical).
Sourcepub const fn mirroring_override(&self) -> Option<Mirroring>
pub const fn mirroring_override(&self) -> Option<Mirroring>
The current per-game mirroring override (for the save-state).
Sourcepub fn mapper_has_hardwired_mirroring(&self) -> bool
pub fn mapper_has_hardwired_mirroring(&self) -> bool
Whether the loaded mapper hardwires its nametable mirroring (so an
external mirroring correction is safe to honor). See
[rustynes_mappers::Mapper::has_hardwired_mirroring].
Sourcepub const fn set_event_logging(&mut self, enabled: bool)
pub const fn set_event_logging(&mut self, enabled: bool)
v1.1.0 beta.2 (T-110-C3) — start/stop event-viewer recording.
Sourcepub const fn event_logging(&self) -> bool
pub const fn event_logging(&self) -> bool
Whether event-viewer recording is on.
Sourcepub const fn set_access_logging(&mut self, enabled: bool)
pub const fn set_access_logging(&mut self, enabled: bool)
v1.1.0 beta.3 (T-110-E2) — start/stop the Lua bus-access log.
Sourcepub const fn access_logging(&self) -> bool
pub const fn access_logging(&self) -> bool
Whether the bus-access log is recording.
Sourcepub fn clear_accesses(&mut self)
pub fn clear_accesses(&mut self)
Clear the bus-access log (called per frame by the run loop).
Sourcepub const fn set_interrupt_logging(&mut self, enabled: bool)
pub const fn set_interrupt_logging(&mut self, enabled: bool)
v1.2.0 (T-110-E1) — start/stop the Lua interrupt-service log.
Sourcepub const fn interrupt_logging(&self) -> bool
pub const fn interrupt_logging(&self) -> bool
Whether the interrupt-service log is recording.
Sourcepub fn interrupts(&self) -> &[InterruptRec]
pub fn interrupts(&self) -> &[InterruptRec]
The interrupt-service entries captured so far this frame.
Sourcepub fn clear_interrupts(&mut self)
pub fn clear_interrupts(&mut self)
Clear the interrupt-service log (called per frame by the run loop).
Sourcepub const fn set_event_breakpoints(&mut self, mask: u16)
pub const fn set_event_breakpoints(&mut self, mask: u16)
v1.4.0 Workstream D (D2) — set the armed event-breakpoint category mask
(a bit-OR of EventBpKind::bit). 0 disarms all (the default + the
per-cycle-cheap path).
Sourcepub const fn event_breakpoints(&self) -> u16
pub const fn event_breakpoints(&self) -> u16
The armed event-breakpoint category mask.
Sourcepub const fn take_event_break_hit(&mut self) -> Option<EventBreakHit>
pub const fn take_event_break_hit(&mut self) -> Option<EventBreakHit>
Take the first event-breakpoint hit of the current frame (cleared on
read). The frontend polls this after run_frame.
Sourcepub const fn clear_event_break_hit(&mut self)
pub const fn clear_event_break_hit(&mut self)
Clear any recorded event-breakpoint hit (called per frame by the run loop so each frame starts fresh).
Sourcepub fn clear_events(&mut self)
pub fn clear_events(&mut self)
Clear the event log (called at each frame start while recording).
Sourcepub fn sample_zapper_light(&mut self)
pub fn sample_zapper_light(&mut self)
Sample the framebuffer luminance at each attached Zapper’s aim point. Called once per frame (only does work when a Zapper is attached, so the no-device path is byte-identical).
Sourcepub const fn controller(&self, port: usize) -> &Controller
pub const fn controller(&self, port: usize) -> &Controller
Borrow controller port (0/1 = $4016/$4017; 2/3 = Four Score
players 3/4).
§Panics
Panics if port is not in 0..=3.
Sourcepub const fn take_frame_complete(&mut self) -> bool
pub const fn take_frame_complete(&mut self) -> bool
Has the PPU completed a frame? Drains the latch.
Sourcepub fn drain_audio(&mut self) -> Vec<f32>
pub fn drain_audio(&mut self) -> Vec<f32>
Drain finalized audio samples (host sample rate, normalized [0, ~1]).
Sourcepub fn drain_audio_into(&mut self, out: &mut [f32]) -> usize
pub fn drain_audio_into(&mut self, out: &mut [f32]) -> usize
Drain into a slice.
Sourcepub const fn current_m2_phase(&self) -> M2Phase
pub const fn current_m2_phase(&self) -> M2Phase
Returns the M2 phase the lockstep scheduler is currently in.
The scheduler ticks the PPU 3 dots per CPU cycle. Convention:
M2Phase::Low is the FIRST half of the cycle (the cycle’s
pre-sub-dot-1 portion, corresponding to silicon’s φ1);
M2Phase::High is the SECOND half (post-sub-dot-1, silicon’s
φ2). The boundary is the M2-rising edge between sub-dot 1 and
sub-dot 2.
At the start of tick_one_cpu_cycle the bus is in M2Phase::Low.
After sub-dot 1 of the 3-PPU-dot tick loop, it transitions to
M2Phase::High. After the cycle’s last sub-dot the bus
advances the cycle counter and returns to M2Phase::Low for
the next cycle.
This accessor is informational. As of Phase B2 the bus stores
per-phase IRQ snapshots (read by [Bus::poll_irq_at_phase])
independently of this accessor — current_m2_phase() itself is
not consulted by the CPU’s IRQ sample path.
Sourcepub const fn set_vs_dip(&mut self, dip: u8)
pub const fn set_vs_dip(&mut self, dip: u8)
Set the Vs. System 8-bit DIP switch bank (switch 1 = bit 0 .. switch 8 = bit 7). No effect on non-Vs. carts. Default 0.
Sourcepub const fn set_vs_ppu_type(&mut self, t: VsPpuType)
pub const fn set_vs_ppu_type(&mut self, t: VsPpuType)
Override the Vs. System PPU type and re-apply the output palette / 2C05 quirks immediately.
iNES-1.0 dumps carry no NES 2.0 byte-13, so the parser defaults a Vs.
cart to [rustynes_mappers::VsPpuType::Rp2C03]; a per-game database (keyed on
the ROM SHA-256) supplies the correct 2C04-000x / 2C05 type, which the
frontend applies through this setter. No effect on the running game’s
logic — only the colour LUT the PPU emits through. No-op shape on
non-Vs. carts (the default path never calls this).
Sourcepub const fn insert_coin(&mut self, acceptor: u8)
pub const fn insert_coin(&mut self, acceptor: u8)
Latch a Vs. System coin insertion. acceptor 0 = acceptor #1 ($4016
bit 5), 1 = acceptor #2 ($4016 bit 6); any other value is ignored. The
frontend should clear the latch (see Self::clear_coin) after the
real-hardware ~40-70 ms window (a few frames). No effect on non-Vs.
carts.
Sourcepub const fn clear_coin(&mut self)
pub const fn clear_coin(&mut self)
Clear all latched Vs. System coin-insert signals.
Sourcepub const fn set_vs_service(&mut self, pressed: bool)
pub const fn set_vs_service(&mut self, pressed: bool)
Set / clear the Vs. System service button ($4016 bit 2).
Sourcepub const fn set_vs_sub(&mut self, is_sub: bool)
pub const fn set_vs_sub(&mut self, is_sub: bool)
v2.0.0 beta.5 (Vs. DualSystem): mark this console as the SUB half of a
DualSystem pair ($4016 reads return bit 7 = 0x80). Wrapper-only.
Sourcepub const fn set_vs_external_irq(&mut self, asserted: bool)
pub const fn set_vs_external_irq(&mut self, asserted: bool)
v2.0.0 beta.5 (Vs. DualSystem): drive this console’s external /IRQ
line (the partner console’s $4016 bit-1 signal, Mesen2
IRQSource::External). Wrapper-only; OR’d into the IRQ level.
Sourcepub const fn take_vs_mainsub_edge(&mut self) -> Option<bool>
pub const fn take_vs_mainsub_edge(&mut self) -> Option<bool>
v2.0.0 beta.5 (Vs. DualSystem): poll-and-clear the latched $4016
bit-1 (main/sub comms signal) LEVEL. Returns Some(level) whenever
this console wrote $4016 since the last poll — deliberately
level-driven, not edge-filtered (see the vs_4016_bit1_dirty field
doc); the wrapper turns the level into the partner’s external-IRQ
assert (LOW asserts, HIGH clears). The shared-WRAM convergence
(pump_comms’s separate drain_vs_dual_wram_writes step) runs on
BOTH consoles every poll, independent of this bit-1 signal.
Sourcepub fn enable_vs_dual_wram(&mut self)
pub fn enable_vs_dual_wram(&mut self)
v2.0.0 beta.5 (Vs. DualSystem): provision the mapper-99 shared 2 KiB
WRAM window ($6000-$7FFF). Wrapper-only; no-op on other boards.
Sourcepub fn set_vs_dual_sub(&mut self)
pub fn set_vs_dual_sub(&mut self)
v2.0.0 beta.5 (Vs. DualSystem): mark the mapper as the SUB
console’s instance (banks the second PRG half + upper CHR pages —
the two CPUs run different programs). Wrapper-only cabinet wiring.
Sourcepub fn take_vs_dual_wram_writes(&mut self) -> Vec<(u16, u8)>
pub fn take_vs_dual_wram_writes(&mut self) -> Vec<(u16, u8)>
v2.0.0 beta.5 (Vs. DualSystem): drain this console’s shared-WRAM
write log for the wrapper to replay into the partner console (the
fully-shared MAME model). Empty off-board. Allocates a fresh Vec
each call — fine for diagnostics/tests, NOT used by the hot
pump_comms path (see Self::drain_vs_dual_wram_writes).
Sourcepub fn drain_vs_dual_wram_writes(&mut self, dst: &mut Vec<(u16, u8)>)
pub 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 into a caller-owned, reusable dst buffer — the
hot-path counterpart of Self::take_vs_dual_wram_writes, used by
VsDualSystem::pump_comms (called after every stepped instruction)
to avoid allocating a fresh Vec on every call.
Sourcepub fn apply_vs_dual_wram_write(&mut self, offset: u16, value: u8)
pub 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 shared-WRAM copy (no re-log).
Sourcepub fn take_vs_dual_wram(&mut self) -> Option<Box<[u8]>>
pub fn take_vs_dual_wram(&mut self) -> Option<Box<[u8]>>
v2.0.0 beta.5 (Vs. DualSystem): take the shared-WRAM copy (wrapper
snapshot-restore normalization). None off-board.
Sourcepub fn set_vs_dual_wram(&mut self, wram: Box<[u8]>)
pub 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).
Sourcepub fn is_vs_system(&self) -> bool
pub fn is_vs_system(&self) -> bool
True when the running cart is Vs. System hardware (NES 2.0 console type).
Sourcepub const fn is_vs_dual_system(&self) -> bool
pub const fn is_vs_dual_system(&self) -> bool
True when the cart’s header marks a Vs. DualSystem board (two CPUs /
two PPUs). Detection only — the dual-console emulation is a documented
v2.0 deferral (docs/audit/vs-dualsystem-design-2026-06-11.md); this
lets the frontend surface a clear note instead of a black screen.
Sourcepub fn mapper_debug_info(&self) -> MapperDebugInfo
pub fn mapper_debug_info(&self) -> MapperDebugInfo
Mapper debug info for the debugger UI: the mapper’s own bank/IRQ state
ENRICHED (v1.5.0 “Lens” Workstream I8) with the cartridge-level metadata
the bus owns — submapper, accuracy tier, ROM/RAM sizes, battery, the IRQ
mechanism, and the expansion-audio chip. Output-only; these enrichment
fields are filled here rather than in each of the 100+ mappers, and they
default to empty (so a mapper’s own debug_info() is unchanged).
Sourcepub const fn mapper_caps(&self) -> MapperCaps
pub const fn mapper_caps(&self) -> MapperCaps
The cached per-cycle mapper capability flags (see
[rustynes_mappers::MapperCaps]). caps.audio reflects whether the
loaded mapper has on-cart expansion audio with the mapper-audio feature
compiled in — used by the frontend to surface expansion-channel mixing
controls only for boards that actually have them.
Sourcepub const fn controllers_ref(&self) -> &[Controller; 2]
pub const fn controllers_ref(&self) -> &[Controller; 2]
Borrow both controllers as a slice.
Sourcepub const fn controllers34_ref(&self) -> &[Controller; 2]
pub const fn controllers34_ref(&self) -> &[Controller; 2]
Borrow the Four Score players 3 & 4 (save-state).
Sourcepub const fn set_four_score(&mut self, enabled: bool)
pub const fn set_four_score(&mut self, enabled: bool)
Enable/disable the Four Score 4-player adapter. Off by default; while
off, $4016/$4017 behave exactly as the standard two controllers
(byte-identical reads — determinism + save-states unaffected).
Sourcepub const fn four_score(&self) -> bool
pub const fn four_score(&self) -> bool
Whether the Four Score adapter is currently enabled.
Sourcepub fn disk_side_count(&self) -> usize
pub fn disk_side_count(&self) -> usize
Number of disk sides in the inserted FDS image (0 for cartridge builds).
Sourcepub fn inserted_disk_side(&self) -> Option<usize>
pub fn inserted_disk_side(&self) -> Option<usize>
The currently inserted FDS disk side, or None when ejected (or for a
cartridge build).
Sourcepub fn set_disk_side(&mut self, side: Option<usize>)
pub fn set_disk_side(&mut self, side: Option<usize>)
Insert FDS side i (Some) or eject (None). No-op on cartridge builds.
Sourcepub fn nsf_song_count(&self) -> u8
pub fn nsf_song_count(&self) -> u8
Number of selectable NSF songs (0 for cartridge / disk builds).
Sourcepub fn nsf_current_song(&self) -> u8
pub fn nsf_current_song(&self) -> u8
The currently-selected 0-based NSF song (0 for cartridge / disk builds).
Sourcepub fn nsf_set_song(&mut self, song: u8) -> bool
pub fn nsf_set_song(&mut self, song: u8) -> bool
Select a 0-based NSF song. Returns true if this is an NSF build (so the
caller re-runs the reset that re-enters the driver’s init).
Sourcepub fn enable_fds_trace(&mut self)
pub fn enable_fds_trace(&mut self)
Start recording the diagnostic FDS read-stream trace (off by default; observation-only). No-op on cartridge builds.
Sourcepub fn take_fds_trace(&mut self) -> Vec<FdsTraceRec>
pub fn take_fds_trace(&mut self) -> Vec<FdsTraceRec>
Drain the accumulated FDS read-stream trace records (empty for cartridge builds / when tracing was never enabled).
Sourcepub fn disk_image_bytes(&self) -> Vec<u8> ⓘ
pub fn disk_image_bytes(&self) -> Vec<u8> ⓘ
Re-serialize the (possibly-modified) FDS disk image to its byte layout for host persistence. Empty for cartridge builds.
Sourcepub fn disk_is_dirty(&self) -> bool
pub fn disk_is_dirty(&self) -> bool
Whether the FDS disk image has unsaved writes.
Sourcepub fn clear_disk_dirty(&mut self)
pub fn clear_disk_dirty(&mut self)
Clear the FDS disk dirty flag (after the host persists the image).
Sourcepub fn set_disk_write_protected(&mut self, protected: bool)
pub fn set_disk_write_protected(&mut self, protected: bool)
Mark the inserted FDS disk read-only (true) or writable (false).
Sourcepub const fn bus_misc_state(&self) -> BusMiscState
pub const fn bus_misc_state(&self) -> BusMiscState
Bus-side bookkeeping snapshot used by bus_snapshot::encode_bus.
Sourcepub const fn set_bus_misc_state(&mut self, s: BusMiscState)
pub const fn set_bus_misc_state(&mut self, s: BusMiscState)
Apply a previously-snapshotted bus bookkeeping state.
Sourcepub const fn set_cycle(&mut self, cycle: u64)
pub const fn set_cycle(&mut self, cycle: u64)
Set the cumulative CPU cycle counter (used by save-state restore).
Sourcepub fn set_ram_bytes(&mut self, bytes: &[u8]) -> Result<(), SnapshotError>
pub fn set_ram_bytes(&mut self, bytes: &[u8]) -> Result<(), SnapshotError>
Sourcepub const fn set_controllers(&mut self, controllers: [Controller; 2])
pub const fn set_controllers(&mut self, controllers: [Controller; 2])
Overwrite both controllers’ state.
Sourcepub const fn set_controllers34(&mut self, controllers: [Controller; 2])
pub const fn set_controllers34(&mut self, controllers: [Controller; 2])
Overwrite the Four Score players 3 & 4 (save-state restore).
Sourcepub fn poke_ram(&mut self, addr: u16, value: u8)
pub fn poke_ram(&mut self, addr: u16, value: u8)
Write a byte directly into CPU work RAM ($0000-$1FFF, mirrored every
$800). Used by the frontend’s raw RAM cheats (GameShark-style),
applied caller-side after crate::Nes::run_frame so the core run
loop stays pure (the determinism contract is unperturbed for the
no-cheat path). No-op for addresses outside system RAM.
Sourcepub fn snapshot(&self, rom_hash_tag: [u8; 6]) -> Vec<u8> ⓘ
pub fn snapshot(&self, rom_hash_tag: [u8; 6]) -> Vec<u8> ⓘ
Encode the entire bus + chip state into a .rns snapshot.
Returns the bytes the caller should persist via
frontend::save_state (or feed into the rewind ring).
The output is bit-deterministic: same (seed, ROM, input sequence)
produces identical bytes.
Sourcepub fn snapshot_into(&self, out: &mut Vec<u8>, rom_hash_tag: [u8; 6])
pub fn snapshot_into(&self, out: &mut Vec<u8>, rom_hash_tag: [u8; 6])
v2.8.0 Phase 3 — Self::snapshot into a caller-owned buffer
(cleared first; capacity reused across calls). The per-call
allocation of the ~250 KiB blob matters to per-frame consumers
(run-ahead, the netplay save-state ring, rewind).
Sourcepub fn restore(&mut self, data: &[u8]) -> Result<(), SnapshotError>
pub fn restore(&mut self, data: &[u8]) -> Result<(), SnapshotError>
Apply a previously snapshotted blob to the bus and chips. The CPU
is restored separately by crate::Nes::restore.
§Errors
Returns SnapshotError for unknown sections, version mismatches,
or malformed bodies.
Trait Implementations§
Source§impl Bus for LockstepBus
impl Bus for LockstepBus
Source§fn read(&mut self, addr: u16) -> u8
fn read(&mut self, addr: u16) -> u8
Pure address-space read under R1 (the DMA drain happens in
[Bus::cpu_clock]; Phase 3 will split the drain out of cpu_read).
Phase 1 delegates to the legacy path so the contract compiles.
Source§fn cpu_divider(&self) -> u64
fn cpu_divider(&self) -> u64
R1 master clocks per CPU cycle for the cartridge region (NTSC 12 / PAL
16 / Dendy 15) — the cpu_divider half of region_dividers.
Drives the CPU loop’s master_clock advance + read/write split so the
CPU<->PPU phase is 3:1 NTSC, 3.2:1 PAL, 3:1 Dendy.
Source§fn run_ppu_to(&mut self, target: u64, is_post_access: bool)
fn run_ppu_to(&mut self, target: u64, is_post_access: bool)
R1 double catch-up: tick whole PPU dots while
ppu_clock + ppu_divider <= target.
R1c-3 (mmc3-m2-phase-irq, default-off): when the feature is
enabled, sub_dot is seeded from the REAL M2-phase of this catch-up
call (0 = pre-access / M2-low, called from Cpu::start_cycle
before the bus access; 2 = post-access / M2-high, called from
Cpu::end_cycle after it) instead of always restarting at 0. Prior
to this experiment sub_dot was a call-LOCAL counter that reset to
zero on every invocation of this function — since run_ppu_to is
called twice per CPU cycle (once per half) and each half typically
ticks at most one PPU dot, the value threaded to
Mapper::notify_a12_at_sub_dot was almost always 0 regardless of
which half of the cycle actually produced the A12 transition. That
meant the M2-phase plumbing ADR-0002 describes (“sub-dot 0/1 is
M2-low, 2 is M2-high”) was never actually true on the live R1
(non-DMA) scheduler path — only on the legacy tick_one_cpu_cycle
DMA-burst path, which genuinely walks all 3 dots of a cycle in one
call with a persistent counter. This experiment closes that gap so
MMC3’s (default-off) M2-phase-aware IRQ-visibility pipeline can be
evaluated against real phase data on the promoted core. See
docs/adr/0002-irq-timing-coordination.md and
docs/audit/r1r2-per-dot-scheduler-attempt-2026-07-02.md.
When the feature is OFF this compiles to the exact prior
call-local-counter behavior (sub_dot always starts at 0) —
byte-identical default build, per the project’s additive/off-by-
default convention.
Source§fn cpu_clock(&mut self)
fn cpu_clock(&mut self)
R1: one CPU cycle of bus-side work (NO PPU advance — that lives in
[Bus::run_ppu_to]). Controller strobe + bus-side DMA drain + cycle
counter + per-cycle PPU/mapper hooks + APU tick. DMA stays bus-side
(the pivot’s working service_dmc_dma); Phase 3 wires the
dma_mc_consumed coherence accounting.
Source§fn poll_nmi(&mut self) -> bool
fn poll_nmi(&mut self) -> bool
true exactly once per high-to-low
transition of the NMI line; subsequent calls return false until the
next transition.Source§fn poll_irq(&mut self) -> bool
fn poll_irq(&mut self) -> bool
Source§fn poll_irq_at_phase(&mut self, phase: M2Phase) -> bool
fn poll_irq_at_phase(&mut self, phase: M2Phase) -> bool
Source§fn on_cpu_cycle(&mut self)
fn on_cpu_cycle(&mut self)
Source§fn internal_data_bus(&self) -> u8
fn internal_data_bus(&self) -> u8
Source§fn cycle_count(&self) -> u64
fn cycle_count(&self) -> u64
Source§fn notify_irq_service(&mut self, vector: u16, is_nmi: bool)
fn notify_irq_service(&mut self, vector: u16, is_nmi: bool)
vector ($FFFE for IRQ/BRK, $FFFA for NMI,
or $FFFA if an IRQ/BRK service sequence was hijacked by an NMI
edge during cycles 1..=5 of the service sequence). is_nmi is
true for an NMI service entry and false for an IRQ or BRK
service entry (so the bus can distinguish hijack from a clean
NMI even when the vector is the same). Read moreSource§fn write(&mut self, addr: u16, value: u8)
fn write(&mut self, addr: u16, value: u8)
Bus::cpu_write].Source§fn cpu_clock_apu_dmc(&mut self)
fn cpu_clock_apu_dmc(&mut self)
Cpu::end_cycle after the access + PPU catch-up). This places the
DMC fire-phase at main’s end-of-cycle position (so DMASync’s $4000
open-bus conflict lands), while the rest of the APU (incl. the IRQ line)
stays on the cycle-start cpu_clock tick (so the C1 φ2 IRQ sample is
unchanged). Default no-op. Pairs with Apu::set_dmc_driven_externally.Source§fn take_dma_mc_consumed(&mut self) -> u64
fn take_dma_mc_consumed(&mut self) -> u64
master_clock in
end_cycle so the CPU<->PPU phase stays coherent across a bus-side
DMA span. Default 0 (no bus-side DMA accounting on test stubs).Source§fn irq_level(&self) -> bool
fn irq_level(&self) -> bool
prev_run_irq delay itself.
Default false; the production bus overrides this.Source§fn nmi_level(&self) -> bool
fn nmi_level(&self) -> bool
prev_need_nmi delay. Default false (test stubs).Source§fn dmc_dma_pending(&self) -> bool
fn dmc_dma_pending(&self) -> bool
read1, running one dmc_dma_step per R1 cycle
BEFORE its own read (DMA halts only on read cycles). Default false.Source§fn dmc_dma_defer_load_entry(&self) -> bool
fn dmc_dma_defer_load_entry(&self) -> bool
mc-r1-dmc-load-get-entry: defer a LOAD whose first-service would be a PUT
cycle by 1 CPU cycle so it enters on a GET (span-3 hardware load). Gates BOTH
the read1 loop AND the idle_tick loop (DMASync’s load fires during NOPs=idle).Source§fn dmc_dma_step(&mut self, halted_addr: u16)
fn dmc_dma_step(&mut self, halted_addr: u16)
halted_addr
is the CPU read the DMA is preempting. Default no-op.Source§fn dmc_dma_step_idle(&mut self)
fn dmc_dma_step_idle(&mut self)
mc-r1-dmc-idle-halt: perform one interleaved DMC-DMA cycle during a CPU
INTERNAL cycle (no instruction read). The bus supplies the held address
(its last-read bus address) since idle_tick has none. Default no-op.Source§fn oam_dma_pending(&self) -> bool
fn oam_dma_pending(&self) -> bool
mc-r1-full-cpu): is an OAM DMA pending or in flight? The CPU
loops on this in read1 (after the DMC loop, DMC-get-before-OAM-get), so
each OAM cycle runs CPU-driven (wrapped start_cycle/end_cycle) and
samples IRQ/NMI via the φ2 pipeline — the surface the bus-burst bypassed.
Default false.Source§fn oam_dma_step(&mut self, halted_addr: u16)
fn oam_dma_step(&mut self, halted_addr: u16)
$4014, then halt/align/read/write per cycle). Does NOT advance
time — the surrounding start_cycle/end_cycle do. Default no-op.Source§fn unified_dma_pending(&self) -> bool
fn unified_dma_pending(&self) -> bool
mc-r1-dma-unified): is ANY DMA work pending for the
unified DMC/OAM engine — a serviceable DMC DMA (pending and not a
load deferred to its get-cycle entry, the mc-r1-dmc-load-get-entry
rule), a $4014 OAM DMA awaiting its first cycle, or an OAM transfer
still in flight? The ONE Cpu::read1/idle_tick DMA loop spins on
this, running one [Bus::unified_dma_cycle] per CPU cycle (each a
full R1 cycle: start_cycle -> dispatch -> end_cycle, so every DMA
cycle keeps the φ2 IRQ sample — the C1-safe shape). Default false.Source§fn unified_dma_cycle(&mut self, halted_addr: u16)
fn unified_dma_cycle(&mut self, halted_addr: u16)
mc-r1-dma-unified): ONE cycle of the unified DMC/OAM DMA
engine — a direct port of the TriCNES _6502 per-cycle DMA dispatch
table (the SINGLE driver standalone DMC, standalone OAM, and the
overlap all ride), at FLOOR parity for this stage. halted_addr is
the CPU read the DMA is preempting (the parked 6502 address bus).
Does NOT advance time — the surrounding start_cycle/end_cycle do.
Default no-op.Source§fn unified_dma_cycle_idle(&mut self)
fn unified_dma_cycle_idle(&mut self)
mc-r1-dma-unified): one unified-engine DMA cycle during
a CPU INTERNAL cycle (no instruction read; the bus supplies its held
last-read address). The unified replacement for
[Bus::dmc_dma_step_idle]. Default no-op.Source§fn oam_dma_in_flight(&self) -> bool
fn oam_dma_in_flight(&self) -> bool
mc-r1-dmc-oam-overlap): is an OAM DMA actually IN FLIGHT
(started, cycles still owed) — distinct from oam_dma_pending, which is
true for a not-yet-started $4014 write too. The overlap loop uses this
to decide whether a DMC halt cycle can SHARE an OAM cycle. Default false.Source§fn oam_dma_overlap_ready(&self) -> bool
fn oam_dma_overlap_ready(&self) -> bool
mc-r1-counter-collapse boundary realign): may a pending DMC
DMA join an OAM DMA as an OVERLAP event? Default delegates to
[Bus::oam_dma_in_flight]. Under the counter-collapse flag the bus also
answers true for a $4014 write that is PENDING but not yet started:
the end-of-cycle byte-timer shift can surface the DMC arm in the gap
between the $4014 write and OAM’s first cycle, and routing that arm to
the standalone dmc_dma_step (full unshared span) instead of the overlap
event is exactly the DMC+OAM idx[7] regime-transition error (lockstep
latches OAM in drain_dma BEFORE its DMC-pending check, so the same arm
overlaps OAM’s halt/alignment cycles there).Source§fn dmc_dma_last_was_get(&self) -> bool
fn dmc_dma_last_was_get(&self) -> bool
Bus::dmc_dma_step] perform the DMC
GET (the sample fetch) rather than a halt/dummy/align cycle? The overlap
loop advances OAM on non-GET (halt) cycles only — the GET steals an OAM
slot. Default false.Source§fn oam_dma_overlap_cycle(&mut self)
fn oam_dma_overlap_cycle(&mut self)
start_cycle/end_cycle do. Default no-op.Source§fn dmc_overlap_begin(&mut self, halted_addr: u16) -> u32
fn dmc_overlap_begin(&mut self, halted_addr: u16) -> u32
service_dmc_dma_during_oam prologue. Latches the DMA span + the
open-bus replay and returns the UNCONDITIONAL halt/dummy/align noop count
(2 for a short/load DMA, 3 for a reload) — NOT parity-gated. The CPU then
runs exactly that many [Bus::dmc_overlap_noop_cycle]s, one
[Bus::dmc_overlap_get_cycle], and (if OAM still owes) one
[Bus::dmc_overlap_realign_cycle]. halted_addr is the CPU read the DMA
pair is preempting — used as the OAM halt address when the event starts a
PENDING (not-yet-latched) $4014 OAM DMA (the counter-collapse boundary
case; an already-in-flight OAM keeps its own latched halt address).
Default 0 (no DMC event).Source§fn dmc_overlap_noop_cycle(&mut self)
fn dmc_overlap_noop_cycle(&mut self)
replay_dma_noop_read +
clock_oam_dma_cycle) minus the time tick. Default no-op.Source§fn dmc_overlap_get_cycle(&mut self)
fn dmc_overlap_get_cycle(&mut self)
dmc_dma_step GET. Default no-op.Source§fn dmc_overlap_realign_cycle(&mut self)
fn dmc_overlap_realign_cycle(&mut self)
if dma_cycles_owed > 0 { tick }. The cycle the prior
per-cycle scaffold was MISSING. Default no-op.Source§fn dmc_abort_pending(&self) -> bool
fn dmc_abort_pending(&self) -> bool
mc-r1-dmc-abort-cancel): is a 1-byte
non-looping implicit DMC-DMA abort matured and awaiting service? The CPU
consults this at the top of read1/write1. Default false.Source§fn dmc_abort_is_get_cycle(&self) -> bool
fn dmc_abort_is_get_cycle(&self) -> bool
!put_cycle)? On a get cycle the matured abort runs as a
1-cycle DMA (Y=1); on a put cycle (or any CPU write) it does NOT occur
(Y=0, “the abort will not land on a write cycle”). Default false.Source§fn dmc_abort_halt_step(&mut self, halted_addr: u16)
fn dmc_abort_halt_step(&mut self, halted_addr: u16)
halted_addr, then clear the abort + the
pending reload. Called by read1 only on a get cycle. Default no-op.Source§fn dmc_abort_cancel(&mut self)
fn dmc_abort_cancel(&mut self)
Source§fn trace_end_cycle(&mut self)
fn trace_end_cycle(&mut self)
Cpu::end_cycle
(after handle_interrupts), so the irq-timing-trace tooling can
record a CycleRecord for the R1 access path (which bypasses the
LockstepBus tick_one_cpu_cycle push). Default no-op; the production
bus overrides it only under the irq-timing-trace feature, so non-trace
R1 builds compile this to an empty call.§fn cpu_cycle_phi1(&mut self)
fn cpu_cycle_phi1(&mut self)
Cpu::read1 / Cpu::write1 when the
cpu-c1-attempt-17-access-reorder feature is enabled. Read more§fn cpu_cycle_phi2(&mut self)
fn cpu_cycle_phi2(&mut self)
Cpu::read1 / Cpu::write1
when the cpu-c1-attempt-17-access-reorder feature is
enabled. Read more