Writing a Game Boy emulator in Go
December 21, 2019
I wrote a Game Boy emulator in Go called Goboy. This was not because the world was desperately crying out for another Game Boy emulator. It was because emulators are a lovely excuse to learn how real machines work without immediately drowning in modern hardware complexity.
The posts below are a build log rather than a pristine tutorial. That means there are wrong turns, mildly embarrassing bugs and a lot of “I thought this would be simple and then it was not simple”. I think that makes them more useful. Most emulator write-ups show the final clean architecture. Real emulator development is more like discovering that one bit in one register was supposed to be read-only and that your entire mental model has been quietly wrong for three days.
The series
- Starting off - setting up the project and beginning the slow march through CPU instructions.
- LOAD complete - implementing load instructions and starting to build a memory model.
- Running ROMs - loading ROMs, debugging opcode decoding and using Blargg’s Game Boy test ROMs.
- Making a readable display - getting the display pipeline to render something recognisable.
- The bugs’ awakening - debugging timer interrupts, memory banking, stack overflows and other tiny disasters.
- Building for WebAssembly - compiling the Go emulator to WebAssembly and running it in the browser.
What the posts cover
The interesting parts are not really the Go syntax. They are the little interfaces between pieces of the emulated machine: the CPU fetching bytes from memory, the decoder turning opcodes into instructions, memory-mapped registers pretending to be ordinary addresses, and the GPU doing its own very particular dance.
If you are writing your own emulator, the main lesson is to invest earlier in tests, debug output and clear boundaries between components. Test ROMs are useful, but they usually tell you that something is wrong rather than where it went wrong. The bugs I remember most clearly were nearly always one-line fixes hiding behind terrible observability.
That is the fun of it, unfortunately.