CHIP-8 Sprites

There are a couple of opcodes that have to do with sprites.

Sprite drawing

The Wikipedia article says this about opcode Dxxx:

Draws a sprite at coordinate (VX, VY) that has a width of 8 pixels and a height of N pixels. Each row of 8 pixels is read as bit-coded (with the most significant bit of each byte displayed on the left) starting from memory location I; I value doesn't change after the execution of this instruction. As described above, VF is set to 1 if any screen pixels are flipped from set to unset when the sprite is drawn, and to 0 if that doesn't happen.

A CHIP-8 sprite is 8-pixels wide and up to 15 lines high. Since it is 8 pixels wide, each line is in a single byte. An encoded sprite for a Space Invaders alien would look like this:

Chip 8 Sprite

   Sprite map   Binary      Hex    
   X.XXX.X.     0b10111010  $BA    
   .XXXXX..     0b01111100  $7C    
   XX.X.XX.     0b11010110  $D6    
   XXXXXXX.     0b11111110  $FE    
   .X.X.X..     0b01010100  $54    
   X.X.X.X.     0b10101010  $AA    

The hex codes for the sprite are put in memory just like that. This program sequence would draw this little guy on the display at 10, 12:

   PC    Opcodes   Assembly    
   0210  620A      MOV V2,#$0A    
   0212  630C      MOV V3,#$0C    
   0214  A220      MOV I,#$220    
   0216  D236      SPRITE V2, V3, #$6    
   0218  1240      JUMP $240    
   0220  BA7C      Sprite data for 6 bytes    
   0222  D6FE    
   0224  54AA    

Now for the drawing itself, the easiest implementation is to do it bit by bit. The algorithm is something like this:

For each line in the sprite

    Starting with MSB, For each bit in the sprite (that is still on the screen)

        If the bit is set

            Determine the address of the effected byte on the screen

            Determine the effected bit in the byte

            Check to see if the screen's bit is set and set VF appropriately

            XOR the source bit and screen bit

            Write the effected bit to the screen

It took me a while to determine that the sprite is clipped at the screen edge instead of wrapping around. If the bit in the sprite is not set, then no action is taken.

Font sprite

Implementation of opcode FX29 SPRITECHAR VX requires that we make a 4x5 font somewhere in the address space that is accessible by the program. Since the CHIP-8 program starts at 0x200 in memory, the bytes starting at zero are unused. We'll put the font there after we read the program into the memory.

I'll hand draw the font out. Since the characters are 4 pixels wide but sprites are 8 pixels wide, I'll pad the other 4 pixels with zeros. I'll show the corresponding hex number, but I'm actually just going to leave the sprite in binary in my code to make it easier to modify by hand.

Chip 8 Letters

   /*    
   Sprite   Binary      Hex    
   .xx..... 0b01100000  0x60    
   x..x.... 0b10010000  0x90    
   x..x.... 0b10010000  0x90    
   x..x.... 0b10010000  0x90    
   .xx..... 0b01100000  0x60    
   /*    

   uint8_t font4x5[] = {    
   //0    
       0b01100000,    
       0b10010000,    
       0b10010000,    
       0b10010000,    
       0b01100000,    

   //1    
       0b01100000,    
       0b00100000,    
       0b00100000,    
       0b00100000,    
       0b01110000,    

   /* etc...*/    

I'll modify my InitChip8() routine to copy the font into memory starting at zero, and give an implementation of FX29.

   #define FONT_BASE 0    
   #define FONT_SIZE 5*16  //5 bytes each * 16 characterss    

   Chip8State* InitChip8(void)    
   {    
    Chip8State* s = calloc(sizeof(Chip8State), 1);    

    s->memory = calloc(1024*4, 1);    
    s->screen = &s->memory[0xf00];    
    s->SP = 0xfa0;    
    s->PC = 0x200;    

       //Put font into lowmem    
       memcpy(&s->memory[FONT_BASE], font4x5, FONT_SIZE);    

    return s;    
   }    

   static void OpFX29(Chip8State *state, uint8_t *code)    
   {    
    int reg = code[0]&0xf;    
    state->I = FONT_BASE+(state->V[reg] * 5);    
   }    

Putting it all together

My program is going to use the MOVBCD, SPRITECHAR, and SPRITE instructions together to draw the player's score on the screen. I might write it like this:

   ;assume score is in register 3    
   ; we want to draw it at 16, 5    
   MOV         V5, #$10    
   MOV         V6, #$5    

   ;Change the score into base-10    
   ;Use scratch memory at $300    
   MOV         I,$300    
   MOVBCD      V3    

   ;Read them back into registers    
   MOVM        V0-V2,(I)    

   ;load address of sprite for hundreds digit into I    
   SPRITECHAR  V0    

   ;Draw sprite (pointed to by I) at V5, V6 for 5 lines    
   SPRITE      V5,V6,#$5    

   ; Increment x by 6 pixels    
   ADDI        V5,#$06    

   ; Do same for tens and ones digits    
   SPRITECHAR  V1    
   SPRITE      V5,V6,#$05    
   ADDI        V5,#$06    
   SPRITECHAR  V2    
   SPRITE      V5,V6,#$05    

All CHIP-8 programs I've seen are visual and pretty short. I'm not going to be able to test the emulator until I can see the results. So I'll start with a platform port next.

← Prev: chip-8-emulator   Next: chip-8-cocoa-port-part-1 →


Post questions or comments on Twitter @realemulator101, or if you find issues in the code, file them on the github repository.