Difference between revisions of "PPU palettes"

From Nesdev wiki
Jump to: navigation, search
(Define "forced blanking" to make the last two sections more concise)
Line 51: Line 51:
 
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]].
 
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]].
  
The 2A03 RGB PPU used in the PlayChoice-10 and Famicom Titler renders hue $D as black, not dark gray.
+
The 2C03 RGB PPU used in the PlayChoice-10 and Famicom Titler renders hue $D as black, not dark gray.
The 2A04 PPUs used in many [[Vs. System]] arcade games have completely different palettes as a copy protection measure.
+
The 2C04 PPUs used in many [[Vs. System]] arcade games have completely different palettes as a copy protection measure.
  
 
== Backdrop color (palette index 0) uses ==
 
== Backdrop color (palette index 0) uses ==
Line 65: Line 65:
 
This can be used to display colors from the normally unused $3F04/$3F08/$3F0C palette locations.
 
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.
 +
 +
== RGB palette values ==
 +
<pre>
 +
6C 6C 6C
 +
00 24 90
 +
00 00 D8
 +
6C 48 D8
 +
90 00 6C
 +
B4 00 6C
 +
B4 24 00
 +
90 48 00
 +
6C 48 00
 +
24 48 00
 +
00 6C 24
 +
00 90 00
 +
00 48 48
 +
00 00 00
 +
00 00 00
 +
00 00 00
 +
B4 B4 B4
 +
00 6C D8
 +
00 48 FC
 +
90 00 FC
 +
B4 00 FC
 +
FC 00 90
 +
FC 00 00
 +
D8 6C 00
 +
90 6C 00
 +
24 90 00
 +
00 90 00
 +
00 B4 6C
 +
00 90 90
 +
24 24 24
 +
00 00 00
 +
00 00 00
 +
FC FC FC
 +
6C B4 FC
 +
90 90 FC
 +
D8 6C FC
 +
FC 00 FC
 +
FC 6C FC
 +
FC 90 00
 +
FC B4 00
 +
D8 D8 00
 +
6C D8 00
 +
00 FC 00
 +
48 FC D8
 +
00 FC FC
 +
48 48 48
 +
00 00 00
 +
00 00 00
 +
FF FF FF
 +
B4 D8 FC
 +
D8 B4 FC
 +
FC B4 FC
 +
FC 90 FC
 +
FC B4 B4
 +
FC D8 90
 +
FC FC 48
 +
FC FC 6C
 +
B4 FC 48
 +
90 FC 6C
 +
48 FC D8
 +
90 D8 FC
 +
90 90 90
 +
00 00 00
 +
00 00 00
 +
</pre>

Revision as of 13:20, 23 December 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.

Address Purpose
$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 backdrop 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., which writes the backdrop color through $3F10.

Thus, indices into the palette are formed as follows:

43210
|||||
|||++- Pixel value from tile data
|++--- Palette number from attribute table or OAM
+----- Background/Sprite select

As in some second-generation game consoles, values in the NES palette are based on hue and brightness:

76543210
||||||||
||||++++- Hue (phase, determines NTSC/PAL chroma)
||++----- Value (voltage, determines NTSC/PAL luma)
++------- 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 mirrors of $1D (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.

The 2C03 RGB PPU used in the PlayChoice-10 and Famicom Titler renders hue $D as black, not dark gray. The 2C04 PPUs used in many Vs. System arcade games have completely different palettes as a copy protection measure.

Backdrop color (palette index 0) uses

During forced blanking, when neither background nor sprites are enabled in PPUMASK ($2001), the picture will show the backdrop color. If only the background or sprites are disabled, or if the left 8 pixels are clipped off, the PPU continues its normal video memory access pattern but uses the backdrop color for anything disabled.

The background palette hack

If the current VRAM address points in the range $3F00-$3FFF during forced blanking, the color indicated by this palette location will be shown on screen instead of the backdrop color. (Looking at the relevant circuitry in Visual 2C02, this is an intentional feature of the PPU and not merely a side effect of how rendering works.) 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.

RGB palette values

6C 6C 6C 
00 24 90 
00 00 D8 
6C 48 D8 
90 00 6C 
B4 00 6C 
B4 24 00 
90 48 00 
6C 48 00 
24 48 00 
00 6C 24 
00 90 00 
00 48 48 
00 00 00 
00 00 00 
00 00 00 
B4 B4 B4 
00 6C D8 
00 48 FC 
90 00 FC 
B4 00 FC 
FC 00 90 
FC 00 00 
D8 6C 00 
90 6C 00 
24 90 00 
00 90 00 
00 B4 6C 
00 90 90 
24 24 24 
00 00 00 
00 00 00 
FC FC FC 
6C B4 FC 
90 90 FC 
D8 6C FC 
FC 00 FC 
FC 6C FC 
FC 90 00 
FC B4 00 
D8 D8 00 
6C D8 00 
00 FC 00 
48 FC D8 
00 FC FC 
48 48 48 
00 00 00 
00 00 00 
FF FF FF 
B4 D8 FC 
D8 B4 FC 
FC B4 FC 
FC 90 FC 
FC B4 B4 
FC D8 90 
FC FC 48 
FC FC 6C 
B4 FC 48 
90 FC 6C 
48 FC D8 
90 D8 FC 
90 90 90 
00 00 00 
00 00 00