Emu Boy is a Nintendo Game Boy emulator I wrote from scratch in C++. I started it at the beginning of 2017 to get back to lower-level development, which I'd drifted away from as my day job at the time rarely called for it. I enjoy working at this level sometimes - it’s completely deterministic, there's little 'magic' hidden underneath to occasionally leak through and bite you unexpectedly, and as a result, it has a certain simplicity to it.
Stack: C++, SFML, Google Test · Source: github.com/tekr/EmuBoy
Why the Game Boy
I wanted a project small enough to finish in spare time but challenging and deep enough to involve real low-level work. The Game Boy fits: an 8-bit, Z80-derived CPU, a four-shade display and sprite-based graphics are manageable in scope, but emulating them correctly (which is mandatory to get many games even half working) takes careful attention to timing, hardware behaviour, and performance trade-offs.
Quality, mature emulators such as VBA and BGB already exist. I didn't study them as it would have killed all the fun (and the purpose, really) of figuring it out myself. Instead I worked from the hardware specifications, which are very well documented these days - including a significant amount of unofficial behaviour - thanks to the reverse-engineering efforts of many people over the years. My goal was to turn that spec into a faithful, reasonably performing software recreation.
Development
The CPU, background graphics, memory map and CPU unit tests were written first, over a couple of weeks in January 2017. Sprites, DMA, the joypad, the timer and CPU conformance took another few weeks, then a few iterative refinement/accuracy rounds, driven by testing with specific games and resolving some lingering failing Blargg tests.
Status
As of now (February 2017), Emu Boy supports:
- Near cycle-accurate emulation of the Sharp LR35902 CPU, covering all documented instructions and interrupts
- Graphics: background, window and sprite layers, accurately timed scanline and H-Blank interrupts
- Joypad input and DMA transfers
- The bundled GBSnake ROM and many commercial games fully or largely playable
- Emulation of the original LCD’s slow pixel response with upscaling and smoothing
- Unit tests covering most CPU instructions
- Windows (Visual Studio 2015 solution)
- Audio not yet implemented

The hard parts
Much of the work came after the core was up and running, particularly when testing with real games began exercising behaviour that the documentation isn't clear on, or covers only briefly.
Instruction decoding - The most straightforward/naive approach would have been one large switch statement with a case for every opcode, but I assumed the compiler would turn that into little better than a linear search. Instead I grouped related opcodes together, with each group handled by one function that does any further decoding it needs, and then defined a 256-entry (one per opcode) jump table manually. That approach took more time and thought, and I'd argue the result is better structured and easier to follow. I later found that at least one popular emulator uses the single large switch, and that a good optimising compiler will turn it into a jump table. Oh well.
CPU conformance - I tried to write unit tests to cover each instruction against the documented behaviour, and then code the implementation to pass all tests. Most of Blargg's cpu_instrs tests (which I discovered along the way) succeeded with the initial implementation, but a few caught failures the unit tests hadn't. That included an incorrect DAA (the decimal adjust after BCD arithmetic) implementation, the 16-bit ADD HL instruction testing the wrong carry bit, and ADD SP,e and LD HL,SP+e setting flags incorrectly. One bug in particular was rather insidious - the BIT instruction was inadvertently writing its result back to the register it tested. In Super Mario Land it manifested as the game selecting a ROM bank that didn't exist, which at first looked like a bank switching issue, rather than a CPU bug. That one took me a while to find.
HALT and interrupt timing - HALT took four versions to get right. The CPU has a hardware bug where HALT, with interrupts disabled and one pending, fails to advance the program counter, so the next byte is executed twice. Waking from HALT takes an extra cycle, and dispatching an interrupt takes five machine cycles rather than the four I'd assumed at first.
The window layer - Often used for status bars and menus, the window layer can be positioned anywhere (including off-screen to the right or below), and its position can be changed on a per-scanline basis. My first attempt didn't handle this properly, and as a result the paused-mode text in Super Mario Land was garbled. It took a couple of iterations to get things just right.
Sprites - Sprites took the longest of any feature to get right (and it probably still isn't 100% there). Each line has to find the sprites that overlap it, draw them in priority order (by X position, then by sprite memory order), and handle 8x16 sprites, sprites drawn behind the background, and overlapping sprites with transparent pixels, etc. I used a Boost multi-index container to keep sprites ordered for lookup, and restructured it three times before settling on a working approach.
Game-specific behaviour - Some games depend critically on hardware side effects. For example, Road Rash and The Legend of Zerd rely on a spurious LCD status interrupt that the original hardware raises whenever the STAT register is written, in order to run properly.
How it works
Timing - The emulated CPU executes one instruction at a time, and reports how many clock cycles it ran for. The main loop runs the CPU for the length of each LCD mode (80 cycles searching sprite memory, 172 transferring pixels and 204 in H-Blank, totalling 456 per line), so the graphics, timer and interrupts stay in step with the CPU. Each frame works out to exactly 70,224 clock cycles.
Rendering - Each line is drawn in one go at the end of the line rather than pixel by pixel. This is simpler, at the cost of effects that change graphics registers part-way through a line, which I haven't come across yet in the games I've tried (but there are probably some out there).
LCD slow-response emulation - The original Game Boy screen has a yellow-green tint and a very slow response time, so moving objects leave trails and flickering sprites look semi-transparent. Emu Boy applies a similar display tint and draws each new frame at 75% opacity over the previous one rather than clearing the screen between frames to approximate the original. It took a few rounds of trial and error, tweaking opacity and tint, in order to get things looking about right.
Next steps
- Sound
- Linux and macOS support
- Performance optimisation
- Stretch goal: implement in VHDL on an FPGA
Technology
C++ (Visual Studio 2015 solution), SFML for video output and keyboard input, and Google Test for unit tests.
References
Built from publicly available hardware documentation, mainly:
The repo includes a build of the open-source GBSnake game for testing.

