Skip to main content

Nes

Struct Nes 

Source
pub struct Nes { /* private fields */ }
Expand description

Top-level NES emulator handle.

Owns the CPU, PPU, mapper, RAM, and controller stub. Construct via Nes::from_rom; drive forward via Nes::run_frame or Nes::step_instruction. The framebuffer can be sampled at any time via Nes::framebuffer.

Implementations§

Source§

impl Nes

Source

pub const TRACE_CAP: usize = 50_000

Maximum cycle-trace ring depth (oldest records drop past this).

Source

pub fn wram(&self) -> &[u8]

Returns a reference to the internal WRAM.

Source

pub fn wram_mut(&mut self) -> &mut [u8]

Returns a mutable reference to the internal WRAM.

Source

pub fn sram(&self) -> &[u8]

Returns a reference to the cartridge SRAM (if any).

Source

pub fn sram_mut(&mut self) -> &mut [u8]

Returns a mutable reference to the cartridge SRAM (if any).

Source

pub fn vram(&self) -> &[u8]

Returns a reference to the internal VRAM (nametables).

Source

pub fn vram_mut(&mut self) -> &mut [u8]

Returns a mutable reference to the internal VRAM (nametables).

Source

pub fn from_rom(bytes: &[u8]) -> Result<Self, RomError>

Build a new emulator from raw ROM bytes (iNES 1.0 or NES 2.0).

§Errors

Returns the underlying [RomError] if the bytes don’t parse.

Source

pub fn from_rom_with_sample_rate( bytes: &[u8], sample_rate: u32, ) -> Result<Self, RomError>

Build an emulator with an explicit audio sample rate (the rate the CPAL stream is opened at).

§Errors

Returns the underlying [RomError] if the bytes don’t parse.

Source

pub fn from_disk(disk_bytes: &[u8], bios_bytes: &[u8]) -> Result<Self, RomError>

Build an emulator from a Famicom Disk System .fds disk image and a user-supplied 8 KiB BIOS (disksys.rom).

The BIOS is never committed to this repo (it is Nintendo IP); the caller supplies it (a frontend BIOS prompt is Stage 2). Construction parses the disk container, builds the FDS device as the bus’s mapper, and runs the standard cold-boot reset (the BIOS reset vector at $FFFC drives the disk-load sequence).

Uses the default 44.1 kHz audio sample rate; use Nes::from_disk_with_sample_rate to pick the rate.

§Errors

Returns the underlying [RomError] if the disk image is unparseable or the BIOS is not exactly 8 KiB.

Source

pub fn from_disk_with_sample_rate( disk_bytes: &[u8], bios_bytes: &[u8], sample_rate: u32, ) -> Result<Self, RomError>

Build an FDS emulator with an explicit audio sample rate. See Nes::from_disk.

The reported rom_sha256 hashes the disk-image bytes (not the BIOS), so save-states / movies key off the disk the way cartridge builds key off the ROM.

§Errors

Returns the underlying [RomError] if the disk image is unparseable or the BIOS is not exactly 8 KiB.

Source

pub fn from_nsf(nsf_bytes: &[u8]) -> Result<Self, RomError>

Build an emulator that plays a classic NSF (NESM) music file.

Only the classic NESM\x1a container is supported; NSFe and expansion-chip audio are documented deferrals.

NSF files carry a ripped NES sound engine plus an init/play address pair, not a PPU program. Construction parses the file, installs a [rustynes_mappers::NsfMapper] (a synthetic 6502 driver + the program image) as the bus’s mapper, and runs the standard cold-boot reset — the driver’s reset vector calls init for the starting song, enables vblank NMI, and the ordinary 60 Hz NMI then calls play once per frame. Audio is produced through the unchanged lockstep loop; there is no video.

Uses the default 44.1 kHz sample rate; see Nes::from_nsf_with_sample_rate.

§Errors

Returns the underlying [RomError] when the NSF header is malformed.

Source

pub fn from_nsf_with_sample_rate( nsf_bytes: &[u8], sample_rate: u32, ) -> Result<Self, RomError>

Build an NSF player with an explicit audio sample rate. See Nes::from_nsf.

§Errors

Returns the underlying [RomError] when the NSF header is malformed.

Source

pub fn nsf_song_count(&self) -> u8

Number of selectable songs in the loaded NSF (0 for a cartridge / disk).

Source

pub fn nsf_current_song(&self) -> u8

The currently-selected 0-based NSF song (0 for a cartridge / disk).

Source

pub fn nsf_set_song(&mut self, song: u8)

Select a 0-based NSF song and restart playback on it (re-runs init via a warm reset). No-op for a cartridge / disk.

Source

pub fn from_rom_with_power_on_seed( bytes: &[u8], seed: u64, ) -> Result<Self, RomError>

Build an emulator with a randomized power-on RAM state (developer mode; Phase 7 / T-72-005).

Identical to Nes::from_rom except the 2 KiB CPU work RAM and the open-bus latch are filled from a deterministic xorshift64 PRNG keyed on seed, modelling the unreliable power-on RAM of real hardware (nesdev “CPU power up state”). Use this to shake out game/test code that depends on a particular post-power-on RAM pattern.

The randomization is seeded and deterministic — the same seed yields the same state, so the same seed + ROM + input ⇒ bit-identical contract still holds. The default Nes::from_rom (zeroed RAM) is what CI, the regression oracle, and save-state tests use.

§Errors

Returns the underlying [RomError] if the bytes don’t parse.

Source

pub fn from_rom_with_power_on_config( bytes: &[u8], config: PowerOnConfig, ) -> Result<Self, RomError>

v2.1.7 P5 — build an emulator with an explicit PowerOnConfig.

Generalizes Nes::from_rom_with_power_on_seed: the caller chooses the power-on work-RAM fill (PowerOnRam::Zeroed / PowerOnRam::Seeded / PowerOnRam::Filled). The config is stored on the bus so a subsequent power-cycle re-applies the same fill (keeping power_cycle == fresh boot). All fills are deterministic, so the same config + ROM + input ⇒ bit-identical contract still holds. PowerOnConfig::default (PowerOnRam::Zeroed) is byte-identical to Nes::from_rom.

§Errors

Returns the underlying [RomError] if the bytes don’t parse.

Source

pub fn reset(&mut self)

Reset (warm boot). Preserves WRAM; reloads PC from $FFFC/D.

Source

pub fn power_cycle(&mut self)

Power-cycle (cold boot). Zeroes WRAM, re-rolls phase, reloads vectors.

Source

pub fn run_frame(&mut self) -> &[u8]

Run until the PPU finishes a frame. Returns the framebuffer slice.

§Panics

Panics if the CPU JAMs without producing a frame. Real software shouldn’t JAM; if it does, the caller’s run-loop should catch it before the next frame.

Source

pub const fn set_rewind_capture(&mut self, enabled: bool)

v2.8.0 Phase 3 — enable/disable the per-frame rewind capture while the ring stays armed. Run-ahead turns it off around its hidden + visible frames so only persistent-timeline frames land in the ring. Default true; with no rewind ring armed this is a no-op.

Source

pub fn step_instruction(&mut self) -> u8

Step exactly one CPU instruction. For debuggers / step-through tools.

Source

pub fn add_breakpoint(&mut self, addr: u16)

v1.1.0 beta.2 (Workstream C) — add an exec/PC breakpoint at addr. Nes::run_frame stops the frame the next time the program counter reaches addr (reportable via Nes::take_break_hit). Idempotent. debug-hooks only.

Source

pub fn remove_breakpoint(&mut self, addr: u16)

Remove a previously-added exec breakpoint (no-op if absent).

Source

pub fn clear_breakpoints(&mut self)

Remove all breakpoints.

Source

pub fn breakpoints(&self) -> &[u16]

The current exec breakpoints (insertion order).

Source

pub const fn set_breakpoints_enabled(&mut self, enabled: bool)

Arm/disarm breakpoint checking without discarding the list. Default on.

Source

pub const fn breakpoints_enabled(&self) -> bool

Whether breakpoint checking is armed.

Source

pub const fn take_break_hit(&mut self) -> Option<u16>

Take the PC that last hit a breakpoint (cleared on read). The frontend polls this after Nes::run_frame to pause when a breakpoint fired.

Source

pub const fn set_event_breakpoints(&mut self, mask: u16)

v1.4.0 Workstream D (D2) — arm the event-driven breakpoint categories (a bit-OR of crate::EventBpKind::bit). 0 (default) disarms every category — the per-access taps are then a single cheap mask == 0 early-out. Output-only: a hit pauses + reports but never mutates state. debug-hooks only.

Source

pub const fn event_breakpoints(&self) -> u16

The armed event-breakpoint category mask.

Source

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 Nes::run_frame to pause when an armed hardware event fired, reporting its kind + frame/cycle/scanline/dot.

Source

pub const fn set_trace_enabled(&mut self, enabled: bool)

v1.1.0 beta.2 (T-110-C2) — start/stop the cycle-trace logger. While on, each executed instruction’s CPU state is pushed to a ring buffer (capped at Self::TRACE_CAP). Default off.

Source

pub const fn trace_enabled(&self) -> bool

Whether the cycle-trace logger is recording.

Source

pub fn trace_len(&self) -> usize

Number of records currently in the trace ring.

Source

pub fn clear_trace(&mut self)

Clear the trace ring.

Source

pub fn trace_records(&self) -> Vec<TraceRec>

Copy the trace ring oldest-first (for the trace panel / file export).

Source

pub fn trace_tail_vec(&self, n: usize) -> Vec<TraceRec>

Copy the most recent n trace records (oldest-first) — for the live trace panel’s tail view, cheaper than Self::trace_records on a full ring.

Source

pub const fn set_event_logging(&mut self, enabled: bool)

v1.1.0 beta.2 (T-110-C3) — start/stop the event viewer. While on, the bus records this frame’s PPU/APU/mapper writes (with their PPU position); the log is reset at each Self::run_frame. Default off; output-only.

Source

pub const fn event_logging(&self) -> bool

Whether the event viewer is recording.

Source

pub fn events(&self) -> &[EventRec]

The current frame’s captured events (for the event-viewer panel).

Source

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. While on, the bus records this frame’s CPU reads + writes (with values); the log is reset at each Self::run_frame. Default off; output-only. Enabled by the scripting engine only while onRead/onWrite callbacks exist.

Source

pub const fn access_logging(&self) -> bool

Whether the bus-access log is recording.

Source

pub fn accesses(&self) -> &[AccessRec]

The current frame’s captured CPU bus accesses (for the Lua engine).

Source

pub const fn set_exec_logging(&mut self, enabled: bool)

v1.1.0 beta.3 (T-110-E2) — start/stop the per-frame exec-PC log for the Lua onExec callback. Independent of the Trace Logger (set_trace_enabled), so enabling it does not disturb the debugger’s trace recording. Cleared every Self::run_frame; output-only.

Source

pub const fn exec_logging(&self) -> bool

Whether the per-frame exec-PC log is recording.

Source

pub fn exec_log(&self) -> &[u16]

This frame’s executed PCs, in execution order (for the Lua engine).

Source

pub const fn was_input_polled_this_frame(&self) -> bool

true if the running program read a controller port ($4016/$4017) during the most recent Self::run_frame — the inverse of a TAStudio “lag frame” (v1.6.0 Workstream A3). The greenzone / piano-roll lag log queries this each frame. Output-only; debug-hooks-gated, so the shipped build is byte-identical and the determinism contract holds.

Source

pub const fn set_interrupt_logging(&mut self, enabled: bool)

v1.2.0 (T-110-E1) — start/stop the per-frame interrupt-service log for the Lua onNmi / onIrq callbacks. The log records this frame’s committed NMI / IRQ / BRK service entries (captured at the CPU’s service-vector commit point, NOT the speculative poll sampler); it is cleared at each Self::run_frame. Default off; output-only. Enabled by the scripting engine only while onNmi/onIrq callbacks exist. Mirrors Self::set_exec_logging.

Source

pub const fn interrupt_logging(&self) -> bool

Whether the per-frame interrupt-service log is recording.

Source

pub fn interrupt_log(&self) -> &[InterruptRec]

This frame’s committed interrupt-service entries, in service order (for the Lua engine). Mirrors Self::exec_log / Self::accesses.

Source

pub fn framebuffer(&self) -> &[u8]

Borrow the framebuffer (RGBA8, 256x240).

Source

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(t) paints output only). Output-only; see [rustynes_ppu::Ppu::debug_set_framebuffer]. Reached only through the script crate’s gated post-frame path, so the shipped build is byte-identical and the determinism contract holds. debug-hooks-gated.

Source

pub fn index_framebuffer(&self) -> &[u16]

Borrow the parallel palette-index framebuffer (256x240 u16s, each (emphasis << 6) | colour) for the NES_NTSC composite filter. See [rustynes_ppu::Ppu::index_framebuffer].

Source

pub fn hd_tile_source(&self) -> &[HdTileSource]

v1.2.0 C3 (hd-pack) — borrow the per-pixel HD-pack tile-source buffer (256x240 [rustynes_ppu::HdTileSource] records). Each entry names the CHR tile that produced the pixel; the frontend HD-pack loader groups these by 8x8 cell, hashes the CHR bytes, and substitutes hi-res tiles. Output-only telemetry; the determinism contract is unaffected. See [rustynes_ppu::Ppu::hd_tile_source].

Source

pub const fn hd_bg_scroll(&self) -> (i32, i32)

The frame’s background scroll (x, y) in NES pixels, for offsetting parallax HD-pack <background> layers (see [rustynes_ppu::Ppu::hd_bg_scroll]). Output-only.

Source

pub const fn ntsc_phase(&self) -> u8

The per-frame NTSC composite colour phase consumed by the NES_NTSC filter (0..=2 on NTSC; frame parity 0..=1 on PAL/Dendy). See [rustynes_ppu::Ppu::ntsc_phase].

Source

pub const fn frame(&self) -> u64

The completed-frame counter (PPU frames since power-on). A monotonic, deterministic, save-state-restored value — the frontend uses it to phase turbo/autofire so the strobe is reproducible under rollback / TAS replay.

Source

pub const fn bus(&self) -> &LockstepBus

Borrow the underlying bus (debugger / tests).

Source

pub const fn bus_mut(&mut self) -> &mut LockstepBus

Mutably borrow the underlying bus (debugger / tests).

Source

pub const fn cpu(&self) -> &Cpu

Borrow the CPU (debugger / tests).

Source

pub const fn cycle(&self) -> u64

Cumulative CPU cycle count.

Source

pub const fn is_jammed(&self) -> bool

true when the CPU has executed a JAM/KIL/STP and is halted.

(v2.0.0 beta.5: the VsDualSystem soft-lockstep driver guards its per-instruction stepping — the pre-existing debugger Self::step_instruction — on this.)

Source

pub const fn region(&self) -> Region

Cartridge region (NTSC / PAL / Dendy / Multi). Drives wall-clock frame pacing in the frontend and clock dividers in the chip cores.

Source

pub const fn prg_rom_len(&self) -> usize

Length in bytes of the loaded cartridge’s PRG-ROM (read-only metadata).

Exposed for the Lua scripting cart:prg_size() query (and any other read-only consumer); does not touch deterministic state.

Source

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; backs the Lua cart:chr_size() query.

Source

pub fn mapper_id(&self) -> u16

The loaded mapper’s iNES / NES 2.0 mapper id (backs cart:mapper_id()).

Source

pub const fn frame_duration(&self) -> Duration

Wall-clock frame duration for this cartridge’s region. The frontend uses this to pace emulator advance independently of monitor refresh rate — without it, Fifo present mode on a 144 Hz monitor would run the emulator 2.4× too fast.

Source

pub fn drain_audio(&mut self) -> Vec<f32>

Drain accumulated audio samples (host sample rate, normalized [0.0, ~1.0]). Call once per frame from the frontend’s audio thread or batch driver.

Source

pub const fn set_buttons(&mut self, port: usize, buttons: Buttons)

Set the buttons currently held on player port. Ports 0/1 are the standard controllers ($4016/$4017); 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.

Source

pub const fn buttons(&self, port: usize) -> Buttons

Get the buttons currently held on player port (0/1 = $4016/$4017; 2/3 = Four Score players 3/4). Read-only; does not advance emulator state.

Used by the TAS movie recorder (crate::movie) to capture the inputs applied before each Self::run_frame. (Movies record players 1 & 2; Four Score players 3/4 are not part of the .rnm stream.)

§Panics

Panics if port is not in 0..=3.

Source

pub const fn set_four_score(&mut self, enabled: bool)

Enable/disable the Four Score 4-player adapter. Off by default; while off, controller reads are byte-identical to the standard two-pad behavior (the determinism contract and save-states are unaffected). When on, players 3/4 (ports 2/3) are multiplexed onto $4016/$4017 across a 24-read serial sequence.

Source

pub const fn four_score(&self) -> bool

Whether the Four Score adapter is currently enabled.

Source

pub fn is_vs_system(&self) -> bool

True when the running cart is Nintendo Vs. System arcade hardware (NES 2.0 console type = Vs. System). The RGB PPU + DIP/coin inputs only take effect on such carts.

Source

pub const fn is_vs_dual_system(&self) -> bool

True when the cart’s header marks a Vs. DualSystem board (two CPUs / two PPUs; NES 2.0 byte-13 high nibble = Vs. hardware type 5/6).

Detection only: this single-system core cannot boot a DualSystem title past its attract handshake, so the frontend uses this to surface a clear note. The two-CPU/two-PPU emulation is a documented v2.0 deferral (docs/audit/vs-dualsystem-design-2026-06-11.md).

Source

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). Read through the upper bits of $4016/$4017. No effect on non-Vs. carts; the standard controller read stays byte-identical.

Source

pub const fn vs_dip(&self) -> u8

Current Vs. System DIP-switch bank.

Source

pub const fn set_vs_ppu_type(&mut self, t: VsPpuType)

Override the Vs. System PPU type and re-apply the output palette.

iNES-1.0 Vs. dumps default to the 2C03 palette (no NES 2.0 byte-13); the per-game database (crate::vs_db) supplies the correct 2C04-000x / 2C05 type, which the frontend applies through this setter. Affects only the colour LUT the PPU emits through, never game logic. No effect on non-Vs. carts.

Source

pub const fn insert_coin(&mut self, acceptor: u8)

Latch a Vs. System coin insertion on the given acceptor (0 = #1, 1 = #2). Reads true for a real-hardware ~40-70 ms window; the frontend should clear it (see Self::clear_coin) after a few frames.

Source

pub const fn clear_coin(&mut self)

Clear all latched Vs. System coin-insert signals.

Source

pub const fn set_vs_service(&mut self, pressed: bool)

Set / clear the Vs. System service button.

Source

pub fn disk_side_count(&self) -> usize

Number of disk sides in the inserted FDS image. Returns 0 for cartridge builds (so a frontend can branch on “is this an FDS game?”).

Source

pub fn inserted_disk_side(&self) -> Option<usize>

The currently inserted FDS disk side index, or None when ejected (or for a cartridge build). A game that prompts “insert side B” is asking the user to call Self::set_disk_side.

Source

pub fn set_disk_side(&mut self, side: Option<usize>)

Insert FDS side i (Some(i)) or eject the disk (None). Inserting resets the head and opens a short deterministic “not ready” window (the BIOS polls $4032 and waits for ready); an out-of-range index is ignored. No-op on cartridge builds. This is how the user complies with a game’s “insert side N” prompt.

Source

pub fn enable_fds_trace(&mut self)

Start recording the diagnostic FDS read-stream trace (the $4031 disk-byte stream + $4025 control writes + side changes). Off by default and observation-only — it never affects emulation, so the determinism contract holds. Drain it with Self::take_fds_trace. No-op on cartridge builds. Used by the fds_trace diagnostic harness to debug disk-read / side-swap failures (e.g. the Kid Icarus side-B ERR.07 stall).

Source

pub fn take_fds_trace(&mut self) -> Vec<FdsTraceRec>

Drain the accumulated FDS read-stream trace records. Empty for cartridge builds or when Self::enable_fds_trace was never called.

Source

pub fn disk_image_bytes(&self) -> Vec<u8>

Re-serialize the (possibly-modified) FDS disk image to the headerless .fds byte layout so the host can write it to a side-car .fds.sav (keyed by Self::rom_sha256). Empty for cartridge builds.

Source

pub fn disk_is_dirty(&self) -> bool

Whether the FDS disk image has unsaved writes since the last Self::clear_disk_dirty. A frontend checks this on quit / periodically to decide whether to persist the disk.

Source

pub fn clear_disk_dirty(&mut self)

Clear the FDS disk dirty flag after persisting the image.

Source

pub fn set_disk_write_protected(&mut self, protected: bool)

Mark the inserted FDS disk read-only (true) or writable (false, the default). Drives the $4032 write-protect flag; a write-protected disk drops bytes in write mode without modifying the medium.

Source

pub fn set_expansion_device(&mut self, port: usize, device: Option<InputDevice>)

Attach a non-standard overlay input device on port (0 = $4016, 1 = $4017). Pass None to unplug it and return the port to the standard controller / Four Score path (byte-identical reads). Devices are unplugged on power-cycle.

§Panics

Panics if port is not in 0..=1.

Source

pub const fn expansion_device(&self, port: usize) -> &Option<InputDevice>

Borrow the overlay device attached to port (0 = $4016, 1 = $4017), if any.

§Panics

Panics if port is not in 0..=1.

Source

pub fn set_paddle(&mut self, port: usize, position: u8, fire: bool)

Attach an Arkanoid “Vaus” paddle on port (typically port 1 / $4017) and set its position + fire state. position is the raw 8-bit potentiometer value ($00 far-left .. $FF far-right); fire is the single button. Convenience wrapper that attaches the device if absent then updates it.

§Panics

Panics if port is not in 0..=1.

Source

pub fn set_zapper(&mut self, port: usize, x: u16, y: u16, trigger: bool)

Attach an NES Zapper light gun on port (typically port 1 / $4017) and set its aim point + trigger. (x, y) is the screen pixel the gun is aimed at (0..256, 0..240; out of range = off-screen); trigger is the trigger state. Convenience wrapper that attaches the device if absent then updates it.

Light detection is sampled from the framebuffer at the end of each Self::run_frame; the determinism contract holds because the sample only runs when a Zapper is attached (the no-device path is unchanged).

§Panics

Panics if port is not in 0..=1.

Source

pub fn set_power_pad(&mut self, port: usize, buttons: u16)

Attach an NES Power Pad / Family Fun Fitness mat on port (typically port 1 / $4017) and set its live button mask (bit i = mat button i+1, 0..=11). Convenience wrapper that attaches the device if absent then updates it. Opt-in: the no-device path stays byte-identical.

§Panics

Panics if port is not in 0..=1.

Source

pub fn controller_buttons(&self, port: usize) -> u8

v1.6.0 B3 — the latched standard-controller button bitmask for port (0 = P1 / $4016, 1 = P2 / $4017; 2/3 are the Four Score players), in Buttons bit order (A = bit 0 .. Right = bit 7). Read-only and side-effect-free — it reads the latched state, not the shift register, so it never perturbs a controller poll. Exposed for the Lua joypad.get query.

§Panics

Panics if port is not in 0..=3.

Source

pub fn set_snes_mouse( &mut self, port: usize, dx: i16, dy: i16, left: bool, right: bool, sensitivity: u8, )

v1.2.0 Workstream D — attach a SNES-style serial mouse on port (0 = $4016, 1 = $4017) and set its movement / buttons / sensitivity. (dx, dy) are the signed per-frame deltas (clamped to +/-127 on latch); sensitivity is 0 (low) / 1 (medium) / 2 (high). Convenience wrapper that attaches the device if absent then updates it. Opt-in: the no-device path stays byte-identical.

§Panics

Panics if port is not in 0..=1.

Source

pub fn set_family_keyboard(&mut self, port: usize, keys: [u8; 9])

v1.2.0 Workstream D — attach a Famicom Family BASIC keyboard on port (typically port 1 / $4017) and set its pressed-key bitmap. keys is one byte per matrix row (keys[row] bits 0..=3 = column-half 0 keys, bits 4..=7 = column-half 1 keys); the frontend builds it from host keys. Convenience wrapper that attaches the device if absent then updates it. Opt-in: the no-device path stays byte-identical.

§Panics

Panics if port is not in 0..=1.

Source

pub fn set_family_trainer(&mut self, port: usize, buttons: u16)

v1.3.0 Workstream F1 — attach a Bandai Family Trainer mat on port and set its 12-button mask (bit i = mat button i+1). The Family Trainer is layout-equivalent to the Power Pad and reuses its scan; this attaches the InputDevice::FamilyTrainer variant (distinct from Self::set_power_pad so the selected device round-trips through a save-state). Opt-in: the no-device path stays byte-identical.

§Panics

Panics if port is not in 0..=1.

Source

pub fn set_subor_keyboard(&mut self, port: usize, keys: [u8; 9])

v1.3.0 Workstream F1 — attach a Subor keyboard on port and set its pressed-key bitmap (one byte per matrix row, like Self::set_family_keyboard). The Subor keyboard reuses the Family BASIC keyboard matrix scan; this attaches the InputDevice::SuborKeyboard variant. Opt-in: the no-device path stays byte-identical.

§Panics

Panics if port is not in 0..=1.

Source

pub fn set_konami_hyper_shot(&mut self, port: usize, buttons: u8)

v1.3.0 Workstream F1 — attach a Konami Hyper Shot on port and set its 4-button mask (bit 0 = P1 Run, 1 = P1 Jump, 2 = P2 Run, 3 = P2 Jump). Opt-in: the no-device path stays byte-identical.

§Panics

Panics if port is not in 0..=1.

Source

pub fn set_bandai_hyper_shot(&mut self, port: usize, sensors: u8)

v1.3.0 Workstream F1 — attach a Bandai Hyper Shot (Exciting Boxing punching bag) on port and set its 8-sensor mask (bits 0..=3 = the A=0 group, bits 4..=7 = the A=1 group). Opt-in: the no-device path stays byte-identical.

§Panics

Panics if port is not in 0..=1.

Source

pub const fn set_mirroring_override(&mut self, m: Option<Mirroring>)

v1.1.0 beta.1 (T-110-B4) — set (Some) or clear (None) a per-game nametable mirroring override, a load-time correction for ROMs whose iNES header carries the wrong mirroring flag (supplied by the frontend’s game database). None (default) defers to the mapper — byte-identical, so the determinism / AccuracyCoin contract and the core test suites are unaffected (they never set it). Persisted in the save-state. Does not affect mappers with on-cart VRAM (4-screen).

Source

pub fn mapper_has_hardwired_mirroring(&self) -> bool

Whether the loaded mapper’s nametable mirroring is hardwired by the cartridge (solder pads / header bit) rather than controlled by the mapper’s own registers at runtime.

The frontend consults this before honoring a game-database mirroring correction: a static override is only valid for a hardwired board, and force-applying one to a mapper that switches mirroring itself (MMC1/3/5, AxROM, VRC, …) corrupts its rendering. See [rustynes_mappers::Mapper::has_hardwired_mirroring].

Source

pub fn poke_ram(&mut self, addr: u16, value: u8)

Write a byte directly into CPU work RAM ($0000-$1FFF). Used by the frontend’s raw RAM cheats (GameShark-style); applied after Self::run_frame, so the deterministic core run loop is unchanged (the determinism contract holds for the no-cheat path). No-op outside system RAM.

Source

pub fn debug_poke_ppu(&mut self, addr: u16, value: u8)

v1.7.0 “Forge” Workstream A1 — debugger writeback into the PPU bus ($0000-$3FFF): CHR pattern bytes (mapper ppu_write, a no-op on CHR-ROM), nametable tiles/attributes (mapper-absorbed, else CIRAM via the active mirroring), and palette RAM. The PPU-bus counterpart of Self::poke_ram.

Reached only through the frontend’s gated post-frame poke path (the same caller-side, after-Self::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.

Source

pub const fn poke_oam_byte(&mut self, idx: u8, value: u8)

v1.7.0 “Forge” Workstream A1 — debugger writeback for one OAM byte (idx = 0..256: byte 0 = Y, 1 = tile, 2 = attributes, 3 = X per sprite). debug-hooks-gated; reached only through the gated post-frame poke path, so the default build is byte-identical.

Source

pub fn debug_set_cpu_state( &mut self, a: u8, x: u8, y: u8, s: u8, p_bits: u8, pc: u16, )

v1.7.0 “Forge” Workstream B (Lua API parity) — debugger/scripted writeback of the CPU register file (a/x/y/s/p bits/pc). The structured-state counterpart of Self::poke_ram, backing the Lua emu:setState(t) field map (Mesen2 parity).

Reached only through the frontend / script crate’s gated post-frame poke path (the same caller-side, after-Self::run_frame stage the raw RAM cheats + the other debug_poke_* writebacks use), so the deterministic core run loop is unchanged and the no-edit path is byte-identical. debug-hooks-gated. p is taken as a raw status-bits byte (truncated to the defined flags, mirroring a PLP / save-state restore).

Source

pub fn peek(&mut self, addr: u16) -> u8

Read a byte from the CPU address space ($0000-$FFFF) for inspection, without the register side effects of a real CPU read — reading $2002 does not clear the VBL flag / address latch and $2007 does not advance the PPU read buffer. Used by the debugger and the Lua scripting API (emu.read); it observes state without advancing the emulator, preserving determinism.

Source

pub fn peek_ppu(&mut self, addr: u16) -> u8

v1.2.0 C3 (hd-pack) — side-effect-free read of the PPU bus ($0000-$3FFF): CHR pattern data, nametables, palette RAM. Used by the HD-pack compositor to hash a tile’s 16 CHR bytes. Observes state without advancing the emulator, preserving determinism.

Source

pub fn add_genie_code(&mut self, code: &str) -> Result<(), GenieError>

Add a Game Genie code (6 or 8 characters, case-insensitive) that substitutes a byte the CPU reads from PRG-ROM ($8000-$FFFF).

Codes are a runtime overlay — they are not part of the save-state and do not perturb the determinism contract when none are active. With codes active, the substituted bytes are part of the deterministic input (record a movie with the same codes to reproduce a run).

§Errors

Returns GenieError if the code string cannot be decoded.

Source

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.

Source

pub fn clear_genie_codes(&mut self)

Remove all active Game Genie codes.

Source

pub fn genie_codes(&self) -> impl Iterator<Item = &GenieCode>

Iterate the active Game Genie codes (address-sorted).

Source

pub fn drain_audio_into(&mut self, out: &mut [f32]) -> usize

Drain into a slice; returns the count copied. Excess samples are dropped if out is smaller than the buffered count.

Source

pub const fn rom_sha256(&self) -> &[u8; 32]

SHA-256 of the ROM bytes this emulator was constructed from.

Used by the frontend’s save-state file layout (one directory per ROM, keyed by hex-encoded SHA-256). The hash is computed once at from_rom time; subsequent calls are O(1).

Source

pub fn rom_hash_tag(&self) -> [u8; 6]

Truncated ROM hash tag stored in the save-state header.

Source

pub fn snapshot(&self) -> Vec<u8>

Encode the entire emulator state into a .rns snapshot blob.

Includes a versioned container header and the four chip + bus sections (CPU , PPU , APU , MAP , BUS ), plus an optional THM thumbnail section (128x120 RGBA8 nearest-neighbor downsample of the current framebuffer). The thumbnail is for UI slot pickers only – per ADR 0003 it is NOT part of the deterministic save-state contract.

Source

pub fn snapshot_core_into(&self, out: &mut Vec<u8>)

v2.8.0 Phase 3 — Self::snapshot minus the THM thumbnail section, encoded into a caller-owned reused buffer. The fast path for per-frame consumers (run-ahead, the netplay save-state ring): no allocation in steady state and no 61 KiB thumbnail build. The output parses with Self::restore / Self::restore_quiet exactly like a full snapshot (THM is optional by format).

Source

pub fn thumbnail(&self) -> Vec<u8>

Generate a 128x120 RGBA8 thumbnail of the current framebuffer.

Nearest-neighbor downsample (sample every 2nd pixel of every 2nd row). The 1/4-resolution result is small enough that storing it in slot files is cheap (61,440 bytes uncompressed, ~10-20 KiB after the LZ4 path the rewind ring uses if it is ever wired through there).

Per ADR 0003: NOT part of the deterministic save-state contract. Different builds may produce different pixel-perfect framebuffers at the same cycle if post-pass filters change.

Source

pub fn extract_thumbnail(data: &[u8]) -> Result<Option<Vec<u8>>, SnapshotError>

Extract a thumbnail from an .rns save-state blob without restoring it. Used by frontends to populate slot pickers.

Returns Ok(None) if the blob is well-formed but contains no thumbnail section (older v0.9.0 slot files).

§Errors

Returns SnapshotError when the container header is malformed.

Source

pub fn restore(&mut self, data: &[u8]) -> Result<(), SnapshotError>

Apply a previously Self::snapshoted blob.

Loading from a different ROM is allowed (the embedded hash tag is only a sanity check), but the result is undefined unless the chip section bodies are appropriate for the running mapper.

§Errors

Returns SnapshotError for malformed inputs.

Source

pub fn restore_quiet(&mut self, data: &[u8]) -> Result<(), SnapshotError>

v2.8.0 Phase 3 — Self::restore WITHOUT clearing the rewind ring.

For internal, machine-driven restores on the same timeline — run-ahead’s per-frame rollback and netplay’s rollback-resimulate — where the buffered rewind history remains exactly as valid as before. User-driven loads (save-state slots) keep using Self::restore, which invalidates the ring.

§Errors

Returns SnapshotError for malformed inputs.

Source

pub fn enable_rewind(&mut self)

Enable the rewind ring buffer with default capacity (32 MiB) and keyframe period (60).

Source

pub fn enable_rewind_with(&mut self, max_bytes: usize, keyframe_period: u32)

Enable rewind with explicit byte budget + keyframe period.

Source

pub fn disable_rewind(&mut self)

Disable rewind and free the buffer.

Source

pub fn rewind_capture(&mut self)

Push the current state onto the rewind ring. Frontends call this at the end of each completed frame.

No-op if rewind is disabled.

Source

pub fn rewind_step_back(&mut self) -> bool

Pop the most recent rewind entry and restore it. Returns true on success, false if the ring is empty (or rewind is disabled).

Source

pub fn rewind_clear(&mut self)

Drop every buffered rewind entry. Called when the user releases the rewind key, so subsequent forward play overwrites — there’s nothing to overwrite, but we want forward play to capture into a fresh ring rather than tail-of-old-history.

Source

pub const fn rewind_enabled(&self) -> bool

true if rewind is enabled.

Source

pub fn rewind_len(&self) -> usize

Number of buffered rewind entries.

Source

pub fn rewind_bytes_used(&self) -> usize

Approximate memory used by the rewind ring, in bytes.

Source

pub fn cpu_snapshot(&self) -> CpuDebugView

Snapshot the CPU register file.

Source

pub fn ppu_snapshot(&self) -> PpuDebugView

Snapshot PPU state for the debugger.

Source

pub fn apu_snapshot(&self) -> ApuDebugView

Snapshot APU channel outputs and IRQ flags.

Source

pub const fn set_apu_channel_mask(&mut self, mask: u8)

Set the APU per-channel enable mask (a UI playback overlay, NOT NES hardware state). Bit 0 = pulse 1, 1 = pulse 2, 2 = triangle, 3 = noise, 4 = DMC, 5 = external/mapper audio. A cleared bit mutes that channel.

The default ([rustynes_apu::CHANNEL_MASK_ALL]) is byte-identical to the un-masked mixer — the deterministic per-frame audio is unchanged unless the frontend explicitly mutes a channel. This is never written into the save state, so it never affects determinism or round-trips.

Source

pub const fn apu_channel_mask(&self) -> u8

Current APU per-channel enable mask. See Self::set_apu_channel_mask.

Source

pub fn set_apu_channel_gain(&mut self, gain: [f32; 6])

v1.4.0 Workstream C — set the APU per-channel output gain (a UI mixing overlay, NOT NES hardware state), generalizing Self::set_apu_channel_mask. Index 0 = pulse 1, 1 = pulse 2, 2 = triangle, 3 = noise, 4 = DMC, 5 = external/mapper audio. Each gain is clamped to 0.0..=2.0.

The default ([rustynes_apu::CHANNEL_GAIN_UNITY], all 1.0) is byte-identical to the un-scaled mixer — the deterministic per-frame audio is unchanged unless the frontend explicitly changes a gain. Never written into the save state, so it never affects determinism or round-trips.

Source

pub fn set_apu_filter_model(&mut self, model: FilterModel)

v2.1.3 — select the APU analog output-filter model (see [rustynes_apu::FilterModel]). The default [rustynes_apu::FilterModel::NesRf] (NES front-loader: 90 + 440 Hz HPF + 14 kHz LPF) is byte-identical to the pre-v2.1.3 output; Famicom (37 Hz HPF) and Clean (~10 Hz DC-block) drop the aggressive 440 Hz high-pass for a fuller low end. Tonal only — channel content is unchanged, and the model is never written into the save state (a frontend/config concern, re-applied at load), so determinism and round-trips are unaffected.

Source

pub const fn apu_channel_gain(&self) -> [f32; 6]

Current APU per-channel output gain. See Self::set_apu_channel_gain.

Source

pub fn oam(&self) -> [u8; 256]

Borrow OAM (256 bytes = 64 sprites x 4 bytes).

Returns a cloned [u8; 256] so the caller doesn’t have to manage a borrow lifetime against &self.

Source

pub fn oam_byte(&self, index: u8) -> u8

One OAM byte (index = 0..=255), without copying the whole 256-byte array — for single-byte readers (e.g. the Lua memory:read_oam) that would otherwise pay a full oam() copy per access. Read-only.

Source

pub const fn palette_ram(&self) -> [u8; 32]

Borrow palette RAM (32 bytes).

Source

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 loaded from a .pal file. A frontend presentation override: it re-tints the displayed RGBA framebuffer via the PPU’s colour LUT but does not touch any logical core state. None (the default) is byte-identical to the built-in palette, so AccuracyCoin + the commercial oracle (which never set one) are unaffected. Not part of the save-state.

Source

pub const fn set_extra_scanlines(&mut self, lines: u16)

v1.7.0 “Forge” Workstream F3 — set the PPU extra-scanlines overclock: the number of EXTRA idle vblank scanlines the PPU inserts per frame (at the existing dot resolution, Mesen2 UpdateTimings). Each extra line is pure additional CPU run-time — it renders nothing, sets/clears no PPU flag, and fires no VBL/NMI/A12 event, so the visible image is unchanged. 0 (the default) is byte-identical to stock NES timing — AccuracyCoin, the commercial oracle, and nestest (which never set it) are unaffected. Off by default; a frontend config knob, not part of the save-state. Distinct from the CPU-multiplier overclock (a v2.0 timebase item).

Source

pub const fn extra_scanlines(&self) -> u16

v1.7.0 F3 — the configured extra-scanline overclock count (0 = stock).

Source

pub const fn set_fast_dotloop(&mut self, enabled: bool)

v2.1.8 A1 — enable or disable the specialized visible-scanline fast dot path (a pure performance optimization for the PPU’s hottest per-dot case).

The PPU dot FSM (Ppu::tick) is the emulator’s single hottest function (~46% of a representative frame’s self-time). This knob dispatches the common “clean” visible BG-render dots (visible scanline, dots 1..=256, rendering stably enabled, no sub-dot disturbance) to a straight-line handler that runs the identical helper sequence with the statically-dead event branches pruned. Off by default and byte-identical to a build without it — proven bit-for-bit by the differential test (fast_dotloop_diff) and the full AccuracyCoin / visual-regression / nestest oracle. A frontend/config knob, NOT part of the save-state.

Source

pub const fn fast_dotloop(&self) -> bool

v2.1.8 A1 — whether the visible-scanline fast dot path is enabled (false = default, byte-identical to a build without it).

Source

pub const fn set_oam_decay(&mut self, enabled: bool)

v2.1.4 F2.3 — enable or disable the optional OAM-decay accuracy model.

The 2C02’s OAM is dynamic RAM refreshed by sprite evaluation; with rendering disabled for a while its un-refreshed 8-byte rows decay to a fixed garbage pattern. This models that (à la Mesen2’s EnableOamDecay): a row un-touched for > 3000 CPU cycles decays on the next read. Off by default and byte-identical to a decay-free core when off — AccuracyCoin, the commercial oracle, and the visual regression suites are unaffected. It is NTSC/Dendy-only (PAL’s refresh cadence masks decay). Deterministic when on (driven off the PPU’s monotonic dot counter — no wall-clock / OS RNG), and a frontend/config knob re-applied on load, NOT part of the save-state (the in-flight per-row ages are serialized as a relative age so a rollback stays deterministic; the enable flag is not).

Source

pub const fn oam_decay_enabled(&self) -> bool

v2.1.4 F2.3 — whether the optional OAM-decay model is enabled (false = default, byte-identical to a decay-free core).

Source

pub const fn set_ppu_revision(&mut self, revision: PpuRevision)

v2.1.7 P5 — select the emulated 2C02 die revision (see PpuRevision).

The PpuRevision::default (PpuRevision::Rp2c02H) models no extra quirks, so at the default this is inert and the core is byte-identical to a build without it — AccuracyCoin, the commercial oracle, and the visual / audio regression suites are unaffected. Selecting PpuRevision::Rp2c02G additionally models the OAMADDR ($2003) write-during-rendering OAM corruption glitch (Huge Insect). The selection is stored so a power-cycle re-applies it; it is config, not save-state (the corruption state it can arm already round-trips via the v6 PPU snapshot tail). Deterministic. A frontend/config knob re-applied on load, mirroring Nes::set_oam_decay.

Source

pub const fn ppu_revision(&self) -> PpuRevision

v2.1.7 P5 — the currently-selected 2C02 die revision (default PpuRevision::Rp2c02H, byte-identical).

Source

pub const fn set_power_up_palette(&mut self, init: PaletteInit)

v2.1.7 P5 — apply a power-up palette-RAM pattern (see PaletteInit).

The 2C02’s palette RAM is not cleared at power-on; this selects the power-up contents. PaletteInit::default (PaletteInit::Zeroed) keeps the established all-zero power-up palette, so at the default this is byte-identical. PaletteInit::Blargg loads the canonical blargg power-up dump for software that samples uninitialized palette RAM. Writes only palette RAM (already part of the snapshot), so no snapshot change is needed; the selection is stored so a power-cycle re-applies it. Best called at power-on (palette RAM is preserved across a warm reset, like real hardware).

Source

pub const fn power_up_palette(&self) -> PaletteInit

v2.1.7 P5 — the currently-selected power-up palette pattern (default PaletteInit::Zeroed, byte-identical).

Source

pub fn set_power_on_ram(&mut self, ram: PowerOnRam)

v2.1.7 P5 — select the power-on work-RAM fill (see PowerOnRam).

Applies the fill to the current 2 KiB work RAM (and open-bus latch) immediately and stores it so a power-cycle re-applies the same fill (power_cycle == fresh boot). PowerOnRam::default (PowerOnRam::Zeroed) is the established all-zero power-up state (byte-identical); the other variants are opt-in and deterministic, surfacing software that reads uninitialized RAM (Final Fantasy RNG, River City Ransom, Cybernoid). RAM is not consulted during reset, so applying it here is safe.

Source

pub const fn power_on_ram(&self) -> PowerOnRam

v2.1.7 P5 — the currently-selected power-on work-RAM fill (default PowerOnRam::Zeroed, byte-identical).

Source

pub const fn set_cpu_2a03_revision(&mut self, revision: Cpu2A03Revision)

v2.1.7 “Hardware Revisions & DMA Frontier” — select the emulated Ricoh 2A03 die revision, which gates the DMA unit’s “unexpected DMA” extra halt-read on the DMC-halt-overlaps-OAM-halt cycle.

Cpu2A03Revision::Rp2A03G is the default and is byte-identical to the core as it shipped before v2.1.7 (AccuracyCoin 141/141, nestest 0-diff, every committed DMA oracle ROM Passed). Cpu2A03Revision::Rp2A03H is a purely additive, opt-in accuracy knob that omits the extra read; its direction is an unverified hypothesis (no public reference emulator or test ROM models the 2A03 die-revision DMA difference — see the type docs and ADR 0033). It is deterministic and a config knob re-applied on load, NOT part of the save-state.

Source

pub const fn cpu_2a03_revision(&self) -> Cpu2A03Revision

v2.1.7 — the configured 2A03 die revision (default Cpu2A03Revision::Rp2A03G, byte-identical to the pre-v2.1.7 core).

Source

pub fn mapper_info(&self) -> MapperDebugView

Mapper debug info (bank registers, IRQ counters, mirroring, …).

Source

pub fn expansion_audio_chip(&self) -> Option<&'static str>

v1.4.0 Workstream C — the loaded mapper’s on-cart expansion-audio chip name (e.g. "VRC6", "VRC7 (OPLL)", "MMC5", "Namco 163", "Sunsoft 5B", "FDS"), or None when the board has no expansion audio (or the mapper-audio feature is compiled out). Used by the frontend to show the expansion-channel volume slider only when present, with a label.

Discovery is dynamic: it consults the cached [rustynes_mappers::MapperCaps] audio flag (true only when the mapper overrides mix_audio with the feature on) and the mapper id to name the chip family.

Source

pub fn cpu_bus_peek(&mut self, addr: u16) -> u8

Side-effect-free CPU bus peek (for the hex viewer).

Source

pub fn ppu_bus_peek(&mut self, addr: u16) -> u8

Side-effect-free PPU bus peek (for the hex viewer + visualizers).

Source

pub fn pattern_table_rgba(&mut self, table: u8) -> Vec<u8>

Render the 256 tiles of a CHR pattern table as RGBA8 (128x128).

table selects which of the two pattern tables: 0 -> $0000, 1 -> $1000. Uses BG palette 0 ($3F00-$3F03) for grayscale-ish rendering. ~80 KiB cloned; only call when the PPU pattern viewer is open.

Source

pub fn nametable_rgba(&mut self, nt: u8) -> Vec<u8>

Render a nametable as RGBA8 (256x240).

nt selects 0..=3 logical nametable. Uses the current BG pattern table base, attribute palette, and CHR data.

Auto Trait Implementations§

§

impl Freeze for Nes

§

impl !RefUnwindSafe for Nes

§

impl Send for Nes

§

impl !Sync for Nes

§

impl Unpin for Nes

§

impl UnsafeUnpin for Nes

§

impl !UnwindSafe for Nes

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.