Chip8 emulation, 3 take-aways!

Chip 8 Emulator

Nostalgia is big in the gaming scene so it is not a wonder that emulators of all sort would see the light. Gameboy, PS1, Nintendo 64… How did they work exactly was a bit a mystery to me before. When I found this article http://www.multigesture.net/articles/how-to-write-an-emulator-chip-8-interpreter/ , I couldn’t help but coding my own Chip8 emulator.

I won’t describe the code here as the original article does already a great job. However, there are a few points that I learnt and will definitively apply to my next emulator project (Gameboy/NES of course ! I need to play Zelda).

DRY

Emulation involves a lot of repetitive work. Opcodes sometimes are really similar (add constant to register, add two registers together). Also, the program counter needs to be increased most of the time after executing an opcode. All the common operations (fetching information from the opcode, increasing the opcode) should be factored in common functions in order to avoid bugs when writing the same thing a lot of times. I know, this is coding 101 but it does not hurt to repeat especially in that case where we can find a lot of code duplication.

Unit test that thing

Not everything can be testing in emulation. For example, graphics and sound might be tricky. Testing the opcodes individually however is easy and should be done to validate the correct behaviour.

From my emulator:

TEST(opcode, op_2nnn) {
    Chip8FreeAccess chip8;

    // 0x2204 - execute subroutine at index 204.
    std::vector<uint8_t> source {0x22, 0x04, 0xA2, 0xF0, 0xA2, 0xFF};
    chip8.load_from_buffer(source);
    chip8.emulateCycle();

    // stack should be one. pc would be stored as 0x200. Current pc is 0x204.
    ASSERT_EQ(1, chip8.sp());
    ASSERT_EQ(0x204, chip8.pc());
    ASSERT_EQ(0x200, chip8.stacks()[0]);

    // to confirm execute the next cycle
    chip8.emulateCycle();
    auto i = chip8.I();
    ASSERT_EQ(0x2FF, i);
}

This is testing that the subroutine opcode does the correct thing. I have similar tests for most opcodes (and should have for all of them…).

Testing all combination of opcodes is not possible but fortunately the emulation scene provides some ROM which are only used for testing emulators.

Create tools ASAP

Using gdb is great to debug programs but I also created some debugging tools to dump info in the emulator window at the same time as the game is running. If the code is stuck because of a bug, I can see immediately on my screen.

Also, I have some code to only print the opcodes in a binary file, not execute them. That way I can take my time to look at the code without running the game.

Especially when reverse-engineering (see Ethereum articles), taking a good look at the binary format saves a LOT of time so implementing tooling will be something I’ll do again next time.

I put my emulator on Github. It should work with most of the games in the repo although I didn’t test it extensively. My next project is a NES emulator, which is a tad more complication but I cannot wait to start. Cheers!