Contents

Writing a Chip8 emulator in C++ Part 5

Contents

One of the things I wanted to in this emulator was to show a startup message like “Ready” using the Chip 8 instructions. Nothing fancy, just print out the letters in the center (more or less) of the emulation screen. I would have to define the sprites for each of the letters to show on the screen, so that’s where I started:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  //Sprite data - starts at 0x21a
  //R
  0xe0,   //11100000
  0x90,   //10010000
  0xe0,   //11100000
  0x90,   //10010000
  0x90,   //10010000
  //E
  0xf0,   //11110000
  0x80,   //10000000
  0xf0,   //11110000
  0x80,   //10000000
  0xf0,   //11110000
  //A
  0xf0,   //11110000
  0x90,   //10010000
  0xf0,   //11110000
  0x90,   //10010000
  0x90,   //10010000
  //D
  0xe0,   //11100000
  0x90,   //10010000
  0x90,   //10010000
  0x90,   //10010000
  0xe0,   //11100000
  //Y
  0x90,   //10010000
  0x90,   //100100000
  0x60,   //01100000
  0x20,   //00100000
  0x20,   //001000000

I used 8x5 bitmaps to define all the letters, but used half width font (the upper nibble had the data, the lower nibble was all zeroes) to make the letters more compact. To prevent the spacing between the letters being too big as a result, I position each letter 5 pixels away by overwriting the blank right hand side of previous sprite. So, if X was 16 (decimal), the routine will first write 4 bits at 16, 17, 18, 19. For the next letter, it would change X to 16 + 5 = 21, which nicely also left 1 pixel space between each letter. And so on. Most of the heavy work is actually done by the DRAW instruction. I just had to ensure that the registers were setup correctly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  0x00, //0x200 CLS
  0xe0, 
  0xa2, //0x202 LOAD I, 0x21a - Sprite data address
  0x1a, 
  0x64, //0x204 LOAD r4, 5 - 5 bytes per sprite
  0x05, 
  0x61, //0x206 LOAD r1, 1 - the sprite counter
  0x01, 
  0x62, //0x208 LOAD r2, $12 - X Position
  0x12, 
  0x63, //0x20a LOAD r3, $0c - Y position
  0x0c, 
  0xd2, //0x20c DRAW r2, r3, $5 - Draw sprite
  0x35, 
  0x72, //0x20e ADD r2, 5 - Add 5 to X Position
  0x05, 
  0xf4, //0x210 ADD I, r4 - Move I to next sprite 
  0x1e, 
  0x71, //0x212 ADD r1, 1 - Increment sprite counter
  0x01, 
  0x31, //0x214 SE r1, 6 - Skip next instruction if r1 = 6
  0x06, 
  0x12, //0x216 JUMP to 0x20c
  0x0c, 
  0x12, //0x218 JUMP here - infinite loop
  0x18, 

To begin with, I cleared the screen with the CLS command. Then the I register was set to point to 0x21a, which would be the start of the sprite data and immediately followed the above set of instructions. The address was easy enough to calculate: The instructions begin at 0x200, which is where all programs usually boot from. There are 13 instructions and since each instruction takes 2 bytes, that’s a total of 26 bytes, so any data must being at 0x21a.

I load the r4 register with 5, which will be used to offset the I register to point to the next set of sprite data (because each sprite has 5 bytes of data). I also set r1 to 1, to act as a sprite counter. The registers r2 and r3 are initialized to the X and Y co-ordinates to print to (18, 12 decimal respectively). These are used by the DRAW instruction to draw 5 bytes of data in the very next line.

Having drawn one character, I update r2 (the X co-ordinate) for the next character by moving right 5 pixels. I also update the I register to point to the next set of sprite data by adding r4 (which is set to 5) to it. I increment r1 (the sprite counter) by 1 for every character drawn. The SE instruction skips the next instruction if r1 = 6 (that is we have finished drawing 5 characters). If not, the next instruction is executed which is a JUMP instruction to 0x20C (the DRAW instruction address) and acts as a draw loop. However, if the SE instruction did skip this instruction, the emulator will land on the instruction below it which is a JUMP instruction to its own address thereby creating an infinite loop. If this is not done, the emulator will mindlessly continue to try to execute the following addresses but since there are no more valid instructions in our little program this will result in reading in garbage.

I stored the above in a character array to use as a boot ROM at startup, which would be uploaded to 0x200 just like any other ROM data :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
unsigned char boot_rom[] = 
{
  0x00, //0x200 CLS
  0xe0, 
  0xa2, //0x202 LOAD I, 0x21a - Sprite data address
  0x1a, 
  0x64, //0x204 LOAD r4, 5 - 5 bytes per sprite
  0x05, 
  0x61, //0x206 LOAD r1, 1 - the sprite counter
  0x01, 
  0x62, //0x208 LOAD r2, $12 - X Position
  0x12, 
  0x63, //0x20a LOAD r3, $0c - Y position
  0x0c, 
  0xd2, //0x20c DRAW r2, r3, $5 - Draw sprite
  0x35, 
  0x72, //0x20e ADD r2, 5 - Add 5 to X Position
  0x05, 
  0xf4, //0x210 ADD I, r4 - Move I to next sprite 
  0x1e, 
  0x71, //0x212 ADD r1, 1 - Increment sprite counter
  0x01, 
  0x31, //0x214 SE 1, 6 - Skip next instruction if r1 = 6
  0x06, 
  0x12, //0x216 JUMP to 0x20c
  0x0c, 
  0x12, //0x218 JUMP here - infinite loop
  0x18, 

  //Sprite data - starts at 0x21a
  //R
  0xe0,   //11100000
  0x90,   //10010000
  0xe0,   //11100000
  0x90,   //10010000
  0x90,   //10010000
  //E
  0xf0,   //11110000
  0x80,   //10000000
  0xf0,   //11110000
  0x80,   //10000000
  0xf0,   //11110000
  //A
  0xf0,   //11110000
  0x90,   //10010000
  0xf0,   //11110000
  0x90,   //10010000
  0x90,   //10010000
  //D
  0xe0,   //11100000
  0x90,   //10010000
  0x90,   //10010000
  0x90,   //10010000
  0xe0,   //11100000
  //Y
  0x90,   //10010000
  0x90,   //100100000
  0x60,   //01100000
  0x20,   //00100000
  0x20,   //001000000
};

And that’s it! This concludes most of what I had to say on the topic of Chip8 emulation. I had fun writing the emulator and while it seems all too easy at first glance, sometimes connecting the dots isn’t, which is why I thought I’ll pen this article to illustrate the steps I used in case it’s of use to someone just starting out own their own.

I didn’t talk about sound, input, openGL or the GUI system because that would have needlessly complicated the article as they deal with platform and non-Chip8 aspects of emulation and come with their own set of baggage and quirks. But for that there is the source code on the GitHub repository with all the source code. Feedback and comments are always welcome. Thanks for reading!