A Game Boy emulator implementation written in Python — work in progress.
This emulator began by loosely following GameBoy Emulation in JavaScript and has since grown to support playable commercial DMG games, audio, battery saves, memory banking, input, and automated CPU regression testing.
The following games have been tested through normal gameplay:
| Game | Cartridge hardware | Status |
|---|---|---|
| Tetris | ROM only | Playable with graphics, input, and audio |
| Donkey Kong Land | MBC1 + RAM + battery | Playable; saving and audio work |
| Final Fantasy Adventure | MBC2 + battery | Playable; MBC2 banking and saving work |
| Kirby's Pinball Land | MBC2 + battery | Playable; graphics, banking, and saving work |
| Pokémon Blue | MBC3 + RAM + battery | Playable; dialogs, saving, and audio work |
| The Legend of Zelda: Link's Awakening | MBC5 + RAM + battery | Playable; window overlays, raster scrolling, saving, and audio work |
ROM images are not included in this repository.
Python 3.12 is recommended. Create an isolated environment and install the runtime dependencies on Windows with:
py -3.12 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -r requirements.txtOther references:
- http://www.devrs.com/gb/files/opcodes.html
- http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf
- https://github.com/Baekalfen/PyBoy
Run a ROM:
python main.py path/to/game.gbInstruction and interrupt tracing is disabled by default. Enable it when debugging with:
python main.py --trace path/to/game.gbFor performance work, run a ROM for a fixed number of frames and optionally
write a cProfile capture:
python main.py roms/tetris.gb --max-frames 60 --profile profile.stats
python main.py roms/tetris.gb --no-display --max-frames 60 --profile profile-headless.statsCount exact base and CB-prefixed opcodes without cProfile's larger overhead:
python main.py roms/tetris.gb --uncapped --no-audio --opcode-stats --max-seconds 30Script repeatable button input for profiling gameplay instead of title screens:
python main.py roms/tetris.gb --input-script input_scripts/tetris_start.json --max-frames 600 --profile profile-gameplay.statsInput scripts are JSON arrays of frame-based button events:
[
{"frame": 60, "button": "start", "pressed": true},
{"frame": 68, "button": "start", "pressed": false}
]If rendering is the bottleneck, skip displayed frames while still emulating every frame:
python main.py roms/tetris.gb --frameskip 2--frameskip 1 draws every completed frame. --frameskip 2 draws every other
completed frame, --frameskip 3 draws every third frame, and so on.
Displayed gameplay is paced to the DMG's approximately 59.73 FPS by default. Disable pacing and audio for performance measurements with:
python main.py roms/tetris.gb --uncappedUse --no-audio to retain normal frame pacing without sound.
Output volume defaults to 10 percent. Set it from 0 to 100 with:
python main.py roms/tetris.gb --volume 25When running with a window, the title bar updates about once per second with recent emulated FPS, drawn FPS, and instruction throughput.
Keyboard controls are the same on Windows, Linux, and macOS:
- D-pad: arrow keys
- A / B:
Z/X - Start: Enter or keypad Enter
- Select: Backspace
Supported cartridge hardware:
- ROM-only cartridges (
0x00,0x08,0x09) - MBC1 cartridges (
0x01,0x02,0x03) - MBC1 ROM and external RAM banking modes
- MBC2 cartridges (
0x05,0x06) with mirrored 512×4-bit internal RAM - MBC3 cartridges without a real-time clock (
0x11,0x12,0x13) - MBC5 cartridges (
0x19through0x1E), including rumble bank masks
Battery-backed cartridge RAM is loaded from a .sav file beside the ROM and
written atomically when the emulator exits normally.
Current DMG graphics support includes background tiles, OAM DMA, and 8x8 or
8x16 sprites with palettes, flips, priority, transparency, and the 10-sprites-
per-scanline limit. Window rendering, scrolling, LCD STAT interrupts, and the
line-153 LY timing behavior used by raster effects are implemented. LCD
enable and disable transitions reset the PPU state and suppress scanline and
VBlank activity while the display is off. OAM DMA currently copies immediately
rather than modeling its 160 M-cycle CPU bus restriction.
Audio support includes all four DMG channels: two square waves, Channel 1 frequency sweep, programmable wave RAM, and noise/percussion. Length counters, volume envelopes, stereo routing, cycle-timestamped register writes, and continuous SDL audio streaming are also implemented.
doctor_suite.py runs Blargg's 11 individual cpu_instrs ROMs headlessly
and compares every CPU state with Gameboy Doctor's reference traces. It stops
each ROM automatically on success, the first mismatch, or an emulator error.
Run this suite before and after CPU, memory, timer, interrupt, or PPU timing
changes.
Download the two upstream repositories into the ignored test-roms directory:
git clone --depth 1 https://github.com/retrio/gb-test-roms.git test-roms/gb-test-roms
git clone --depth 1 https://github.com/robert/gameboy-doctor.git test-roms/gameboy-doctorRun the entire suite:
python doctor_suite.pyRun selected ROM numbers:
python doctor_suite.py 1 5 10