1b4db3a506671d02181495c86ffbbb760c547f22
Some checks failed
CI / rust (push) Has been cancelled
Three audio bugs fixed: 1. DMC loop mode skipped the last byte of each sample iteration. provide_dmc_dma_byte() was immediately setting dmc_dma_request on loop restart while the sample buffer was still full, causing the while-loop in clock_cpu_cycles to service a second DMA immediately and overwrite the valid buffer. Per NES hardware spec, the reader only fills an empty buffer — the request is now left to clock_dmc when the output unit actually empties the buffer into the shift register. Fixes intermittent clicking/crackling in games that use looped DMC samples (BGM, SFX). 2. Missing DC blocker (high-pass filter) in AudioMixer. The NES APU has a capacitor-coupled output stage that blocks DC bias. Without it, abrupt channel state changes (length counter expiry, sweep mute, triangle period < 2) produce DC steps that manifest as audible clicks. Added a one-pole IIR high-pass filter at ~5 Hz applied after the existing low-pass filter. 3. cpal stream was opened at application startup with BufferSize::Fixed(256), forcing PipeWire/PulseAudio to run the entire audio graph at a 5.3 ms quantum. This disrupted other audio applications (browsers, media players) even when no ROM was loaded. Fixed by: (a) creating the stream lazily on the first push_samples call so no device is touched until a ROM is running, and (b) switching to BufferSize::Default so the audio server chooses the quantum instead of the emulator imposing one. Ring buffer capacity increased from 1536 to 4096 samples to absorb larger server quanta.
nesemu
NES/Famicom emulation workspace in Rust.
The workspace is built around a reusable core library. It also contains optional adapter crates and a GTK4 desktop frontend for manual testing.
What Is Here
nesemu: core emulation librarynesemu-adapter-api: backend-agnostic adapter traitsnesemu-adapter-headless: null/headless adapter implementationsnesemu-desktop: GTK4 desktop frontend
What The Core Library Provides
- CPU, PPU, APU, bus, and cartridge mapper emulation
- iNES ROM parsing
- Save/load state support
- Host-facing runtime wrappers for frame execution and pacing
- Public API and behavior tests
Quick Start
Add the main crate as a dependency:
[dependencies]
nesemu = { path = "../nesemu" }
Enable optional adapter support if needed:
[dependencies]
nesemu = { path = "../nesemu", features = ["adapter-api", "adapter-headless"] }
Recommended import style:
use nesemu::prelude::*;
Minimal setup:
use nesemu::{Cpu6502, NativeBus, create_mapper, parse_rom};
let rom_bytes = std::fs::read("game.nes")?;
let rom = parse_rom(&rom_bytes)?;
let mapper = create_mapper(rom)?;
let mut bus = NativeBus::new(mapper);
let mut cpu = Cpu6502::default();
cpu.reset(&mut bus);
Higher-level runtime setup:
use nesemu::{FRAME_RGBA_BYTES, NesRuntime};
let rom_bytes = std::fs::read("game.nes")?;
let mut runtime = NesRuntime::from_rom_bytes(&rom_bytes)?;
runtime.run_until_frame_complete()?;
let mut rgba = vec![0; FRAME_RGBA_BYTES];
runtime.render_frame_rgba(&mut rgba)?;
Desktop Frontend
Run the GTK4 desktop frontend:
cargo run -p nesemu-desktop -- path/to/game.nes
Linux build requirements: GTK4 development packages and pkg-config (for example on Debian/Ubuntu: libgtk-4-dev pkg-config).
Controls:
Esc: quitP: pause/resumeOpen ROM: load.nesfile- Arrow keys: D-pad
X: AZ: BEnter: StartLeft Shift/Right Shift: Select
Development
cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
Documentation Map
- API Contract: supported external surface and stability expectations
- Integration Guide: how to embed the library into a host or frontend
- Architecture: internal module layout and layering
Description
Languages
Rust
100%