Writing a Chip 8 Emulator (Part 2)

By Craig Thomas, Thu 17 July 2014, in category Emulation

chip8, emulator, python

In a previous post, I wrote about the basic CPU structure needed to emulate a Chip 8. I also wrote about memory access, and how to define bytes and words. In this post, I will talk about how to interpret Chip 8 instructions, as well as how to set up a main emulator loop.

The Execution Cycle

Every computer executes a simple loop over and over again while it is powered up. This loop consists of the following steps:

During the fetch portion of the loop, the CPU will grab the contents of memory where the Program Counter is currently pointing. This memory content indicates the instruction that the CPU should execute. For example, let’s say that the program counter points at memory location $0200, and that the data $1100 is stored there (technically, $11 is stored in location $0200, and $00 is stored in location $0201). The fetch portion of the cycle will load the CPU with the instruction $1100 (more on what this instruction actually does a little later).

The decode portion of the loop translates the instruction into a form that the CPU understands. Each instruction has a specific number of operands that are associated with it. These are essentially parameters that describe what registers or memory locations the instruction should actually be executed on. In the case of the Chip 8, the operands are all part of the 16-bit word that is read from the Program Counter. For example, with the instruction $1100, the $1 indicates that the instruction is a JUMP instruction, while the 100 is the operand that relates to the address that the CPU needs to jump to.

The execute portion of the loop actually executes the instruction on the operands that were previously decoded. In the case of the decoded JUMP instruction $1100, the CPU will advance the program counter to the memory location $100.

The computer will continue performing this loop over and over again until the power is turned off. Now that we understand what we need to do in the main loop, let’s look at how we decode the different instructions.

Anatomy of a Chip 8 Instruction

Chip 8 instructions are actually really nice to work with. Each instruction that the CPU can execute is exactly two bytes long, making them a 16-bit word in length. All of the data needed to execute the instruction is stored within this 16-bit word. Let’s take a look at a simple instruction and dissect how the operation works.

The Jump Instruction

The Chip 8 JUMP instruction tells the CPU to move the Program Counter to the address specified. The JUMP instruction is encoded as follows:

1nnn

The n characters are used to denote 4-bit numbers. When combined, the numbers form a 12-bit address that the CPU should jump to. For example, if the CPU encountered the instruction $1234 then the CPU would set the program counter to $234. During the next loop of the CPU, the next instruction would be fetched from location $234.

The Load Operation

Let’s look at another example. The LOAD instruction tells the CPU to load a specific number into a register. The LOAD instruction is encoded as follows:

6snn

The s character is used to denote a 4-bit value that specifies a register to store the number in. The n characters denote 4-bit numbers that, when combined, indicate the 8-bit value that should be loaded into that register. For example, if the CPU encountered the instruction $6248 the CPU would load the value $48 into register 2.

Chip 8 Operands

We can start to see patterns in the way a Chip 8 instruction is encoded. The first hex digit of the instruction usually provides a hint at what major operation is about to occur. The next three hex digits encode numeric information, or the registers that the operations work on. Here is a mostly complete set of Chip 8 instructions:

Bit Shifting and Bit Masks

So, when writing an emulator, how do you separate out the various operands? The simple way is through bit masks and bit shifting. A bit mask is a binary pattern, usually used to turn certain bits on or off, or used to determine if a particular bit pattern is activated. Bit shifting is where the bits in a pattern are moved left or right by a certain number of spaces. Let's work through a simple mask and shift example.

Let's say that we had the hex number $23. In binary, this would be 0010 0011. The 0010 corresponds to the 2, while the 0011 corresponds to the 3. Now let's say you want to get just the hex value of the first digit. What we do is 1) apply a bit mask with a logical AND operation, and 2) shift the bits.

For the first step, what we want to do is isolate the high 4-bits in the pattern, and blank everything else out. We do that by applying a bit mask that has all ones in the high 4-bits. In binary, this would be 1111 0000 - hex it is $F0. Now that we know the mask value we need to apply, the next thing we do is apply a logical AND operation to $23 and $F0. The way an AND works is if both digits are a 1, then a 1 is the result, otherwise, the value is a 0. When we apply the AND:

0010 0011
1111 0000
--------- &
0010 0000

The result we are left with is 0010 0000, which is the hex value of $20. Now that we have blanked everything else out, we perform the second step, which is the logical shift. We want to move all of the bits of the result 4 places to the left. In a language like C, this is done with the >> operator.

0010 0000
--------- >> 4
0000 0010

This leaves us with 0000 0010 which is the hex value of $02, which is exactly what we wanted!

Shifting and Masking Chip 8 Instructions

Let's take a look at the Chip 8 instruction to move a value of one register into another. This corresponds to the instruction 8st0. This says to move the value in register s into the register t. An example of this would be $8620, which would make the Chip 8 CPU move the value in register 6 into register 2.

The first thing we need is the value for s. Using a bit mask, we want the following:

1000 0110 0010 0000
0000 1111 0000 0000
------------------- &
0000 0110 0000 0000

Once again, the magic hex value is $F for the 4-bit pattern 1111. The same operation in hex gives us:

$ 8 6 2 0
$ 0 F 0 0
  ------- &
$ 0 6 0 0

This gives us the result $0600. Now we need to shift the bits 8 places to the left this time:

0000 0110 0000 0000
------------------- >> 8
0000 0000 0000 0110

The same operation in hex gives us:

$ 0 6 0 0
  ------- >> 8
$ 0 0 0 6

So now we know that the s value should be $6! We do the same thing for the t register, except this time we only need to perform a shift of 4 places to the left. The hex operations are as follows:

$ 8 6 2 0
$ 0 0 F 0 
  ------- &
$ 0 0 2 0
  ------- >> 4
$ 0 0 0 2

This tells us that the t value should be $2!

The key points to take away here are:

Conclusion

In this post, we looked at the execution loop, and examined how Chip 8 instructions are encoded. We also looked at bit shifting and bit masks, and how those two simple tools can be used during the decoding phase. In a future post, I’ll look at some of the Chip 8 instructions in more detail, as well as how to code the execution loop in a language like C.