Alexander Dickson

JavaScript Chip-8 Emulator

Writing an emulator is something I’ve always wanted to do, and I decided to finally give it a crack by writing a Chip-8 interpreter in JavaScript. This is a high level overview to help others with doing the same.

What exactly is Chip-8?

Chip-8 Emulator in JavaScript

CHIP-8 is an interpreted programming language, developed by Joseph Weisbecker. It was initially used on the COSMAC VIP and Telmac 1800 8-bit microcomputers in the mid-1970s. CHIP-8 programs are run on a CHIP-8 virtual machine. It was made to allow video games to be more easily programmed for said computers.

Source.

I chose Chip-8 because it is relatively simple and a good first emulation project to cut my teeth on. It should be noted that is more of an interpreter than an emulator, as we’re essentially creating the Chip-8 VM in JavaScript.

What is an emulator?

Good question, thanks for asking! I could delegate to Wikipedia again here, but I thought I’d explain it myself. An emulator (with reference to how we’re implementing this one) is a program that emulates another, be it in software or hardware.

It does this by emulating all the parts of the target system, such as memory, processor, input devices, graphical displays, etc using the host system’s software and attached peripherals.

First steps

Firstly, you need some good reference material to get familiar with how Chip-8 works, and to refer to later when implementing opcodes, etc.

To start with your learning exercise, read over the Wikipedia article to get yourself familiar with the system.

Secondly, if you’re not familiar with assembly, it may be worthwhile to read a few articles, as you will be dealing with registers, the stack, the stack pointer, program counter, memory, etc. This will be especially useful if you decide to write your own Chip-8 programs.

After you’re familiar with the system, you’ll need to get yourself a good reference to how Chip-8 works. The best reference I could find is Cowgod’s Chip-8 Technical Reference. Keep this one open, as you’ll constantly refer to it as you build your system.

High level overview of how it works

Basically, your emulator is going to revolve around reading opcodes from your program’s memory and acting accordingly, updating the system’s state. An opcode is short for operation code, which is a number that corresponds to a certain instruction, optionally with further data encoded in it. For example, an opcode of 0x00E0 will clear the screen, while the opcode 0x1EF1 will jump to location in memory 0xEF1 (the lower three nibbles of the opcode).

Start ‘er up

Hopefully you’re now quite familiar with Chip-8 and are ready to put code to file. Let’s start by setting up the variables to hold the state of the interpreter. Go ahead and implement the memory, registers, stack pointer, etc. I initialised mine to a fixed size via the Array constructor, which gave me the easy ability to reset them via their length property, allowing me to not have to bother carrying around the lengths separately.

If the following terms are not familiar yet, re-read the linked documentation above.

var chip8 = function() {
	this.reset();
};

chip8.prototype.reset = function() {
	// Program counter 
	this.pc = 0;

 	// Memory
	this.memory = new Array(4096);
	
	// Stack
	this.stack = new Array(16);
	
	// Stack pointer
	this.sp = 0;
	
	// "V" registers
	this.v = new Array(16);
	
	// "I" register
	this.i = 0;
	
	// Delay timer
	this.delayTimer = 0;
	
	// Sound timer
	this.soundTimer = 0;
};

This sort of initialisation is good to place inside a reset() function of your emulator, so at any time, you can reset your Chip-8’s state, and perhaps load a new program. If you experience performance issues with your emulator, you could always use typed arrays to hold Chip-8’s state.

The loop

The bulk of your emulator’s code is to emulate the target device’s cycles, so you’ll need to fetch opcodes and update its internal state, dependent on the register. To emulate cycles, you would typically use a loop. Because a loop in JavaScript locks the browser and JavaScript doesn’t have a sleep() equivalent (please don’t mention while() and Date), we will loop for a set number of cycles and then return some spare for the browser.

I settled on running 10 cycles in a loop inside of a recursive use of requestAnimationFrame(). Don’t forget to use Paul Irish’s shim (or similar) as browser support is still flakey.

Read opcodes

Once it’s all set up, it’s time to start reading opcodes in your loop. An opcode in Chip-8 is two bytes long, and you can read it as two bytes or join it together and treat it as one number.

var opcode = memory[pc] << 8 | memory[pc + 1];

In the above example, I’ve joined two bytes so I can read each opcode as one number. When you get the opcode, you can use the bitwise AND (&) to read the first nibble and then classify that opcode.

switch (opcode & 0xF000) { }

You can now read the high order nibble and determine if you need to further classify the opcode (with nested switch statement, for example) or if you can directly pull out the encoded numbers.

Refer to the opcode reference when implementing the opcodes. For example, the 0x00EE opcode is RET which returns from the current subroutine and can be implemented as…

pc = stack[--sp];

After you’ve read an opcode, you need to increment the program counter by 2 (as each opcode is 2 bytes) and you need to decrement the sound and delay timer by 1.

Getting the program’s memory into your memory

JavaScript has typically had a hard time reading raw bytes from files via XHR. Thankfully, modern browsers are up to the task. All you need to do is set the XHR’s responseType to "arraybuffer" and read the response into a typed array (namely a Uint8Array).

// Load program.
var xhr = new XMLHttpRequest;
xhr.open("GET", "programs/invaders", true);
xhr.responseType = "arraybuffer";

xhr.onload = function () {
   var program = new Uint8Array(xhr.response);
};

xhr.send();

Reading input

The Chip-8 system uses a hexadecimal pad input, which has to be one of the weirdest inputs I’ve heard about, but I guess it makes sense for older machines. I ended up choosing a bunch of keys to use for input. I originally used the numpad, but laptop users couldn’t interact with it. However, keep in mind users with touch screens won’t be able to interact with your emulator.

I also included GamePad support, which is a lot of fun. Except for mapping 16 buttons to a controller, and trying to get it to work well in a cross program way.

There is one opcode that I found slightly tricker to implement, and that is 0xFx0A, which says to halt the emulator until a key is pressed. I decided to re-assign my input handler function to handle this opcode quickly and easily, without having to pass around the value of x.

Implementing graphics & sound

I used the canvas element to produce my graphics (with a scaling factor of 16), but you could easily use absolutely positioned div elements, table cells or even just ASCII characters.

For the one sound effect in the game, the beep (produced when the soundTimer hits 0) I chose to play a sound with the Web Audio API, or just shake the screen if it wasn’t supported.

I also implemented full screen which upsizes the display, and a colour chooser for the game display. Just ‘cos I can.

Programs

I found a bunch of Chip-8 programs online by simply Googling. I had read that the ones I found were in the public domain, so I distributed them with my emulator.

What’s next?

I have decided to have a go at emulating the Nintendo Entertainment System.


Want to discuss this post? Just mention me @alexdickson.