A clean, extensible NES/Famicom emulator with FDS support, written in modern C++23.
- CPU -- MOS 6502 with unofficial opcodes, NMI/IRQ handling
- PPU -- Scanline-accurate rendering, sprites, sprite 0 hit, sprite overflow
- APU -- 5 channels, DMC stalling, LP/HP filters, BLEP resampling, ring buffer, DRC, HP-TPDF dithering, pseudo-stereo
- FDS -- Famicom Disk System with wavetable audio, envelope and modulation
| # | Name | Expansion Audio |
|---|---|---|
| 0 | NROM | |
| 1 | MMC1 | |
| 2 | UxROM | |
| 3 | CNROM | |
| 4 | MMC3 | |
| 5 | MMC5 | Yes |
| 7 | AxROM | |
| 9 | MMC2 | |
| 11 | Color Dreams | |
| 19 | Namco 163 | Yes |
| 24 | VRC6a | Yes |
| 26 | VRC6b | Yes |
| 69 | Sunsoft 5B | Yes |
| 85 | VRC7 | Yes |
- xBRZ -- CPU-based edge-smoothing upscaler (2x--6x)
- xBRZ GPU -- Metal compute shader accelerated xBRZ
- FSR 1 -- CPU-based AMD FidelityFX Super Resolution
- FSR 1 GPU -- Metal compute shader EASU + RCAS pipeline with zero-copy presentation
- BlipBuffer band-limited resampling with Blackman-Nuttall windowed sinc (64-tap, 256 phases)
- Hardware-accurate or enhanced Butterworth filter chains
- NES and Famicom filter profiles
- Resistance-modeled expansion audio mixing
- Dynamic rate control for drift-free playback
Auto-detection via NES 2.0 header, CRC32 database, and filename heuristics. Manual override for NTSC, PAL, and Dendy.
- Keyboard:
Z= B,X= A, right shift = Select, enter = Start, arrow keys = D-pad - SDL3 gamepad: D-pad, Start, Back/Select, A on the east face button, B on the south face button
- Gamepad bindings can be changed from the SDL3 frontend command line
- The GUI Settings panel can configure keyboard and gamepad bindings, gamepad slot, deadzone, preview scale, UI density, and audio output settings
- Frontends share a universal
mapperbus.conffile under the user configuration directory and restore supported settings on the next launch
- C++23 compiler (Clang 17+ or GCC 13+)
- Meson build system
- just command runner (optional)
- SDL3 (fetched automatically via Meson wraps)
just setup
just buildOr manually:
meson setup buildDir
meson compile -C buildDir| Option | Default | Description |
|---|---|---|
enable_sdl3 |
true |
Build SDL3 frontend |
enable_nodalkit_gui |
false |
Build NodalKit GUI frontend |
enable_cli |
true |
Build CLI frontend |
enable_tests |
true |
Build test suite |
# Standard
just run <rom.nes>
# With GPU xBRZ upscaling (2x-6x, default 3x)
just run-gpu <rom.nes> [scale]
# With GPU FSR 1 upscaling (2x-6x, default 4x)
just run-gpu-fsr <rom.nes> [scale]
# CPU xBRZ upscaling
just run-xbrz <rom.nes> [scale]
# Headless CLI
just run-cli <rom.nes>SDL3 runtime scaler hotkeys:
0or1: native scale2-6: switch to that scale factor immediatelyF9: cycle scaler mode for the active factor (CPU xBRZ,CPU FSR,GPU xBRZ,GPU FSR)F5/F7: save / load state (slot 0)Shift+F5/Shift+F7: save / load state (slot 1)F11: toggle fullscreenV: toggle vsync
mapperbus-sdl3 [options] <rom-file>
--scale N Upscale factor (2-6)
--gpu GPU-accelerated xBRZ upscaling
--gpu-fsr GPU-accelerated FSR 1 upscaling
--fsr CPU FSR 1 upscaling
--sample-rate N Audio sample rate (default: 96000)
--resampling MODE blip or cubic (default: blip)
--filter-mode MODE accurate, enhanced, or unfiltered (default: unfiltered)
--filter-profile P nes or famicom (default: nes)
--region R ntsc, pal, or dendy (default: auto)
--stereo Enable pseudo-stereo output
--dither Enable HP-TPDF dithering
--expansion-mixing M simple or resistance (default: simple)
--gamepad N SDL gamepad index (default: 0)
--gamepad-deadzone N Analog axis deadzone (default: 12000)
--gamepad-map MAP Comma-separated NES=SDL map
--gamepad2 N SDL gamepad index for player 2 (enables two-player)
--gamepad2-map MAP Player 2 gamepad mapping (same format as --gamepad-map)
Example gamepad remap:
mapperbus-sdl3 --gamepad-map a=east,b=south,up=lefty-,down=lefty+,left=leftx-,right=leftx+ <rom-file>Gamepad controls use SDL names such as a, b, x, y, start, back, dpup, dpdown, dpleft, dpright, leftshoulder, rightshoulder, and axis directions such as leftx-, leftx+, lefty-, and lefty+.
Player 1 defaults to keyboard. When --gamepad2 is provided, a second gamepad drives player 2:
mapperbus-sdl3 --gamepad2 1 <rom-file>MapperBus uses a frontend-neutral mapperbus.conf file:
[mapperbus]
format=mapperbus.conf
version=1
[frontend]
preview_scale=0
ui_density=0
audio_muted=false
[audio]
sample_rate=96000
buffer_size=2048
resampling=blip
filter_mode=unfiltered
filter_profile=nes
stereo=mono
dithering=false
expansion_mixing=simple
[input.keyboard]
up=82
down=81
left=80
right=79
a=27
b=29
start=40
select=229
[input.gamepad]
enabled=true
index=0
deadzone=12000
up=dpup
down=dpdown
left=dpleft
right=dpright
a=east
b=south
start=start
select=backDefault locations:
- macOS:
~/Library/Application Support/MapperBus/mapperbus.conf - Linux:
$XDG_CONFIG_HOME/mapperbus/mapperbus.confor~/.config/mapperbus/mapperbus.conf - Windows:
%APPDATA%\MapperBus\mapperbus.conf
just testUnit and integration suites cover bus logic, mappers, APU, audio pipeline, input mapping, configuration, region detection, emulation sessions, render integration, and synthesized CPU accuracy tests (opcode dispatch, flag behavior, unofficial opcodes).
src/
core/ # Emulation core (zero platform dependencies)
platform/ # Platform abstraction interfaces
frontends/ # SDL3, NodalKit GUI, and CLI implementations
app/ # EmulationSession and composition root
tests/
unit/ # Deterministic headless tests
integration/ # ROM-based render tests
This project is licensed under the MIT License -- see the LICENSE file for details.
Aleksandr Pavlov ckidoz@gmail.com
