Potato Chip: A Shitty CHIP-8 emulator

The CHIP-8 was a system used on a few graphing calculators in the 80s. It consists of the CHIP-8 programming language, and a virtual machine that runs its instructions. Since the instruction set is small and simple, and the hardware isn't that complicated, it's a good start for anyone looking to program their first emulator. I programmed an emulator called Potato CHIP using the documentation at Cowgod's CHIP-8 Reference and the tutorial at Multigesture. It was programmed in C, and used SDL2 for input and output.

GRAPHICS SPECS

The CHIP 8 has a 64x32 screen, with 1-bit per pixel. This means a pixel can only be black or white. The screen is updated at 60 frames per second when not waiting for input.

CPU Specs

Setting it up

Since I already had a basic C development environment set up, the first step was to set up SDL2, which changes based on your operating system, SDL version, way you installed it etc. Eventually, #include <SDL2/SDL.h>, along with compilation using gcc -o chip8emu main.c -g -lSDL2 (In that order) worked on my updated Ubuntu 16 installation.

To test that SDL was working, I used a series of tutorials and SDL's official documentation to create an empty window.

Types: I know a char is supposed to be 8 bits, and a short 16, but just to be sure, I made the types of all my important system variables uintx_t, where x is the amount of bits.

I set up the CHIP-8's memory:

static uint8_t mem[4096];

Next, I added the the font sprites to memory starting at address 0. This code is way more compact than it has to be, and I probably should have hardcoded the bytes instead of looping through. But it works and I'm not going to rewrite it.

//Load character sprites in.
int sprites[] = {0xF999F,0x26227,0xF1F8F,0xF1F1F,0x99F11,0xF8F1F,0xF8F9F,0xF1244,\
0xF9F9F,0xF9F1F,0xF9F99,0xE9E9E,0xF888F,0xE999E,0xF8F8F,0xF8F88};
for(i = 0; i < 0x50; i++) {
 //Puts each 4 bytes into a memory slot in the high part of the byte.
 mem[i] = (sprites[i/5] & (0xF << (4-(i%5))*4)) << 4*(i%5) >> 12;
}

🍖The MEAT🍖

After initializing memory, I wanted to set up the main game loop and dig into the meat of the opcodes. I set up a switch statement on the highest 4 bits of the opcode and added cases for each type of opcode.

switch (opcode & 0xF000) {
 //cases
}

The hardest opcode was definitely the spite display opcode: 0xdxyn.

case 0xD000: {  int yLines = 0;
 V[0xF] = 0;
 //i - regI is y
 for(yLines = 0; yLines < (opcode & 0x000F); yLines++) {
  int xLines;
  uint8_t currentByte = mem[yLines + regI];
  for(xLines = 0; xLines < 8; xLines++) {
   uint8_t currentPixel = (currentByte >> (7-xLines)) & 1;
   if(currentPixel) {
    if(screenBuffer[V[x] + xLines + 64*(V[y] + yLines)] == 1) {
     V[0xF] = 1;
    }
    screenBuffer[V[x] + xLines + 64*(V[y] + yLines)] ^= 1;
   }
  }
 }
}
isDrawing = 1;
break;

The stack push and pop opcodes also caused problems for me, however, when I followed the documentation exactly the problems disappeared.

After processing the opcode, I would increment pc by 2 to move on to the next one. This caused problems with jumping. In retrospect, I should have incremented the program counter between reading and processing the opcode, since it is only needed in reading the current opcode.

Problems

One problem I am still having is controlling the timers for graphics and the dt register independently at 60 Hz, which seems to be speeding up the program unpredictably.

As it stands, my emulator is a buggy piece of shit. But it's my buggy piece of shit and I luv it. Check in later for more updates!

Thanks for reading!