December Adventure 2024

Home · Blog

30 November 2024

I’m doing a December Adventure this year for the first time. The idea is to pick a topic or project, work on it a little each day during December, and log your progress.

My plan is to start working on a CHIP-8 emulator / interpreter. I’ve been interested in emulators and virtual machines for a while, and CHIP-8 seems like a great place to start because it’s pretty simple and logical. My goal isn’t necessarily to have a working emulator at the end of December -- the idea is to spend a little time on it every day, learn something, and share it with others.

Below are my daily updates in reverse-chronological order.

Day 31: Wrapping Up

Looking back, I’m happy I joined the December Adventure this year. The idea of writing a CHIP-8 emulator has been in the back of my mind for a while and this gave me the occasion to finally do it. Working a little on it each day was fun and I got lucky that the timing work out exactly right, getting to a working emulator on December 29th.

If I join again next year, I’ll look for a more open-ended topic. The CHIP-8 interpreter was a very clearly defined project. That was good because it meant I could focus on the programming, not on figuring out what I wanted to do, and there was no danger of getting stuck in analysis paralysis. But perhaps the real spirit of adventure would be starting out with a vague idea, writing some code every day, and then just seeing where that leads you…

Day 30

Cleaned up the code a little, added some documentation, and uploaded everything to github.com/lfritz/chip8.

Day 29

The emulator works! Here’s me playing the BRIX game:

All that’s left now is to clean up the code a little and add some documentation.

Day 28

Took a couple of days off for Christmas and family time. Now I’m back debugging why the BRIX ROM isn’t working.

I found one issue today: the BRIX code is drawing a sprite partially off-screen. My emulator doesn’t allow that, but I suppose it should be allowed. Also the error handling in that case was pretty bad.

For today I’ve improved the error handling and updated my unit tests. Implementing the improved drawing code is postponed to tomorrow morning -- that code involves some tricky bit-shift operations and I need a rested mind to deal with those.

Day 25

Changed the emulator to load CHIP-8 programs from binary files (people call these “ROMs”) and added two new programs that convert between hexadecimal and binary CHIP-8 files.

I also tried some CHIP-8 ROMs I found online. The chip8-test-rom worked and showed OK for all tests. I also tried the BRIX ROM (found here: github.com/JamesGriffin/CHIP-8-Emulator/tree/master/roms) but that one doesn’t work right. It shows part of the user interface and then just stops.

Next step will be to try to debug that. Still six days left in December… let’s see if I can make it work in that time and actually play the game on my emulator.

Day 24

Took a break from the CHIP-8 emulator today and instead wrote up instructions on Using Raylib with Zig. I’m very happy with my decision to use Raylib; it was easy to get started with and works well without any Zig-specific bindings.

Day 23

Created a build.zig file so I can use Zig’s build system to compile the code.

Day 22

Implemented instructions for timers and sound. CHIP-8 defines a very simple sound device that’s either on or off, basically a buzzer. To keep things simple, I emulated the buzzer by just showing a bell emoji in the title bar when the buzzer is on.

Day 21

Created a unit test for random number generation (from Day 14) following a suggestion by Toby Jaffey on Mastodon. The idea was to use a real random number generator but with a fixed seed to make the numbers predictable. This works quite well :)

Day 20

Got my first CHIP-8 program running today! Here’s the output:

CHIP-8 has characters for the hex digits (0-9 and A-F) pre-defined, so this seemed a good choice for a Hello, World! type program. By coincidence, I was actually sitting in a café when I got it to work and saw the letters flash up on the emulated screen, which I found rather pleasing. (Gotta enjoy those little things these days, when some bigger world events are, uhm, not sparking joy.)

Day 19

Work and life have been busy and I didn’t manage to do any adventuring yesterday. Today I at least managed to add the error checking for the screen.

Day 17

Working on connecting the screen, the emulated CPU, and the main program. I ran into some errors with out-of-bounds memory access, so before I continue I’ll add some checks for things like trying to render a sprite with invalid screen coordinates.

Day 16

Didn’t have a lot of time today, but I started implementing the type for the screen.

Day 15

Experimented a bit to figure out how to represent the CHIP-8 screen. This morning I started implementing a data structure based on an array of 256 bytes (32x64 monochrome pixels ~ 2048 bit ~ 256 bytes), but when I looked back at it tonight I realized it would be much easier to represent each row of pixels as one number so the whole screen is an array of 32 64-bit unsigned integers.

I do that sometimes: start an akward/overcomplicated implementation; step away from the code for a bit; realize there’s a better way and start over. This seems to work well enough as a software design methodology 🤷

Day 14

Implemented the instruction for random numbers.

Generating random numbers with Zig is pretty simple; the standard library implements several different random number generators. Where I ran into trouble was trying to write a unit test. So far I’ve written a unit test for each CHIP-8 instruction that I’ve implemented, so I was going to do the same thing for the random number one. My idea was to just use a “fake” random number generator that returns some fixed number. In Go that’s a common pattern, but it usually requires using an interface so you can write a fake implementation of that interface. Well, I realized today that Zig doesn’t have interfaces in the language so that pattern doesn’t work so well.

For the random number generation there is actually a simple solution: use a function pointer instead of an interface. I might use that later, but for now I just went with a more limited test that doesn’t actually check the generated value.

Day 13

Implemented the “computed jump” instruction.

Day 12

I’ve implemented all the instructions that don’t have to do with I/O, so next I have to figure out how I’ll emulate screen and keyboard. My first idea was to just use the terminal -- the CHIP-8 screen is 64x32 monochrome pixels, so I could just use characters for pixels. But a real graphics library will be more flexible and I also don’t want to have to deal with terminal control sequences.

So today I started to experiment with using raylib from Zig. Here’s what I got so far:

It’s the CHIP-8 display with only the four corner pixels set.

Day 11

A small change today: incrementing the program counter correctly.

Day 10

Added a stack and implemented subroutine calls.

Day 9

Didn’t have a lot of time today so I only implemented one instruction. It’s possibly the weirdest one, though: binary-coded decimal (BCD).

Day 8

Added memory and the load and store instructions.

Day 7

I remembered there was one more thing I wanted to do before emulating memory, which is adding a program counter and implementing the jump and branch instructions that depend on it. Did that today.

Day 6

I’ve implemented all the simple instructions: the ones that only involve the CPU, not memory or I/O. The next step will probably be to start emulating memory.

Day 5

Finished the instruction decoding function and started using it for the CPU emulator.

Also, now that I’m looking at the instructions more closely I noticed a couple of mistakes I made in the diagram from day 1. Oops! I should create an updated version at some point.

Day 4

Defined a type for instruction using Zig's tagged unions. Started working on a function for “instruction decoding”, which takes an instruction as a 16-bit number and returns the new type.

Day 3

Started working on the emulator today: set up a Git repo and wrote just enough code that I can say I’ve implemented one CPU instruction, with unit tests :)

Day 2

My task for today was just to write up a better description of the diagram from yesterday. To recap, it’s an image where every possible CHIP-8 instruction is represented by one pixel. Here it is again:

It’s kinda neat, right? Here’s the key to the colors:
███ call machine language subroutine
███ jump to address
███ conditional skip instruction
███ arithmetic-logical operations
███ draw sprite
███ delay timer
███ call subroutine
███ load immediate
███ set address register
███ generate random number
███ use keyboard
███ binary-coded decimal
███ clear screen
███ return from subroutine
███ sound timer
███ select sprite
███ store registers in memory
███ load registers from memory
███ invalid instruction

Some observations:

On a more general note, I like how the visualization makes some of regularities of the instruction set jump out at you.

Day 1

Before I start on the implementation I wanted to get better feeling for the CHIP-8 instructions. I’ve been using this page: CHIP-8 Instruction Set, which has a nice table with 35 instructions, but… that table is just a bit dry and I don’t feel like I have an intuitive understanding of the instruction set. It would be nice to have a visual representation.

Here’s my idea: each instruction is 16 bit long, so there’s 216 possible instructions. 216 is 256 * 256, so I can graph the instructions on a 256x256 pixel image. If I split the instructions into categories, I can use a different color for each category, plus gray for invalid instructions, to get the color for each pixel.

My project for today was to write a Python script that creates this image. I’m going to write it up properly tomorrow, but for now I’ll just put the image here: