# A not so classic Chip8 emulator
**2017** · C++ · OpenGL · GLM · [Source](https://github.com/Rydgel/chip8) · [Demo](https://www.youtube.com/watch?v=h3pDNioWFzI)
![[chip8.webp|A Chip8 display transformed into a field of 3D cubes]]
I always found emulators to be amazing pieces of software. The first one I used was a SNES emulator, which I used to play classic games I could never get when I was a kid. I thought those _beasts_ were _black magic_, something I would 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 make the rendering 3D so I could move “inside” the game?
Here is a small video of what it looks like: [watch the demo on YouTube](https://www.youtube.com/watch?v=h3pDNioWFzI).
## About Chip8
Chip8 is actually a virtual machine developed in the 70s by [Joseph Weisbecker](https://en.wikipedia.org/wiki/Joseph_Weisbecker). So it was never a real console to begin with, but we can still 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. Tobias V. Langhoff's [CHIP-8 emulator guide](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/) is a useful modern reference for the instruction set and its compatibility quirks.
You can also see the emulator in action in [another YouTube video](https://www.youtube.com/watch?v=R1K5NIyvHFk).
## C++ data modeling
```cpp
struct Memory
{
std::array<uint8_t, 4096> storage;
};
struct Cpu
{
uint16_t opcode;
Memory & memory;
std::array<uint8_t, 16> registers;
uint16_t index;
uint16_t pc;
std::array<uint8_t, 64 * 32> pixels;
uint8_t timerDelay;
uint8_t timerSound;
std::array<uint16_t, 16> stack;
uint16_t sp;
};
```
As we can see, that machine is very simple. We've got 4 KB of memory to work with, 16 registers, and then some variables where we store the current opcode, the current index, and the value of the program counter (pc). There are two timers that are clocked at 60 Hz. 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](https://tobiasvl.github.io/blog/write-a-chip-8-emulator/), which explains a lot more than I do.
## Rendering
Usually, games are rendered in a `64 * 32` framebuffer. To make mine a bit different from the others, I decided to treat each pixel as a _cube_. Each single pixel is treated as an entity to be drawn. This is definitely not the most efficient way of doing it, but for such a small number of pixels, it worked well, with an average of 200 fps on a machine without a dedicated graphics card.
![[chip8_1_a9afb7f689.png|The Chip8 framebuffer rendered as illuminated 3D cubes]]
The 3D engine is a simple **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 that array of pixels as cubes and translate them into 3D using matrices.
```cpp
auto view = camera.getViewMatrix();
auto projection = glm::perspective(camera.getZoom(), 2.0f, 0.1f, 500.0f);
auto cameraPosition = camera.getPosition();
int i = 0;
for (const auto & pixel : pixels) {
if (pixel > 0) {
auto pos = glm::vec3(i % WIDTH, -i / WIDTH, -40);
glm::mat4 model(1.0f);
model = glm::translate(model, pos);
draw(model, view, projection, cameraPosition);
}
i ++;
}
```
You can find the source for everything [here](https://github.com/Rydgel/chip8).
A NES emulator should be a fun next exercise.
---
Back to [[Experiments/Index|Experiments]] · see also [[Monkey language interpreter made in Rust]] · [[Home]].