Chip-8 Emulator*
Hello Emulator.
Chip-8 emulators are a hello world to emulator programming.
* All images are from ROMs loaded in the emulator, these ROMs are not created by me.
This emulator support loading and displaying ROMs of different iterations of the Chip-8 language.
- Chip-8 ROMs: The original language for the Cosmac VIP and Telmac 1800, with a screen resolution of 64x32.
- Hires Chip-8 ROMs: An extended version with a screen resolution of 64x64.
- Super Chip ROMs: A descendant of Chip-8, with a screen resolution of 128x64.
Challenges
Creating this emulator involved writing a small OpenGL based engine, with a game loop and texture drawing. Parsing the ROMs required loading it, interpretting each opcode (a 2 byte code which indicates some action to perform) and passing through the output to the renderer.
Game loop
The game loop has an important function within the emulator. It calls checks the input, calls the emulators update function a few times and draws the texture it received from the emulator. The game loop is limited by framerate.
The emulators Update() method will update the timers, process the current opcode and add to the counter when finished. The opcode is split into a command (the first 4 bits) and command info, the last 12 bits. The command is used to call a function from an array of function objects. Within each opcode function the command info is used to process the commands specifics.
/* UPDATE */
#pragma region UPDATE
// Update tick
void Chip8::Update()
{
// Count ticks
++m_TickCount;
// Get Opcode
U16 opcode = (m_Memory[m_RegPC] << 8) | m_Memory[m_RegPC + 1];
// Process opcode
U8 command = opcode >> 12;
U16 commandInfo = opcode & 0x0fff;
// Timers
if (m_DelayTimer > 0)
--m_DelayTimer;
if (m_SoundTimer > 0)
{
--m_SoundTimer;
if (m_SoundTimer == 0)
Beep(400, 80);
}
// Process command
m_OperationsArr[command](commandInfo);
// Check for buffer overflow
if (m_RegI >= MEMORYSIZE)
clog << "I-reg TOO HIGH for opcode: " << command << endl;
// Counter
m_RegPC += 2;
}
#pragma endregion
Texture setup
Due to the fact that the different descendants of the Chip-8 language target hardware with different screen resolutions, the array used for storing the texture has to be quite extensible. The array has to be able to render 64x32, 64x64 and 128x64 resolutions. There are a few options for handling this situation.
- Multiple arrays, one for each resolution.
- One array, only use the part you really need.
- One array, scale up the pixels. (which is perfectly possible since the screen resolutions are all powers of two)
During research about the matter I found one note on a test ROM (Emutest by HAP) which states that, when not in super chip mode, the scroll opcode should still scroll by one super chip pixel. Which happens to be half a chip-8 pixel. So for this emulator I chose to implement the last option. This means that for a chip-8 draw operation, each pixel will be registered as a 2x2 square.
// Calculate drawing bit and array index.
U16 index = r * TEXTUREWIDTH + c;
index *= multiplier;
U16 drawBit = 1 << ((index % (sizeof(char) * 8)));
index /= sizeof(char) * 8;
// Flip bit if necessary
m_Texture[index] ^= drawBit;
if (m_EmulatorMode == EmulatorMode::Chip8)
{
// Flip other 3 bits for 2x2 pixels
m_Texture[index] ^= (drawBit << 1);
index += TEXTUREWIDTH / (sizeof(char) * spriteWidth);
m_Texture[index] ^= drawBit;
m_Texture[index] ^= (drawBit << 1);
}
OpenGL input & window handling
The GLFW library gives access to the window events as well. For key input, I noticed listening to the event was kind of slow, so I opted for using a key check each update from within the game loop. This allows for more responsive input for the emulator.
Some other window events are handled as well. Resizing will correctly resize the quad on which the texture is drawn. Dropping ROMs on the window will try to load them.
Extra info
- Wikipedia article
- http://chip8.com — Website devoted to chip-8. Contains a lot of test, demo and game ROMs.
- http://mattmik.com/ — Some good information on the different opcodes.
- http://devernay.free.fr/ — Some good information on the emulators structure.