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 (macOS) and Vulkan (Linux) compute shader accelerated xBRZ
- FSR 1 -- CPU-based AMD FidelityFX Super Resolution
- FSR 1 GPU -- Metal and Vulkan 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 and Ninja (Meson 1.11+ for the optional NodalKit GUI)
- just command runner (optional)
- SDL3 (fetched automatically via Meson wraps)
glslangValidatorfor Vulkan GPU upscaling (optional; shaders are compiled and embedded at build time)
just setup
just buildOr manually:
meson setup buildDir
meson compile -C buildDirThe SDL3 frontend supports Wayland and X11. The optional NodalKit GUI currently requires a Wayland session. The CLI runs without a display or audio device.
On Arch Linux, install the build and desktop dependencies:
sudo pacman -S --needed base-devel git meson ninja pkgconf sdl3 \
wayland wayland-protocols libxkbcommon freetype2 fontconfig harfbuzz glib2 \
glslang vulkan-headers vulkan-icd-loaderVulkan rendering also needs the Vulkan driver for your GPU. To build all frontends with Vulkan upscaling enabled:
meson setup buildDir -Denable_nodalkit_gui=true -Dvulkan_shaders=enabled
meson compile -C buildDir
meson test -C buildDir --print-errorlogs
./buildDir/src/frontends/nodalkit/mapperbus-guiAdd --reconfigure to the setup command when using an existing build directory.
Install the executables, GUI toolkit runtime, desktop launcher, and icon with:
sudo meson install -C buildDir --tags runtimeWithout glslangValidator, the default build still provides CPU upscaling;
requesting an unavailable GPU upscaler uses nearest-neighbor scaling. Set
-Dvulkan_shaders=disabled to explicitly omit Vulkan shaders. Shadercross, when
installed as a system library, provides an alternative HLSL compilation path.
For a build with only the CLI and tests:
meson setup buildDir-headless -Denable_sdl3=false -Denable_nodalkit_gui=false
meson compile -C buildDir-headless
meson test -C buildDir-headless --print-errorlogs| 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 |
vulkan_shaders |
auto |
Embed Vulkan GPU upscaler shaders (auto, enabled, disabled) |
# 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
On Linux, empty or relative XDG_CONFIG_HOME values use the ~/.config fallback.
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).
The sdl3-gpu suite checks GPU readback, shader colors, and vsync device lifetime.
It skips cases requiring an unavailable display or GPU backend. To require a
compatible GPU device during validation:
MAPPERBUS_REQUIRE_GPU=1 meson test -C buildDir sdl3-gpu --print-errorlogssrc/
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 [email protected]
