A Game Boy (DMG) emulator written from scratch in Rust. It boots and plays commercial cartridges. The primary test target has been The Legend of Zelda: Link's Awakening.
The emulator is playable; it runs the full boot sequence, renders background and sprite graphics, accepts joypad input, and drives the game's main loop through an accurate interrupt and timer system.
| Subsystem | Status |
|---|---|
| CPU (SM83 core) | Complete - all opcodes, passes Blargg cpu_instrs |
| Memory + MBC1 mapper | Complete (512KB / 32-bank cartridges) |
| Interrupts | Complete - VBlank, STAT, timer, with EI delay |
| Timer (DIV/TIMA) | Complete |
| PPU - background | Complete (scroll, palettes, tile-data addressing) |
| PPU - sprites | Functional (8×8); see Refinements for known gaps |
| OAM DMA | Functional (instant copy) |
| Joypad input | Complete (polled) |
| Sound (APU) | Not implemented |
The emulator expects a cartridge ROM. Place it under roms/ and set the path
in main.rs:
let path = "roms/your-game.gb";Then:
cargo run --release--release is strongly recommended - debug builds run the emulation loop far
slower.
| Game Boy | Keyboard |
|---|---|
| D-pad | Arrow keys |
| A | Z |
| B | X |
| Start | Enter |
| Select | Backspace |
| (quit) | Escape |
The emulator is organized around the real hardware's component boundaries.
main.rs GameBoy struct (CPU + Bus), the frame loop, minifb window, input
cpu.rs SM83 CPU core - registers, opcode execution, interrupt dispatch
bus.rs Memory map and routing, MBC1 mapper, DIV/TIMA timer, OAM DMA
ppu.rs Picture Processing Unit - VRAM, OAM, registers, scanline rendering
joypad.rs Button state and the multiplexed 0xFF00 register
-
The Bus owns the hardware and routes access. The CPU sees memory only through
Bus::read_byte/write_byte, which dispatch each address to the right component (ROM, RAM, PPU, joypad, timer registers). -
The PPU owns its own state. VRAM, OAM, the LCD registers, the scanline counter, and the framebuffer all live in
Ppu. The Bus routes the relevant address ranges (0x8000–0x9FFF, 0xFE00–0xFE9F, 0xFF40–0xFF4B) to it. The PPU never reaches back into the Bus - instead,Ppu::tickreturns anInterruptRequestdescribing which interrupts it wants raised, and the Bus applies them. This keeps ownership clean and avoids cyclic borrows. -
Rendering is scanline-based. Each visible line is drawn when
LYadvances: the background row first, then sprites on top. By the timeLYreaches 144 (VBlank), the full frame is in the framebuffer, and the frame loop pushes it to the window. -
Timing is instruction-stepped. The CPU executes one instruction, reports its cycle cost, and the Bus advances the PPU and timer by that many cycles. The frame loop runs ~70224 cycles (one frame) per displayed frame, paced to ~60 FPS.
The CPU has a unit-test suite (75 tests) covering opcodes with real logic - flag behavior, stack operations, branching, and the subtle cases (DAA, the SP-relative arithmetic flag quirk, the carry-fold in ADC/SBC, the rotate zero-flag behaviors).
cargo testFor end-to-end CPU validation, point the ROM path at Blargg's cpu_instrs.gb.
The test ROM reports its results over the serial port, which the emulator
prints to stdout. A correct CPU prints Passed all tests.
These are known simplifications - the emulator works without them, but they're on the roadmap toward full accuracy:
- 8×16 sprite mode: sprites are currently rendered as 8×8. Games that use tall sprites (including parts of Link's Awakening) will show only the top half of affected objects until this is added.
- Sprite priority: the "behind background" priority bit is not yet honored; sprites always draw on top.
- 10-sprites-per-line limit: the hardware drops sprites beyond 10 per scanline; this limit is not enforced.
- The window layer: the third rendering layer (used for status bars and dialogue boxes) is not yet implemented.
- OAM DMA timing: the DMA copy is instantaneous rather than taking its real ~160 cycles. Games that depend on DMA timing are unaffected in practice.
- EI delay precision / STAT timing: interrupt timing is accurate enough to
pass
cpu_instrs, but some cycle-exact edge cases the Mooneye test suite probes are not yet handled. - Sound (APU): not implemented.
This emulator is released under the MIT License. See LICENSE for the full text.
The MIT License covers the emulator code only. It does not cover, and cannot grant any rights to, copyrighted game ROMs. Commercial Game Boy games remain the property of their respective copyright holders; you must supply your own legally-obtained ROMs to run them. No commercial ROMs are included in this repository, and they should not be committed to it.
Blargg's test ROMs, used for CPU validation, are freely redistributable homebrew and may be included.
- Blargg's test ROMs, for rigorous CPU validation.
- The Pan Docs, the community's reference for Game Boy hardware behavior.