A GameBoy emulator written in C++, using the SDL2 library for graphics.
Screenshots
![]() |
![]() |
![]() |
|---|---|---|
![]() |
![]() |
![]() |
Usage
Requirements
- C++ Compiler: Support for C++23 or later.
- SDL2 Library: For graphics rendering.
- CMake and Make: For building the emulator.
Building the Emulator
Make sure the SDL2 package is available somewhere on your system and run:
mkdir build
cd build
cmake .. -DCMAKE_PREFIX_PATH=<path_to_sdl2_package>
make
Place SDL2.dll from the package in the build folder before running.
Running the Emulator
./gbemu <ROM file>
Controller Mapping
| Gameboy | Key |
|---|---|
| Start | Enter |
| Select | Backspace |
| A | X |
| B | Z |
| Up | ↑ |
| Down | ↓ |
| Left | ← |
| Right | → |
| Speed-up | Space (hold) |
| Pause/Resume | P |
Implementation Details
Opcodes
There are just over 500 opcodes, and typing them all out is not only boring and error-prone, but it is also highly inefficient as many of them can be grouped together. I have included my Python scripts that I used to automatically generate the C++ code for all of them here.
Timing
All of the components in the GameBoy need to work in synchronization - as the internal timer ticks, each component needs to carry out the correct amount of work. There are 2 main ways to emulate this:
- CPU driven: The CPU fetches an opcode, carries out the relevant operations and then notifies all other components how many cycles it took so that they can catch up.
- Timer driven: Each timer tick, every component advances one cycle.
The first method is far easier to implement, and would also result in a faster emulator. However it is not very accurate, and passing many of the tests mentioned below would be impossible. I have therefore chosen to take the timer driven approach, and this is why all the opcodes are implemented in steps.
Absent Features
- The APU does not function other than to hold the relevant registers. This does not impact any other functionality.
- Some bugs I have chosen not to implement, like the OAM corruption bug and STAT blocking.
- OAM and VRAM have inaccessibility windows, but the CPU is currently free to access them at all times.
- Cartridge types beyond MBC3.
- Save functionality.
Tests
- Blargg - Passes 19/27 - all except the OAM bug tests (as not implemented).
- Mooneye - Passes 78/86 of the tests that DMG-ABC and MBCs 1-3 should pass - all except for:
acceptance/interrupts/ie_pushacceptance/ppu/hblank_ly_scx_timing-GSacceptance/ppu/intr_2_mode0_timing_spritesacceptance/ppu/intr_2_oam_ok_timingacceptance/ppu/lcdon_timing-GSacceptance/ppu/lcdon_write_timing-GSacceptance/ppu/stat_irq_blockingacceptance/ppu/stat_lyc_onoff
- rtc3test - Used to get the Real Time Clock working for MBC3.
References
- PanDocs - Implementations for everything except the CPU mostly utilised the information found here. Note that for the PPU, lots of specific details are missing or glossed over.
- CPU opcodes - A useful overview of all the CPU opcodes.
- CPU instruction timings - Information on the general grouping of the CPU opcodes and importantly their timings.





