Backgrounds

The NES has one background layer. The background consists of:

  1. Tiles: 8×8 groups of pixels, each with four colors. Tiles are placed next to each other to cover the screen, constructing the entire background. Tiles are stored in a region of the PPU's memory called the pattern table.

  2. Palettes: the colors applied to the tiles. All tiles use colors 0 through 3, but it's the palettes that decide what these colors mean.

  3. Name tables: data specifying which tiles go where in the background.

  4. Attribute tables: data specifying which palettes apply to which part of the background.

There are also a bunch of registers that control how the background is displayed. We will go over all of these concepts in this chapter, culminating in some interactive demos that let you play around with some backgrounds. I will also tie back these concepts to both the introductory "1-2-3" demo and the "brick wall" demo you've already seen. As preview, the brick wall has one tile, one palette, and basic name and attribute tables that request the same tile and palette across the entire screen.

Decomposing the "1-2-3" demo

Let's start making sense of the above concepts by visualizing how the "1-2-3" demo from the introduction was made. We won't look at any code yet, just how we might organize a background prior to implementing it in code. The demo consists of one background, with a lot of unique tiles arranged to form the numerals on the screen. Nevertheless, the background does reuse some tiles multiple times, when possible! Finally, the entire background uses a single palette. Take a look at the name and attribute tables for this background by toggling the different overlays below:

A screenshot of the 1-2-3 demo from the introduction, showing the final background with the default colors. The 1 is blue, the 2 is green and the 3 is red.

There are a couple of things to notice. First, you'll see the name table breaks up the background into 8×8 blocks, the tiles. Each tile is identified by an index into the entire set of tiles. For example, the all black tile is index $00. Here are three tiles, one from each numeral:

Tile from numeral 1 Tile from numeral 2 Tile from numeral 3

The indices of these tiles are $01, $1A and $4D respectively. Notice they are grayscale because palettes have not been applied yet. In total, there are $80 (128) tiles in the tileset. Sometimes, tiles are reused within a single numeral. Notably, the all "one-color" tile is used many times within each numeral, though some other tiles are reused when possible.

Next, the attribute table breaks up the background into 16×16 blocks. The number in each block represents with of the four possible active background applies to that region. The 1-2-3 demo only uses one palette, applying that to every block in the attribute table. This is the reason the "all-filled-in" tiles in each numeral is a separate tile in the tileset, since each such tile has to point to a different color. If I instead chose to apply different palettes to each numeral, I could have reused a single "all-filled-in" tile across the three numerals.

Configuring the background with PPU registers

Before we can jump into the graphics, we need to talk about how the backgrounds are configured. This configuration tells the NES to, for example, show the background at all, and where to get the data when rendering the background. This is done with two memory-mapped registers: PPUMASK and PPUCTRL. This is our first time looking at memory-mapped registers, so let's explore the first one in some detail, and then we can look at the second one more quickly.

Showing and hiding the background with PPUMASK

The instruction set of a processor typically lets you do things like store data to memory, read it back, perform arithmetic, etc. But what about performing machine-specific operations like enabling or disabling the background layer on the NES? Instead of adding custom instructions just for this machine, the NES uses a common technique known as memory-mapped IO. Normally, when you store a value into a memory location, all that does is update the value at that location. When you read from a memory location, you get back what you previously stored. With memory-mapped IO, the act of writing to certain memory locations tells the machine to do something, and reading from certain other memory locations return data from the machine instead of data you put there.

The first such memory location we'll look at is at location $2001 in memory. The location is one byte long, so it's referred to as a memory-mapped register, a small, fixed-width memory location for writing or reading data (in this case, only writing). Instead of using the memory location, the register is a given a name: PPUMASK.

PPUMASK is write-only, and within its eight bits, it contains multiple pieces of data. The tables below break down the bits into the constituent pieces, allowing you to play with them. Technically, any bit can be either 0 or 1, but in some cases, I only let you toggle through specific values for the multi-bit data for illustrative purposes. I've also set up the default values to what we will set them to.

PPUMASK ($2001, write-only)
#7#6#5#4#3#2#1#0
CI
SpV
BgV
SpC
BgC
Gr
BitsNameDescription
5-7CI

Emphasize intensity for a certain color. For example, setting bit 7 emphasizes blues. Setting bit 6 emphasizes reds on NTSC systems, and greens on PAL systems. It's possible to set more than one bit, but this demo only shows the 1-color emphasis configurations. Some TVs have problems when multiple bits are set.

See the Nesdev wiki for more details.

Selected: Regular color
4SpV

Sprite visibility. Set to 1 to show sprites. We will discuss sprites in the next chapter.

Selected: Hide sprites
3BgV

Background visibility. Set to 1 to show the background. Otherwise, you'll get a black screen (though you'll see sprites on top of the black screen if sprites are visible).

Selected: Show backgrounds
2SpC

Sprite clipping. Set to 0 to hide any sprites in the leftmost 8×8 column of the screen.

Selected: Sprites not displayed in leftmost 8×8 column
1BgC

Background clipping. Set to 0 to hide any background tiles in the leftmost 8×8 column of the screen. This is useful when dealing with horizontal scrolling, allowing you to dynamically load in tiles in the leftmost column.

Selected: Backgrounds are displayed in leftmost 8×8 column
0Gr

Grayscale vs. color display. Set to 0 for a color display. Interestingly, this setting is independent of the color emphasis, so if you ask for a grayscale display with blues emphasized, you'll get a blue tint on the entire screen!

Selected: Color display

So how do you actually configure the PPU using PPUMASK? First, you load into the accumulator the value you want to write into the register. The easiest way to do this is to use the % notation for binary numbers, to reflect the exact bit values. Once you have the value you want to write, you store the accumulator value into location $2001. Here's the code for it, based on the specific configure we want for the brick wall program:

  lda #%00001010 ; unmodified color intensity
                 ; sprites hidden
                 ; backgrounds visible
                 ; sprites clipped -- ignored
                 ; backgrounds not clipped
                 ; color display
  sta $2001

Notice that we only want the background, filling the entire screen, and no sprites.

Note: you want to run these instructions at a specific time in the program execution. TODO: probably need an earlier chapter on overall program structure

Configuring the PPU with PPUCTRL

Let's do some more practice. There's one more register you need to write to in order to finish configuring the PPU: the PPUCTRL register at $2000. Another write-only register, PPUCTRL tells the NES, among other things, where to read the background data from. We will talk more about what the background data looks like below.

PPUCTRL ($2000, write-only)
#7#6#5#4#3#2#1#0
VB
BgE
SSz
BgP
SpP
VI
BgN
BitsNameDescription
7VB

Generate a Non-Maskable Interrupt (NMI) at the start of the vertical blanking interval (V-Blank). We will discuss interrupts and V-Blank in the next chapter. For now, because we don't make changes to the screen after the initial setup, we just leave this off.

Selected: Don't generate an NMI each V-Blank
6BgE

Whether to read the read the background palette color from the EXT pins on the machine. Keep this set to 0. Again, the Nesdev wiki has more detailed information.

Selected: Read background palette color from EXT pins (always palette entry zero)
5SSz

Sprite size. Set to 0 for 8×8 pixel sprites and 1 for 8×16 sprites.

Selected: 8×8 sprites
4BgP

Background pattern table address in VRAM. Set to 0 for $0000 and 1 for $1000. The pattern table is where the tile data is stored.

Selected: Read background pattern table data from $0000
3SpP

Sprite pattern table address in VRAM. Set to 0 for $0000 and 1 for $1000. The pattern table is where the tile data is stored. It's possible to use the same tile set for both backgrounds and sprites.

Selected: Read sprite pattern table data from $1000
2VI

VRAM address increment. As we will see later, reading or writing data to the PPU's memory involves another register. The address increment determines how much to adjust the PPU memory address when doing these reads and writes. Set to 0 to increment by 1 byte each time and 1 for 32 bytes at a time.

More information below (TODO: link to section below)

Selected: Increment VRAM address by 1 byte after each read/write
0-1BgN

Where to start reading the background name table from in memory.

Selected: Start reading background nametable from $2000

Once again, for the brick wall program, we want to configure the background and any sprite-specific configuration is irrelevant (since sprites are not turned on):

  lda #%00001000 ; disable NMI on VBlank
                 ; 8x8 sprites -- ignored
                 ; background pattern table at $0000
                 ;     sprite pattern table at $1000 -- ignored
                 ; name table at $2000
  sta $2000

Tiles

As we've seen above, tiles are just 8×8 blocks of grayscale pixels (when we cover sprites later, we will see that sprite tiles can also be 8×16). Each tile can have four possible colors, specified as a 0, 1, 2 or 3. These color indices are reinterpreted into actual colors by indexing into the active palette for the region of the background the tile ends up in. That means a single tile could show up in different colors if the name and attribute tables work together to put that tile in different regions with different palettes.

For the rest of this section on tiles, we'll only talk about grayscale. Just remember that even the grayscale representations I'm using are arbitrary. There's no reason the color at index 0 has to be darker than the color at index 2.

Representing tiles in memory

TODO: bit planes

0000111111000000
0011111111100000
1111111110010100
1111111111100100
1111101110010100
0010101001010000
0000010101000000
0000000000000000
Most significant bit
0000111111000000
0011111111100000
1111111110010100
1111111111100100
1111101110010100
0010101001010000
0000010101000000
0000000000000000
Least significant bit

Below, you can play around with the byte format of a tile, one row at a time. The top row represents a row of pixels, which I've initialized to match the first row of our brick tile. Click a box to change which of the four colors that pixel is. Below the pixels are the binary representation for each color, followed by the binary and hexadecimal representations of the least-significant and most-significant bit planes.

01
01
01
10
10
10
10
00

While you want to use automated tools to help you define your graphics—as I'll explain below—it's worth doing this conversion from pixel art to raw bytes by hand once. Try it below! The top row of bytes contains the bytes for the least-significant bit plane, and the bottom row contains the bytes for the most-significant bit plane. For any row, you can get the byte value using the above row↦bytes converter. Put the byte for the least-significant bit plane into the first input, and the byte for the most-significant bit plane into the second input. Repeat for all eight rows of the tile to construct a full tile.

  .byte 
  .byte 
11122220
11111220
01111100
00000000
22201112
12201111
11000111
00000000

Obviously, this is very tedious to do, especially when you want to update your tiles with small tweaks. NESASM provides a shortcut using the .defchr assembler directive. With this directive, you specify the tile with the numbers 0, 1, 2 and 3, and NESASM automatically decomposes the tile into bit planes before producing the necessary bytes. The assembly code below updates based on the tile you picked above.

 .defchr $11122220,\
         $11111220,\
         $01111100,\
         $00000000,\
         $22201112,\
         $12201111,\
         $11000111,\
         $00000000,\

Where the tiles are stored

Now that we've seen the format the tiles are stored in, we need to wrap up the tileset storage by talking about where the tile data is stored. Each tile takes up 16 bytes, and up to 256 tiles can be stored contiguously in memory, taking up 4Kib ($41000 bytes). Such a block of memory is called a pattern table.

TODO: CHR-ROM + PPU memory space

TODO: Storing tile data in CHR-ROM

TODO: PPUCTRL-based configuration

Revisiting the brick demo

And with that, you can now see how I actually defined the brick tile. Try messing around with the tile definition (note that this definition is not tied to the above demos). Even though the brick tile doesn't use color #3, I've set up the palette to have blue for that color, in case you want to use another color!

Palettes

Name and attribute Tables

Background scrolling

Name table mirroring