MyNES is a ground-up C11 rewrite of a NES emulator I first wrote in
2012. The old code is preserved on the
legacy branch; this
branch is the clean-start version.
The rewrite has two ideas behind it:
- Timing is data. The 6502 CPU is described as declarative microcode in a small Scheme-compiled DSL. The compiler generates the cycle-by-cycle C state machine, including helper knowledge the rest of the emulator needs for DMA, dummy reads, interrupt timing, and bus side effects.
- Video starts as a signal, not RGB. The NES PPU does not output pixels in red, green, and blue. It outputs a composite waveform. MyNES generates that waveform first, then decodes or displays it through the same kinds of stages a television would use.
Same ROM, two rendering paths. Left: the SDL2 frontend. Right: the experimental GPU frontend running the analog signal / CRT pipeline.
The normal frontend is mynes, an SDL2 desktop emulator intended for
playing games. It uses the shared C core plus a CPU composite pipeline
for palette, NTSC/PAL signal, audio, input, screenshots, fullscreen, and
a ROM browser.
The experimental frontend is mynes_gpu, an SDL3 + SDL_GPU signal-chain
lab. It models the path from the 2C02 DAC to CRT glass as GPU compute
stages: console output, cable, RF, comb filtering, chroma demodulation,
luma filtering, matrix decode, video amp, electron beam, phosphor mask,
glass, and room/display effects. It is for exploring analog video
physics, presets, PAL/NTSC behaviour, and per-stage debugging rather than
being the default way to play.
The project is accuracy-oriented, but still in active development. The core has unit tests, AccuracyCoin coverage, Blargg-style ROM-test support, and a hook/trace/debugging layer for investigating timing bugs. Run the tests in your local build for the current pass/fail state.
The core is platform-agnostic C11. SDL, GPU APIs, audio devices, windows,
and file browsers live in frontends/; the emulator library itself does
not depend on them.
CPU: src/cpu/nes6502.dsl describes all official and unofficial
6502 opcodes as reusable cycle patterns. tools/dsl2c.scm generates
cpu_step() and bus-introspection helpers such as "what address would
the next read use?" and "is the next microcycle a write?". That derived
knowledge is used by DMC DMA and edge-case store instructions instead of
maintaining a second hand-written timing model.
System timing: src/nes/nes.h is the master scheduler. Each
nes_step() advances one CPU cycle worth of master-clock time, interleaves
PPU dots around CPU bus access, handles OAM DMA and DMC DMA cycle
stealing, propagates APU/mapper IRQs, and dispatches debug hooks.
PPU/APU/mappers: the PPU runs dot-by-dot, the APU produces filtered audio, and cartridge mapper logic is isolated behind a mapper interface. The supported mapper set is focused on real games and test ROMs rather than trying to cover every board at once.
Composite video: src/nes/composite.h is the CPU path used by the
SDL2 frontend, headless renderer, and tests. It generates the 2C02/2C07
waveform, bandwidth-limits it, demodulates chroma, matrix-decodes it, and
applies the lightweight display treatment needed for real-time play.
GPU signal chain: frontends/gpu/ contains the physical signal-chain
frontend. The CPU uploads the generated waveform; compute shaders handle
Y/C separation, PAL chroma correction, luma/chroma filtering, RGB decode,
deflection maps, beam deposition, temporal persistence, mask/glass, and
tone mapping. The connection type is not just a quality slider: RF,
composite, S-Video, RGB, and direct paths activate different stages
because different cables bypass different real electronics.
Required for the core and tests:
- CMake 3.16+
- A C11 compiler
- Chicken Scheme (
csi) if you want to regenerate CPU code from the DSL locally. Pre-generated CPU code is committed as a fallback.
Optional frontends/tools:
- SDL2 development headers for
mynes - SDL3 development headers for
mynes_gpu glslcfor GLSL to SPIR-V shader compilationspirv-crossfor SPIR-V to Metal Shading Language on macOS
Use an out-of-source build:
mkdir build
cd build
cmake ..
cmake --build . -jThe main build options are:
cmake .. -DNES_BUILD_TESTS=ON # default: ON
cmake .. -DNES_BUILD_FRONTENDS=ON # default: ON
cmake .. -DNES_BUILD_GPU_FRONTEND=ON # default: OFF, requires SDL3From the build directory:
./bin/mynes # open the fullscreen ROM browser
./bin/mynes path/to/game.nes # run a ROM directlyThe repository does not include commercial ROMs. Keep local ROMs in
roms/; that directory is ignored by git.
Build with SDL3 support enabled:
cmake .. -DNES_BUILD_GPU_FRONTEND=ON
cmake --build . -jRun it the same way:
./bin/mynes_gpu
./bin/mynes_gpu path/to/game.nesShaders are built automatically into the build tree. To rebuild them manually from the repository root:
./frontends/gpu/shaders/compile_shaders.shThe GPU frontend restores its last preset from
~/.config/mynes/config.json. Press M in-game for the on-screen
signal-chain menu, P to cycle presets, C to switch composite/raw
views, Shift+C for split view, and O to reopen the ROM browser.
Build with tests enabled, then run:
ctest --test-dir build --output-on-failureUseful individual targets and tools:
cmake --build build --target test_cpu test_cpu_cycles test_ppu test_nes test_rom
cmake --build build --target accuracy_coin
./build/bin/test_runner tests/nes-test-roms/.../some-test.nes --blarggGPU-related tests are available when NES_BUILD_GPU_FRONTEND=ON:
ctest --test-dir build -R 'gpu_kernel|preset|signal_precompute|video_chain' --output-on-failureThe docs are part of the project, not a generated afterthought:
- Architecture overview explains the core components and data flow.
- Timing covers master-clock scheduling, DMA, interrupts, and region timing.
- Testing infrastructure describes unit, AccuracyCoin, Blargg, script, and trace workflows.
- DSL design and DSL reference explain the CPU microcode language and compiler.
- GPU pipeline design and pipeline reference document the palette-index-to-phosphor path.
- GPU frontend design preserves the design plan and implementation notes for the SDL_GPU frontend.
- Blog notes are a narrative series about the emulator's main ideas: timing as data, compiler-derived DMA behaviour, composite signal generation, CRT stages, comb filtering, beam physics, presets, and why the CPU and GPU pipelines both exist.
- Debugging workflows and development commands are the operational guides.
include/nes/ Public API headers
src/cpu/ 6502 DSL source and generated CPU interface
src/nes/ System integration, APU, mappers, ROM loading,
composite video, hooks, tracing
src/ppu/ Dot-accurate PPU
frontends/sdl/ SDL2 playable frontend
frontends/gpu/ SDL3 + SDL_GPU analog signal-chain frontend
frontends/headless/ Non-interactive runner
tests/ Unit tests, integration tests, AccuracyCoin,
and test ROM fixtures
tools/ DSL compiler, test runner, visualiser tools
docs/ Architecture, debugging, GPU pipeline, and blog notes
presets/ GPU frontend physical display presets
See LICENSE.