A Game Boy Color emulator written in Zig with DMG (original Game Boy) backward compatibility.
This emulator uses a clean architecture with the following design principles:
- Single Source of Truth: All I/O state lives in
IoRegisters- no duplication - Typed References: Components hold typed pointers (
*IoRegisters,*Mmu) - no callbacks or*anyopaque - Complete Initialization: Factory function returns a fully-valid GameBoy instance
- Variable PPU Timing: Mode 3 duration calculated per-scanline based on sprites/scroll/window
- Full Sharp SM83 (LR35902) instruction set implementation
- All 256 main opcodes and 256 CB-prefixed opcodes
- Accurate cycle timing
- HALT and STOP mode support
- Interrupt handling (V-Blank, LCD STAT, Timer, Serial, Joypad)
- HALT bug emulation
- Scanline-based renderer (160x144 resolution)
- Background and window layer rendering
- Sprite rendering (40 sprites, 10 per scanline limit)
- DMG 4-shade palette support
- GBC color palettes (8 background + 8 sprite palettes, 32,768 colors)
- GBC VRAM banking (2 banks)
- Variable Mode 3 timing based on:
- Number of sprites on scanline
- SCX scroll position
- Window activation
- 4 audio channels:
- Channel 1: Square wave with frequency sweep
- Channel 2: Square wave
- Channel 3: Programmable wave
- Channel 4: Noise (LFSR-based)
- Frame sequencer for length, envelope, and sweep clocking
- Stereo output with per-channel panning
- 44.1 kHz sample rate
- Full 64KB address space emulation
- Memory Bank Controllers:
- ROM only (no MBC)
- MBC1 (with RAM/ROM banking modes)
- MBC2
- MBC3 (with RTC support)
- MBC5
- Battery-backed save RAM (.sav files)
- GBC WRAM banking (8 banks)
- DIV counter (16-bit internal, 8-bit exposed)
- TIMA/TMA/TAC with falling-edge detection
- TAC glitch emulation
- Keyboard-based joypad emulation
- Joypad interrupt support
- Double-speed CPU mode (8.38 MHz)
- HDMA and General Purpose DMA
- VRAM bank switching
- WRAM bank switching
- Color palettes (background and sprite)
- Speed switch via STOP instruction
brew install sdl3zig buildzig build run -- path/to/rom.gbOr run the compiled binary directly:
./zig-out/bin/zig-gbc path/to/rom.gbzig build test| Action | Keys |
|---|---|
| D-Pad Up | Arrow Up, W |
| D-Pad Down | Arrow Down, S |
| D-Pad Left | Arrow Left, A |
| D-Pad Right | Arrow Right, D |
| A Button | Z, J |
| B Button | X, K |
| Start | Enter |
| Select | Backspace, Right Shift |
| Quit | Escape |
| Extension | Description |
|---|---|
.gb |
Game Boy ROM |
.gbc |
Game Boy Color ROM |
.sav |
Battery-backed save file (created automatically) |
The emulator aims to be compatible with most commercial Game Boy and Game Boy Color games. Compatibility can be validated using test ROMs:
- blargg's cpu_instrs: CPU instruction tests
- blargg's instr_timing: Instruction timing tests
- mooneye-gb: Timer accuracy tests
zig-gbc/
├── build.zig # Build configuration
├── build.zig.zon # Dependencies
├── src/
│ ├── main.zig # Entry point, SDL event loop
│ ├── gameboy.zig # Top-level orchestrator
│ ├── core/ # CPU implementation
│ │ ├── cpu.zig # SM83 CPU core
│ │ ├── opcodes.zig # All instructions (main + CB)
│ │ └── registers.zig # CPU register file
│ ├── memory/ # Memory subsystem
│ │ ├── mmu.zig # Memory Management Unit
│ │ ├── io_registers.zig # Central I/O state (KEY FILE)
│ │ └── cartridge.zig # ROM/MBC handling
│ ├── graphics/ # Graphics
│ │ └── ppu.zig # PPU with variable Mode 3 timing
│ ├── audio/ # Audio
│ │ ├── apu.zig # Audio Processing Unit
│ │ └── channels.zig # Sound channels 1-4
│ ├── peripherals/ # I/O devices
│ │ ├── timer.zig # DIV/TIMA with falling-edge
│ │ └── joypad.zig # Input handling
│ └── platform/ # Frontend
│ └── sdl.zig # SDL3 wrapper
└── tests/
└── cpu_tests.zig # CPU unit tests
io_registers.zig: Central I/O state - single source of truth for all hardware registersgameboy.zig: Factory function creates fully-initialized emulator with all components wiredmmu.zig: Memory routing with typed references to PPU, APU, Cartridgeppu.zig: Variable Mode 3 timing calculated per-scanline
| Address Range | Description |
|---|---|
| 0x0000-0x3FFF | ROM Bank 0 (16 KB) |
| 0x4000-0x7FFF | ROM Bank N (switchable, 16 KB) |
| 0x8000-0x9FFF | VRAM (8 KB, 2 banks on GBC) |
| 0xA000-0xBFFF | External RAM (8 KB) |
| 0xC000-0xCFFF | WRAM Bank 0 (4 KB) |
| 0xD000-0xDFFF | WRAM Bank 1-7 (4 KB, switchable on GBC) |
| 0xE000-0xFDFF | Echo RAM |
| 0xFE00-0xFE9F | OAM (160 bytes) |
| 0xFEA0-0xFEFF | Unusable |
| 0xFF00-0xFF7F | I/O Registers |
| 0xFF80-0xFFFE | HRAM (127 bytes) |
| 0xFFFF | Interrupt Enable |
- CPU Clock: 4.194304 MHz (DMG) / 8.388608 MHz (GBC double speed)
- Frame Rate: ~59.7 FPS
- Dots per Scanline: 456
- Scanlines per Frame: 154 (144 visible + 10 V-blank)
- Mode 3 Duration: 172-289 dots (variable based on sprites/scroll/window)
| Mode | Duration | Description |
|---|---|---|
| Mode 2 (OAM Scan) | 80 dots | Scanning OAM for sprites |
| Mode 3 (Drawing) | 172-289 dots | Rendering pixels to LCD |
| Mode 0 (H-Blank) | 87-204 dots | Horizontal blanking |
| Mode 1 (V-Blank) | 4560 dots | Vertical blanking (10 lines) |
- Save States: Save/load state functionality
- Super Game Boy (SGB): SGB borders and enhanced features
- Game Boy Camera/Printer: Peripherals not emulated
- Link Cable: Serial communication not functional
- MBC6, MBC7, HuC1, HuC3: Uncommon memory bank controllers
- Rumble: MBC5 rumble motor feature
- Scanline-based rendering: Not pixel-accurate; some mid-scanline effects may not work
- Audio timing: Minor timing differences may cause subtle audio artifacts
- STOP instruction: Speed switching works, but some edge cases may differ from hardware
This project is provided as-is for educational purposes.
- Pan Docs - Comprehensive Game Boy technical reference
- gbdev.io - Game Boy development community
- Gameboy CPU Manual
- The Cycle-Accurate Game Boy Docs