Jérôme Mahuet's Blog

A not so classic Chip8 emulator

I always found emulators amazing pieces of software. The first I used was a SNES one, that I use to play some classic games I never was able to get when I was a kid. I thought those beasts were black magic, something I will never be able to grasp.

Motivations

I basically wanted to improve my OpenGL (and C++) skills through a fun project. I always wanted to make an emulator, and the Chip8 is a fairly easy one to start with. So why not making the rendering 3D to be able to move “inside” the game.

Here is a small video of what it looks like: youtube

video

About Chip8

The Chip 8 is actually a virtual machine developed in the 70s by Joseph Weisbecker. So it was never a real console to begin with but we still can emulate it. There have been plenty of games written for it so we can run them later in our emulator.

This is probably the easiest emulator you can start with, since it has a very small number of opcodes and they are pretty simple to implement. You can find easily on the internet the specifications needed to be implemented. For instance on this website.

chip8-2.png

C++ data modeling

1struct Cpu 2{ 3 uint16_t opcode; 4 Memory & memory; 5 std::array<uint8_t, 16> registers; 6 uint16_t index; 7 uint16_t pc; 8 std::array<uint8_t, 64 * 32> pixels; 9 uint8_t timerDelay; 10 uint8_t timerSound; 11 std::array<uint16_t, 16> stack; 12 uint16_t sp; 13} 14 15struct Memory 16{ 17 std::array<uint8_t, 4096> storage; 18}

So as we can see that machine is very simple. We've got 4Kb memory to work with, 16 registers and then some variables where we are storing the current opcode, the current index and the value of the program counter (pc). There is two timers that are clocked at 60Hz. Each one of them should be decremented 60 times per second. If the timerSound is above 0, we should play a sound.

If you are interested in knowing more about it, you should check this guide which explains a lot more things than me.

Rendering

Usually the games are rendered in a 64 * 32 framebuffer. To make mine a bit different from the others I decided to treat each pixels as a cube. Each single pixel is treated as an entity to be drawn, this is definitively not the most efficient way of doing it, but for such a small number of pixels it went well with an average of 200 fps on a machine without a dedicated graphic card.

chip8-1.png

The 3D engine is a simple basic OpenGL renderer with a few shaders for the shadows and lights and a very basic free fly camera. The most important part is to render those array of pixels into cubes and translating it into 3D using matrices.

1auto view = camera.getViewMatrix(); 2auto projection = glm::perspective(camera.getZoom(), 2.0f, 0.1f, 500.0f); 3auto cameraPosition = camera.getPosition(); 4 5int i = 0; 6for (const auto & pixel : pixels) { 7 if (pixel > 0) { 8 auto pos = glm::vec3(i % WIDTH, -i / WIDTH, -40); 9 glm::mat4 model; 10 model = glm::translate(model, pos); 11 draw(model, view, projection, cameraPosition); 12 } 13 i ++; 14}

You can find the source for everything here.
A NES emulator should be a fun next exercice.

Made in France in 2024 with NextJS, Typescript, Strapi, Tailwind etc.