I’ve always been interested in emulation - it seemed incredible that we could bring back past hardware and software. In fact I wanted to take a try at building my own - but before diving into the deep end I wanted to try something simple. CHIP8 was one of the recommended beginner emulator projects so I decided to dive in.
I read this reference and based my implementation off of this. It had everything I needed!
The first thing to figure out was how I was going to structure the code. I had a
System struct that handled almost everything. Looking back, I wish I had separated out the display and input. The struct had the system configuration options, registers, stack, and memory. The registers and memory were simply represented as a byte slice. However, for the stack I chose to write a wrapper around the slice and get rid of the stack pointer. The stack pointer was no longer needed as it became internal to the stack and I could just push and pop without worrying about the index.
It will loop listening for a halt signal on a channel - if it isn’t received, it will read and run the next opcode. The clock speed is variable and can be set up to a maximum of 1000 Hz (1 ms/loop). The actual opcode execution is just a massive switch statement, mapping all the possible opcodes. All the instructions were implemented and have been tested to work (to a degree). The rest of the implementation is pretty boring except the few things I will point out below.
The reference states that CHIP8 has two timers: sound and delay. These times decrease at a steady rate of 60 Hz (with no mention of clock speed). I initially, included it in the clock loop, but because of the variable clock speed this clearly wouldn’t work. I chose to use a separate goroutine running on a ticker from
time and decrementing the timers if necessary. This separated the two ideas and allowed them to coexist.
For displaying, I used termbox. It was very simple to use, especially when compared with ncurses. However, it was a bit minimalistic but for my project it was more than enough. I held a buffer of the display which I would then sync with the actual display after I finished updating it.
I also used termbox for input, as it handles inputs to terminals. I have a separate goroutine running to handle inputs and set state. However, there is one thing that’s a bit of a hack due to running in a terminal. It can’t detect whether a button is being held down so I decided to use variable key timeouts. This can be set using a flag and determines for how long the key should be considered to be pressed down.
Although this was probably very simple compared to actual hardware system, being an interpreter and all, I feel like it was a good start. Hopefully it helps when I’m building my next emulator!