A small, readable Game Boy (Color) emulator written in C with SDL3. The design favors a flat, globally-visible machine state and direct code over deep abstraction, so it stays easy to read and hack on. See ARCHITECTURE.md for how the pieces fit together, and TODO.md for the remaining gaps.
- Full SM83 CPU (validated against the SM83 single-step tests, 500/500 files, and Blargg).
- Dot-driven pixel-FIFO PPU (background, window, sprites); renders
dmg-acid2pixel-perfect. - All four APU channels + mixer; passes Blargg
dmg_sound12/12. - Accurate timing: an M-cycle bus with T-cycle interrupt sampling. Passes Blargg
mem_timing/instr_timingand 66/75 of the Mooneye acceptance suite (the remaining 9 need non-DMG hardware models). - Game Boy Color (CGB): colour palettes + tile attributes, VRAM/WRAM banking,
double-speed, and HDMA — CGB games (e.g. Pokémon Crystal) run in colour; DMG
games are unaffected and get a compatibility colourisation on CGB. Model is taken
from the cart header or forced with
--cgb/--dmg/--agb. - Mappers: ROM-only, MBC1 (incl. MBC1M multicart), MBC2, MBC3/MBC30 (live RTC), MBC5, MMM01, HuC1, HuC3 (RTC), BANDAI TAMA5 (RTC), and the Game Boy Camera.
- Battery SRAM and RTC persisted to
<rom>.sav(VBA/BGB/mGBA-compatible footer). - Full-machine save states, screenshots, fast-forward, and an FPS overlay.
- Two-player split-screen play over a local link cable (
--split). - Game Boy Printer (
--printer); each print is saved as a BMP. - Loads ROMs straight from
.ziparchives. - Runs natively (SDL3) and in the browser (WebAssembly,
make web) — the web build is an installable, offline PWA with an on-screen phone shell.
Requires a C23 compiler and SDL3.
brew install sdl3 pkgconf # macOS (Linux: apt install libsdl3-dev / dnf install SDL3-devel)
make # produces ./gb
make DEBUG=1 # AddressSanitizer + UBSan build
make cleanSDL3 ships no *-config script, so the Makefile locates it via pkg-config (hence
pkgconf). The Game Boy Camera shoots from a real webcam through SDL3's
cross-platform camera API (macOS / Linux / Windows); with no camera or permission it
falls back to a synthetic test image.
make web # needs the Emscripten SDK (emcc) on PATH; writes web/gb.js
open web/index.html # runs straight from file:// — no server neededThe wasm is base64-embedded into a single gb.js, so the page works from the
filesystem. Battery saves and save states are kept in the browser's IndexedDB.
Served over https:// (or localhost) it is an installable, offline PWA: a
service worker precaches the app shell, and there is an on-screen Game Boy shell
(touch D-pad + face buttons, a navigable menu, fast-forward) so it plays on a phone.
The Game Boy Camera uses the browser webcam via getUserMedia (which needs a secure
context), falling back to the synthetic scene when access isn't granted. The link
cable (--split) is native-only.
./gb path/to/rom.gb # run a ROM (a .zip containing one .gb also works)
./gb # no ROM: just show the DMG test pattern
./gb rom.gb --printer # attach a Game Boy Printer (each print → a BMP)
./gb rom.gb --split [rom2] # two-player split screen over the link cable
./gb rom.gb --boot dmg_boot.bin # boot through the (optional) DMG boot ROMHeadless mode (no window/audio) is handy for CI and quick checks:
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
./gb rom.gb --screenshot out.bmp --frames 600 # run 600 frames, dump a BMP, exitGame ROMs are not included (they are copyrighted). Put your own under roms/,
which is gitignored. Run ./gb --help for the full option and config-key list.
| Game Boy | Key | Hotkey | Key | |
|---|---|---|---|---|
| D-pad | Arrow keys | Emulation speed ×1–×9 | 1–9 | |
| A | X | Fast-forward (hold) | Tab | |
| B | Z | Save / load state | F5/F6 | |
| Start | Enter | Restore autosave | F7 | |
| Select | Backspace | Screenshot (BMP) | P | |
| Reset (keeps battery SRAM) | R | |||
| Toggle fullscreen | Esc | |||
| Toggle FPS overlay | F | |||
| Toggle audio mute | M | |||
| Pause / resume | Space | |||
| Quit | Cmd+Q |
All bindings are configurable in gb.conf (a flat key = value file, loaded from
the working directory). It also sets the window scale, turbo speed, an optional DMG
boot ROM, and folders for saves/screenshots. See ./gb --help for every key and its
default. In --split mode, player 1 (left screen) uses WASD + G/H/T/Y and
player 2 (right screen) uses the arrow keys + K/L/RShift/Enter.
The emulator's own code is released under the MIT license (LICENSE). It
contains no Nintendo code and no ROMs: the optional DMG boot ROM and all game/test
ROMs must be supplied by you, and are never committed to the repository. The only
vendored third-party code is src/puff.c (zlib's public-domain inflate, for .zip
loading), which keeps its own notice.
