blog-static/content/blog/02_learning_emulation.md
Danila Fedorin 2130b00752 Narrow some of the tags
Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
2024-03-13 15:59:46 -07:00

3.9 KiB

title date tags
Learning Emulation, Part 2 2016-11-23 23:23:18.664038
C
Emulation

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.