PPU palettes: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(Clarify where the backdrop color is used.)
(Add some internal details related to disabling background and sprite rendering and restructure things a bit)
Line 48: Line 48:
Note that most VS Unisystem arcade PPUs have completely different palettes, and Playchoice-10 PPUs render hue $D as black.
Note that most VS Unisystem arcade PPUs have completely different palettes, and Playchoice-10 PPUs render hue $D as black.


When background rendering is disabled and sprite rendering is enabled in [[PPU registers|PPUMASK ($2001)]], the background uses the backdrop color. The backdrop color is also used for the leftmost 8 pixels of the background when those are hidden via PPUMASK, provided sprite rendering is enabled.
== Backdrop (palette index 0) uses when rendering is disabled ==
 
If rendering is enabled (i.e., if either background or sprite rendering is enabled in [[PPU registers|PPUMASK ($2001)]]), the backdrop color will be used for the background when background rendering is disabled, and will also be used for the leftmost 8 pixels of the background when those are hidden via PPUMASK.
 
Internally, disabling either background or sprite rendering (but not both) causes 0 to be used as the palette index for the corresponding background and sprite pixels, but does not otherwise affect what background and sprite VRAM tile fetches the PPU performs during rendering.
 
== The background palette hack ==


When rendering is ''completely'' disabled in PPUMASK (both background and sprite rendering disabled) and the current VRAM address points in the range $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 normally unused $3F04/$3F08/$3F0C palette locations.
When rendering is ''completely'' disabled in PPUMASK (both background and sprite rendering disabled) and the current VRAM address points in the range $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 normally unused $3F04/$3F08/$3F0C palette locations.
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.
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.

Revision as of 03:48, 7 April 2013

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 (since the pattern values that would otherwise select those cells select the background color instead). They can still be shown using the background palette hack, explained below.

Addresses $3F10/$3F14/$3F18/$3F1C are mirrors of $3F00/$3F04/$3F08/$3F0C. Note that this goes for writing as well as reading; a symptom of not having implemented this correctly in an emulator is the sky being black in Super Mario Bros (as it writes the background color palette entry through the mirror).

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 should not be used; it results in a "blacker than black" signal that may cause problems for some TVs. 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.

Backdrop (palette index 0) uses when rendering is disabled

If rendering is enabled (i.e., if either background or sprite rendering is enabled in PPUMASK ($2001)), the backdrop color will be used for the background when background rendering is disabled, and will also be used for the leftmost 8 pixels of the background when those are hidden via PPUMASK.

Internally, disabling either background or sprite rendering (but not both) causes 0 to be used as the palette index for the corresponding background and sprite pixels, but does not otherwise affect what background and sprite VRAM tile fetches the PPU performs during rendering.

The background palette hack

When rendering is completely disabled in PPUMASK (both background and sprite rendering disabled) and the current VRAM address points in the range $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 normally unused $3F04/$3F08/$3F0C palette locations. 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.