About The Project#
please leave a star if you can afford it 💜
ppGB (plus plus GameBoy emulator) is a GameBoy Emulator written in C++.
This project aims to implement a basic featured Gameboy Emulator that will
- allow the user to play simple games like Tetris (or at least that’s what it started as),
- while exhibiting good software engineering practices like version control, build systems, documentation, etc (this was important for the course I started this for, idc about that as much anymore)
Showcase#
![]() | ![]() |
my screen recorder broke, i’ll add gifs once i fix it (along with my PPU)
How It Started#
In our 4th semester my college has a software engineering course which has a lab component that involves creating a full software project of our choice. I ended up choosing the gameboy emulator because it seemed fairly common on build-your-own-x, and I figured I already had some prior experience with emulators since I had made a CHIP-8 emulator too. This was a mistake.
The levels of complexity are very, very different, and I severely underestimated the work required in the two-month timeframe I had for that course. On top of that I wanted to do it in C++, a language I was still in the process of learning and not familiar with at all. I am documenting this so that I and others learn from these faulty lines of thought.
I managed to show something for the course, however I wasn’t happy with any of the code I had written and wanted a do-over. (You can look at the trash I wrote here, feel free to complain about it in the replies)
I did have a fun time setting up things like a build system, CI/CD, unit tests, etc. I’ll discuss those some other time
How It’s Going#
In the summer following that semester I had nothing to do so I decided to restart from scratch. I had a better, more modular structure this time, and had less of a skill issue with vim macros for the tedious repititive tasks. I was pretty happy with myself for a while. I was working smoothly on my CPU (almost 500 opcodes btw), I was adding non-trivial unit tests, and things were going well.
ℹ️ warning
My CPU however, was NOT perfect. I came up with all the testcases I added, so they were setup according to my own understanding of each instruction, flawed as it may be. They were not very extensive either as I got lazy at times.
This is NOT good, as I learnt (and have been facing the consequences for since then) a perfect CPU is a NECESSITY!!! I’ll discuss this more in the tests section
The CPU does not take too long to implement, and I think I spent around two weeks on a minimum “functioning” CPU. After that I moved on to the PPU which is the Pixel Processing Unit. And holy shit 😭 the ppu gave me trauma man.
I’m going to try and briefly explain how it works. The PPU renders line-by-line, pixel-by-pixel. It has a separate pixel-fetcher FIFO which is a MASSIVE pita. The PPU is a 4 state Finite State Machine:
- Mode 0 HBLANK: after a line of 160 pixels has been rendered, before it moves on to the next line we enter the HBLANK state in case the ROM wants to make any changes before the next line starts.
- Mode 1 VBLANK: similar thing, but after an entire frame of 144 scanlines has been rendered
- Mode 2 OAM SCAN: this is the period before a line is rendered when the PPU identifies the sprites to be rendered on this line (i still haven’t implemented this btw 😭)
- Mode 3 PIXEL TRANSFER: this is where pixels are fetched from the FIFO and actually rendered to the screen.
The FIFO is also a 4-state FSM and another beast altogether. I have lost so many hours of sleep to that thing. This complexity gives game developers a lot of flexibility to play with the graphics and is the reason behind the many iconic effects of the time.
Anyway with my half-baked understanding of how the PPU works, I managed to implemented a very barebones PPU that could just barely manage to render the background layer. (there’s also a window layer and the sprites mentioned earlier) by the end of the summer.
I could just about get the Hello World ROM shown in the showcase above to render at this point. I cleaned up the code a little built after this and took a lovely hiatus once I got back to college.
CPU Tests#
to get an idea of the scale of the cpu please look at this opcode table
This last week I had the sudden urge to try and fix stuff up. Firstly I traced the source a lot of the source of my confusion to my convoluted FIFO that I did not understand at all, so I cleaned that up as best as I could. I also decided to join the EmuDev Discord which was a fantastic decision. I love the people there so much, thank you everyone!!!
All of them really strongly advocated testing with the Single Step Tests for the sm83 and I was noticing some discrepancies with what my CPU was supposed to be doing, so I decided to go for it. The tests are designed in such a way that
- You load an initial state
- Step your CPU (run the opcode once basically)
- compare with a final state
To integrate with these I was already using Catch2 v3, and just added nlohmann/json. It did not end up taking long at all and helped me identify important issues with my CPU. I also played with gameboy-doctor which helped a bit too.
There were a bunch of small bugs, like missing pc
increments and accidentally reading bytes
instead of words
, but the major highlight was the 0xf8
instruction
0xF8
: LD HL, SP+e8#
From the docs:
Add the signed value e8 to SP and copy the result in HL.
The entire time I believed that this meant that
sp += e8
hl = sp
when apparently its just
hl = sp+e8
wtf. Please tell me it’s worded weird and that I’m not just stupid 😭. Always test your code people. Anyway, passing almost 3 million assertions feels quite nice actually
❯ ./build.sh -t
[+] ROM successfully loaded
Randomness seeded to: 3822501476
===============================================================================
All tests passed (2830171 assertions in 30 test cases)
i still haven’t implemented all the
0xcb
opcodes, i’ll probably get on that the next free weekend I get
[WIP] PPU Tests and The STAT Interrupt#
[WIP] Making it Faster#
Looking Forward#
- got to finish the cb opcodes
- OAM Scan and sprite fifo left yay
- joypad interrupts and then hopefully I’ll be able to play tetris
- More profiling and optimisation because I’m still not at 60fps
- Disassembler. having to look up opcodes in the table gets annoying v fast
- Audio someday?
Acknowledgements#
- Imran Nazar: GameBoy Emulation in Javascript
- Cinoop: stole CPU timings and eventually will make disassembler
- MagenBoy: Helped me with the FIFO a little
- Lazy Stripes: Palette Stuff. Very detailed in general