3.9 KiB
title | date | tags | ||
---|---|---|---|---|
Learning Emulation, Part 2 | 2016-11-23 23:23:18.664038 |
|
This is the second post in a series I'm writing about Chip-8 emulation. If you want to see the first one, head [here]({{< relref "/blog/01_learning_emulation.md" >}}).
Now that we have an understanding of the physical capabilities of a Chip-8 system, we can write code that will represent such a system on our computer. In this post we'll start writing some basic code - be prepared.
A Simple Representation
We'll start with the memory. Chip-8 has, as we remember, 4096 memory locations, all of which are bytes. We will create an array of 4096 bytes to match that.
unsigned char memory[4096];
If we have instructions in memory, we also need what's called a program counter, a variable that points to the next instruction in memory we have to execute. This variable would never need to be negative, and hold a value of up to 4095. The type we use for the program counter would thus have to be bigger than a char
(max unsigned value of 256). The next biggest type we can have would be a short
.
unsigned short int pc;
Next up, the registers. They all hold an 8-bit value, which is always positive. We can represent them with a 16-long array.
unsigned char v[16];
Note: I used v
for the variable name as the registers are named V0
-VF
in the Chip-8 specs.
We also need to create the I
register. As it's used to handle memory locations, it needs to be able to hold values up to 4095, just like the program counter. So we write it as a short integer.
unsigned short int i;
Now, the stack. The stack is, unsurprisingly, a stack datastructure. According to the Wiki page on Chip-8,
modern implementations [of the stack] normally have at least 16 levels
Because of that, we're going to represent the stack as a 16-long array of short integers. These integers will represent locations in memory at which a jump was made to a subroutine. If this sounds confusing, it'll be explained when we implement it. Additionally, we'd need a stack pointer, which holds a number up to 15 and gives the index in the stack of the latest value that was added.
unsigned short int stack[16];
unsigned char stackp;
We're almost there. We now need to add the timers. I do not remember seeing the size of these timers on the Wiki, so we'll represent them with generous integers.
unsigned int delay_timer;
unsigned int sound_timer;
Finally, we need a variable to hold the display pixels (which are true or false values). My code is in C, and C did not have a primitive boolean type out of the box unti C99. Because I'm mildly crazy, I will avoid this new feature and use, instead, a char. If your language of choice supports booleans (and / or you don't exhibit compulsive behaviors), by all means use them. Anyway, since the display is 64x32, we can make a display variable like this:
unsigned char display[64 * 32];
If we were to use booleans, this might look something like:
bool display[64 * 32];
Those are all the variables that we have to keep track of!
Let's group them under a single chip struct
.
struct chip_s {
unsigned char memory[4096];
unsigned short int pc;
unsigned char v[16];
unsigned short int i;
unsigned short int stack[16];
unsigned char stackp;
unsigned int delay_timer;
unsigned int sound_timer;
unsigned char display[32 * 64];
};
Since this is C, if we were to just use a struct
we'd have to use the struct
keyword every time. To avoid doing that, I'll use a typedef
to define a chip_t
type:
typedef struct chip_s chip_t;
That's it for today! By the end of this post you should have a data type representing a simple Chip-8 machine. I think the next two posts will be independent of each other: one for basic rendering with GLFW, and one for actual Chip-8 code.