How to Render a Nintendo Sprite, an Overview

Nixel, my JavaScript NES sprite editor, http://nes.alexpate.net/nixel/

Background

There are a few small pieces of info that you will need to know before we can get into pulling a sprite from a ROM and rendering it.

Games store their data in a multitude of ways, including compressing the graphics or keeping them in program data. This makes some things tricky or impossible to do without actually running the ROM.

Being able to read hexadecimal and binary will help, but a quick trip to wikipedia should answer and questions you have.

Storage

NES game data can be stored in two parts: PRG and CHR.

All games have PRG data. This is the program data for the game and is stored in 16kb banks.

Most games also have CHR data, this is where the sprite information is kept and is stored in 8kb banks. Not all games have CHR ROM, for example Zelda, Mega Man, and Metroid, all store their sprite data in PRG.

For games that do use CHR to store sprites, its pretty trivial to grab. The ROM header will tell you how many banks of PRG and CHR there are. To see the layout of the iNES header check out the nesdev wiki.

Once you have looked at the header you will know where to fetch the CHR data and how large it is.

Drawing

Sprites are 8x8 or 8x16. A 8x16 sprite is really just two 8x8 sprites.

It takes 16 bytes to make one sprite. Each byte represents one row of the sprite, however since each pixel can have four values you need another byte. 8 x 2 = 16. Each bit in the byte is a pixel. We need to combine two bytes to get the value of each pixel, because each pixel is a value of 0–3.

Lets say byte 0 is 0x4D (decimal 77) and we are combining it with byte 8 which is 0xC4 (decimal 196).

The two bytes are added together using binary addition:

  0  1  0  0  1  1  0  1
+ 1 1 0 0 0 1 0 0
------------------------
10 11 00 00 01 11 00 01
  2  3  0  0  1  3  0  1

If you notice, it interleaves the bits.

To build the sprite you take 16 contiguous bytes, and then combine them.

byte 0 <~> byte 8 = sprite row 1
byte 1 <~> byte 9 = sprite row 2
byte 2 <~> byte 10 = sprite row 3
byte 3 <~> byte 11 = sprite row 4
byte 4 <~> byte 12 = sprite row 5
byte 5 <~> byte 13 = sprite row 6
byte 6 <~> byte 14 = sprite row 7
byte 7 <~> byte 15 = sprite row 8

Example:

Given sprite data

[ 0x03, 0x0F, 0x1F, 0x1F, 0x1C, 0x24, 0x26, 0x66,
0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x3F, 0x7F ]
| Bytes 0-7 |  Binary  | Bytes 8-15 |  Binary  |  Binary Addition  |
|-----------|----------|------------|----------|-------------------|
| 0x03 | 00000011 | 0x00 | 00000000 | 00000011 |
| 0x0F | 00001111 | 0x00 | 00000000 | 00001111 |
| 0x1F | 00011111 | 0x00 | 00000000 | 00011111 |
| 0x1F | 00011111 | 0x00 | 00000000 | 00011111 |
| 0x1C | 00011100 | 0x1F | 00011111 | 00033322 |
| 0x24 | 00100100 | 0x3F | 00111111 | 00322322 |
| 0x26 | 00100110 | 0x3F | 00111111 | 00322332 |
| 0x66 | 01100110 | 0x7F | 01111111 | 03322332 |
That is the top left of Mario’s head.

But I want color!

This is great but the CHR only stores the info necessary to draw the sprite. It doesn’t contain the color information. For colors we need to obtain the palette data. Sadly this could be anywhere in the PRG data or even generated at run time.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.