Building my first emulator

Screenshot of the Chip8 emulator displaying a white IBM logo on a black screen

Recently I've become interested in embedded devices and developing for them. Initially, that started out as wanting to write a game for the Game Boy Advance, but if I'm being honest my knowledge wasn't up to scratch. So instead, I thought I'd try my hand at developing a simple emulator. After researching for a bit (read: about 5 minutes), I settled on a Chip8 emulator because a few threads said it was a good place to start, and there were a fair few useful resources around developing one. The finished product can be seen on Github.

Starting out

To begin with, I settled on a language to use. It made sense to go low-level, because the end-goal is getting better at coding with strict device constraints. I considered Rust, but decided against it because there was a lot more syntax and new concepts to learn. C fit the bill better: I'd used it before, the syntax is simple, and it's such a ubiquitous language there are tons of resources around - you're rarely treading new ground with a language like C.

I followed this guide from Tobias V. Langhoff to describe the desired functionality, and used Cookerly's Chip8 implementation as a basis for when I got stuck - this was particularly useful when dealing with the SDL2 graphics library.

Building the emulator

Getting started was the hardest part. After defining the basic structure of the emulator - allocating space for 4096 bytes of RAM, loading the font into memory and adding a stack and different registers, I created the SDL2 window and sized it appropriately, and worked on drawing out the display. Initially, I simply treated the display as a 64 x 32px grid and manually drew out each pixel from the display - black if the bit was 0, white if it was 1. This worked fine, but was a bit slow, so I took some cues from Cookerly's code and used a texture instead. This works pretty neatly, and the code is a little bit cleaner.

Once all this was done, it was time to build the execution loop. It looks a bit like this:

  • Fetch, decode, and execute the next instruction
  • Decrement the two timers
  • If updated, re-render the display
  • handle any user input

Handling the instructions was easily the most time-consuming part. There are roughly 33 instructions, which meant a big old switch statement, with some nested switches for good measure. None of the instructions were particularly tough to implement, they're all relatively simple. The rest of the code was fairly simple to do - I already had the display rendering code written, I just had to call the function if the updated flag had been set. SDL2 makes taking user input easy, I just had to map it to the correct hexadecimal character for the appropriate key.

Testing

Along with a few basic ROMs to test that it actually played games, I found a Chip8 emulator test ROM that checked various opcodes and output a pass/fail on the screen. This proved to be a fairly useful sense check (although definitely not a complete test, as I still found some bugs when running other ROMs).

Screenshot of the Chip8 emulator showing the Test Opcode ROM output. It shows a black screen with 6 rows and 3 columns of white text, each with a shorthand version of the opcode and the text "OK". The first entry refers to opcode 0x3X00, so states 3X OK, and so on

Next steps

I'm more-or-less done with this project, so next I'd like to move onto building an emulator for more complicated devices, such as a Game Boy. However, if I do come back to the project I'd like to do more work into improving things like the CPU clock speed - right now it feels a bit too fast on some devices, and too slow on others, so needs some fine-tuning. There are also some ROMs that don't work, which I suspect is due to some differences in implementation across the devices that ran Chip8.