> π¦ **Curious what happens when you hand the same project to an AI team?** Check out [**rubc**](https://github.com/duysqubix/rubc) β a sister Game Boy / Game Boy Color emulator written in Rust, built almost entirely by an AI as an experiment. It went a good bit further on hardware accuracy than this one did. `gobc` is the hand-written, human original that started it all.
**gobc** is a Game Boy / Game Boy Color emulator written in Go. Standing on the
shoulders of giants β Pan Docs, SameBoy, PyBoy, mooneye, gbdev β it aims to be
a fast, hackable, hardware-accurate emulator with a real focus on passing the
canonical test suites (Blargg, Mooneye) rather than just "running games".
[**π Tests passing β**](TESTS.md)
[**π΅ Audio setup β**](docs/audio_tests.md)
[**π¬ Discord β**](https://discord.gg/EVCX5X3A)
[**π¦ Latest release β**](https://github.com/duysqubix/gobc/releases/latest)
## Feature matrix
Legend: β
= full hardware accuracy (regression-guarded in CI) Β· π‘ = partial / WIP Β· β = not started
| Subsystem | Status | Notes |
|---|:-:|---|
| SM83 CPU | β
| All 245 implemented opcodes + CB-prefix; passes Blargg `cpu_instrs` 11/11 + `instr_timing`. Atomic-instruction model β sub-instruction T-cycle timing is [#18](https://github.com/duysqubix/gobc/issues/18). |
| Interrupts | β
| EI 1-instruction delay, HALT bug, peripherals ticked during ISR. Passes `interrupt_time` and `halt_bug`. |
| Joypad | β
| All 8 buttons, D-pad + face buttons + Start/Select. |
| Timers (DIV/TIMA) | β
| Including CGB double-speed scaling. |
| LCD / PPU | β
| Tile + sprite render, STAT interrupts, mode 0/1/2/3 transitions, BG-OBJ priority. Pixel-FIFO accuracy and cycle-accurate mode 2 timing tracked in [#19](https://github.com/duysqubix/gobc/issues/19). |
| **APU (sound)** | β
| **NEW in v2.0.** Full 4-channel emulation on `gopxl/beep/v2`. Square Γ 2 with NR10 sweep, wave with 32-sample wave RAM, noise with 7/15-bit LFSR, frame sequencer at 512 Hz. **Passes all 12/12 Blargg `dmg_sound` AND all 12/12 `cgb_sound`.** Setup guide: [`docs/audio_tests.md`](docs/audio_tests.md). |
| CGB mode | β
| BG / OBJ palette RAM, VRAM bank switching, double-speed switching via STOP + KEY1, APU scaling. |
| Serial port | β | Output captured for test ROMs; full serial transfers / link cable: [#11](https://github.com/duysqubix/gobc/issues/11). |
| Save / load states | β
| Snapshot the full Motherboard (CPU + memory + cart + APU + PPU). |
| Debugger | β
| VRAM viewer, tile data + tilemap, CPU registers, IO regs, cart RAM browser, breakpoints, single-step. |
| Shaders | β | CRT / LCD / GBC palette post-processing: [#17](https://github.com/duysqubix/gobc/issues/17). |
### Cartridge MBC support
| MBC | Status | Issue |
|---|:-:|---|
| ROM_ONLY (no MBC) | β
| β |
| MBC1 (+ RAM + BATTERY) | β
| β |
| **MBC2** | β | [#3](https://github.com/duysqubix/gobc/issues/3) |
| MBC3 (+ RTC + RAM + BATTERY) | β
| β |
| MBC5 (+ RAM + BATTERY + RUMBLE) | β
| β |
| **MMM01** (multi-game compilations) | β | [#14](https://github.com/duysqubix/gobc/issues/14) |
| **HuC1** (Hudson IR) | β | [#12](https://github.com/duysqubix/gobc/issues/12) |
| **HuC3** (Hudson IR + RTC + speaker) | β | [#13](https://github.com/duysqubix/gobc/issues/13) |
| **Pocket Camera** ($FC) | β | [#15](https://github.com/duysqubix/gobc/issues/15) |
| **Bandai TAMA5** ($FD) | β | [#16](https://github.com/duysqubix/gobc/issues/16) |
### Blargg test ROM scorecard (regression-guarded in CI)
| Suite | gobc v2.2 | Notes |
|---|:-:|---|
| `cpu_instrs` | β
PASS | All 11 sub-tests. |
| `instr_timing` | β
PASS | |
| `halt_bug` | β
PASS | |
| `interrupt_time` | β
PASS | Both DMG and CGB double-speed iterations. |
| `dmg_sound` | β
**12/12** | All 6 DMG quirks (length-clock-on-trigger, NR41 power-off, wave RAM bus contention, wave retrigger corruption, etc.). |
| `cgb_sound` | β
**12/12** | DMG-vs-CGB-aware quirks; same code paths gated on `mb.Cgb`. |
| `oam_bug` | π‘ **6/8** with `--force-dmg` | Sub-tests 2β7 PASS. Sub-test 1 (LCD-on cycle sync) + sub-test 8 (POP CRC under cumulative delay-loop corruption) tracked in [#19](https://github.com/duysqubix/gobc/issues/19). Use `gobc --no-gui --force-dmg oam_bug.gb` β the test ROM's header is CGB-flagged but it exercises DMG-only hardware quirks. |
| `mem_timing` / `mem_timing-2` | β | Requires sub-instruction T-cycle accurate CPU. Tracked in [#18](https://github.com/duysqubix/gobc/issues/18). |
## Installing
You need:
- [Go 1.26+](https://golang.org/doc/install)
- [OpenGL + GLFW](https://github.com/gopxl/pixel#requirements) (`libgl1-mesa-dev xorg-dev` on Debian/Ubuntu)
- Audio (optional): on WSL2, follow the one-time setup in [`docs/audio_tests.md`](docs/audio_tests.md#wsl2-setup-one-time)
```bash
# Option 1 β pre-built release binary (Linux x86_64)
curl -L https://github.com/duysqubix/gobc/releases/latest/download/gobc-v2.0-linux-x86_64 -o gobc
chmod +x gobc
# Option 2 β go install
go install github.com/duysqubix/gobc/cmd/gobc@latest
# Option 3 β build from source
git clone https://github.com/duysqubix/gobc && cd gobc
cargo install just # or: brew install just / apt install just
just bootstrap # installs Go (if missing) + gopls + staticcheck + dlv + goimports
just install-hooks # wires the repo pre-commit hook (gofmt + vet + staticcheck)
just build # vet + race-test + compile to bin/gobc + bin/cartdump
just build-release # stripped (-trimpath -s -w) release binary
```
The project uses [`just`](https://github.com/casey/just) as its task runner. Run `just` with no
arguments to list every recipe. Useful environment overrides:
| Env | Effect |
|---|---|
| `GO_VERSION=1.27.0 just bootstrap` | Pin a specific Go release. |
| `GO_INSTALL_DIR=$HOME/.local just bootstrap` | Install Go to a user-local prefix (no sudo). |
| `COVER_MIN=70 just test-cover-check` | Fail if line coverage drops below 70 %. |
## Usage
`gobc` exposes two subcommands plus a shorthand:
| Command | Purpose |
|---|---|
| `gobc run ROM_File [options]` | Boot the emulator and run a ROM. |
| `gobc cartdump ROM_File [options]` | Dump cartridge header (and optional opcode disassembly). |
| `gobc ROM_File [options]` | Shorthand for `gobc run`. |
```bash
# Run a ROM
gobc run roms/cpu_instrs.gb
gobc run roms/zelda.gb --debug --breakpoints 0x100,0x200,0x300
gobc run roms/pokemon.gb --force-cgb # force CGB on a DMG ROM
gobc run roms/blargg.gb --no-gui # headless (CI / test ROMs)
LOG_LEVEL=debug gobc run roms/zelda.gb
# Audio (new in v2.0)
gobc run roms/crystal.gbc --audio-rate 32000 # match host throughput on slow CPUs
gobc run roms/crystal.gbc --audio-smooth # calibrate to host (5% pitch trade-off)
gobc run roms/crystal.gbc --no-audio # silent run
# Inspect a cartridge
gobc cartdump roms/pokemon.gb # writes cartdump.txt
gobc cartdump --raw roms/pokemon.gb # raw header to stdout
gobc cartdump --instruction-set --include-nop -o dump.txt roms/pokemon.gb
```
Run `gobc --help` for the full flag reference and `gobc --help` for per-subcommand help.
## Key bindings
### Main window
| Key | Action |
|---|---|
| `F1` | Toggle gridlines |
| `F2` | Toggle debug viewer windows (VRAM / Memory / Cart / CPU / IO) |
| `F3` | Cycle DMG palette |
| `F4` | Save cartridge SRAM to `.sav` |
| `F5` | Save state to `.state` |
| `F6` | Load state from `.state` |
| `A` | Game Boy B button |
| `S` | Game Boy A button |
| `Enter` | Start |
| `Shift` | Select |
| Arrow keys | D-pad |
| `Space` *(debug on)* | Pause / resume emulation |
| `N` *(debug on)* | Step **N** CPU cycles |
| `M` / `B` *(debug on)* | Increase / decrease cycles-per-frame 10Γ |
| `F` *(debug on)* | Step one frame |
### Debug viewers
| Window | Keys |
|---|---|
| VRAM | `T` toggle tile addressing Β· `B` toggle tilemap addressing Β· `G` toggle grid Β· `V` toggle VRAM bank 0/1 |
| Memory | Arrow keys page/scroll Β· mouse wheel scrolls |
| Cart | Arrow keys page/scroll Β· `[` / `]` switch RAM bank |
## Testing
Unit tests use the stdlib `testing` package + `testify`. ROM integration tests under
`default_rom/` run `gobc --no-gui` against Blargg ROMs and grep the serial output (or
cart-RAM `.sav` for the newer `dmg_sound`/`cgb_sound`/`oam_bug` suites).
```bash
just test # go test -race ./...
just test-cover # writes coverage.out + summary
just test-cover-html # generates coverage.html
COVER_MIN=70 just test-cover-check # fail if coverage drops below threshold
just bench # benchmarks
just test-rom-audio # runs the full 12-ROM dmg_sound matrix
```
Every push runs the full test pipeline + ROM integration suite on GitHub Actions; coverage
is uploaded to [Codecov](https://codecov.io/gh/duysqubix/gobc).
## Known game bugs
| Game | Symptom | Issue |
|---|---|---|
| Link's Awakening (DMG) | Main menu doesn't render | [#5](https://github.com/duysqubix/gobc/issues/5) |
| PokΓ©mon Crystal | Battle scene doesn't animate | [#4](https://github.com/duysqubix/gobc/issues/4) |
| PokΓ©mon Crystal | Character sprite split entering battle | [#4](https://github.com/duysqubix/gobc/issues/4) |
## Project layout
```
gobc/
βββ cmd/
β βββ gobc/ # main binary; urfave/cli/v2 app with run + cartdump subcommands
β βββ cartdump/ # standalone cart-dump binary (same logic as gobc cartdump)
βββ internal/
β βββ motherboard/ # CPU + opcodes + memory + timer + interrupts + PPU + APU
β βββ cartridge/ # header parser + ROM_ONLY / MBC1 / MBC3+RTC / MBC5
β βββ windows/ # Pixel/GLFW GUI: 1 main + 5 viewer windows
β βββ bootrom/ # DMG + CGB boot ROMs as hex blobs
β βββ root.go # shared utilities: Logger, constants, bit-ops, state save/load
βββ default_rom/ # Blargg + Mooneye test ROMs
βββ docs/ # audio_tests.md and project docs
βββ .githooks/ # repo-tracked pre-commit hook
βββ .github/workflows/ # CI: unit tests + coverage + ROM integration
βββ justfile # task runner (run `just` to list recipes)
```
Each subdirectory has its own `AGENTS.md` with conventions, anti-patterns, and quirks. Start
with [`AGENTS.md`](AGENTS.md) (root) before contributing.
## Contributing
The known problems are tracked in the Issues tab β grab anything that looks interesting!
For substantial new features, open an issue first to align on the approach. We follow the
existing per-package conventions (see each subdirectory's `AGENTS.md`) and the pre-commit
hook enforces `gofmt`, `vet`, and `staticcheck`. Tests must stay green.
### Contributors
- Duan Uys β [@duysqubix](https://github.com/duysqubix)
## References
- [Pan Docs](https://gbdev.io/pandocs/About.html) β canonical Game Boy hardware reference
- [GBEDG](https://hacktix.github.io/GBEDG/) β Game Boy emulator development guide
- [pastraiser opcode table](https://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html)
- [SameBoy](https://github.com/LIJI32/SameBoy) β cycle-accurate reference emulator (consulted heavily for v2.0 APU + interrupt timing)
- [PyBoy](https://github.com/Baekalfen/PyBoy) β Python reference + huge inspiration; the v2.0 adaptive frame limiter is adapted from PyBoy's
- [Test ROMs Archive](https://gbdev.gg8.se/wiki/articles/Test_ROMs)