PPU palettes
The palette for the background runs from VRAM $3F00 to $3F0F; the palette for the sprites runs from $3F10 to $3F1F. Each color takes up one byte.
$3F00 | Universal background color |
$3F01-$3F03 | Background palette 0 |
$3F05-$3F07 | Background palette 1 |
$3F09-$3F0B | Background palette 2 |
$3F0D-$3F0F | Background palette 3 |
$3F11-$3F13 | Sprite palette 0 |
$3F15-$3F17 | Sprite palette 1 |
$3F19-$3F1B | Sprite palette 2 |
$3F1D-$3F1F | Sprite palette 3 |
Each palette has three colors. Each 16x16 pixel area of the background can use the backdrop color and the three colors from one of the four background palettes. The choice of palette for each 16x16 pixel area is controlled by bits in the attribute table at the end of each nametable. Each sprite can use the three colors from one of the sprite palettes. The choice of palette is in attribute 2 of each sprite (see PPU OAM).
Addresses $3F04/$3F08/$3F0C can contain unique data, though these values are not used by the PPU when normally rendering. They can still be shown using the background palette hack, explained below.
Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C.
Another way of looking at it:
43210 ||||| |||++- Pixel value from tile data |++--- Palette number from attribute table or OAM +----- Background/Sprite select
Like most early color game consoles, the NES palette is based on Hue/Saturation/Value
76543210 |||||||| ||||++++- Hue (phase) ||++----- Value (voltage) ++------- Unimplemented, reads back as 0
Hue $0 is light gray, $1-$C are blue to red to green to cyan, $D is dark gray, and $E-$F are black. The canonical code for "black" is $0F or $1D ($0D results in a "blacker than black" signal). It works this way because of the way colors are represented in an NTSC or PAL signal, with the phase of a color subcarrier controlling the hue. For details, see NTSC video.
Note that most VS Unisystem arcade PPUs have completely different palettes, and Playchoice-10 PPUs render hue $D as black.
If rendering is disabled in PPUMASK ($2001) (both background and sprite rendering is disabled; the edge clip flags do not affect), and the current VRAM address points in $3F00-$3FFF, the color indicated by this palette location will be shown on screen instead of the backdrop color. This can be used to display colors from the palette locations $3F04/$3F08/$3F0C, that are normally not used by the rendering process. A loop that fills the palette will cause each color in turn to be shown on the screen. So to avoid horizontal rainbow bar glitches while loading the palette, wait for a real vertical blank first using an NMI technique.