PPU palettes

From NESdev Wiki
Revision as of 17:27, 17 June 2023 by Drag (talk | contribs) (→‎The background palette hack: Removed "the the")
Jump to navigationJump to search

The NES has a limited selection of color outputs. A 6-bit value in the palette memory area corresponds to one of 64 outputs. The emphasis bits of the PPUMASK register ($2001) provide an additional color modifier.

For more information on how the colors are generated on an NTSC NES, see: NTSC video. For additional information on how the colors are generated on a PAL NES, see: PAL video.

Memory Map

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).

It works this way because of the way colors are represented in a composite NTSC or PAL signal, with the phase of a color subcarrier controlling the hue. For details regarding signal generation and color decoding, see NTSC video.

The canonical code for "black" is $0F or $1D.

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.

Palettes

The 2C02 (NTSC) and 2C07 (PAL) PPU is used to generate an analog composite video signal. These were used in most home consoles.

The 2C03, 2C04, and 2C05, on the other hand, all output analog red, green, blue, and sync (RGBS) signals. The sync signal contains horizontal and vertical sync pulses in the same format as an all-black composite signal. Each of the three video channels uses a 3-bit DAC driven by a look-up table in a 64x9-bit ROM inside the PPU. The look-up tables (one digit for each of red, green, and blue, in order) are given below.

RGB PPUs were used mostly in arcade machines (e.g. Vs. System, Playchoice 10), as well as the Sharp Famicom Titler.

2C02

The RF Famicom, AV Famicom, NES (both front- and top-loading), and the North American version of the Sharp Nintendo TV use the 2C02 PPU. Unlike some other consoles' video circuits, the 2C02 does not generate RGB video and then encode that to composite. Instead it generates NTSC video directly in the composite domain, decoded by the television receiver into RGB to drive its picture tube.

Most emulators can use a predefined palette, such as one commonly stored in common .pal format, in which each triplet represents the sRGB color that results from decoding a large flat area with a given palette value. The following palette was generated using Persune's palette generator v0.3.6 with the following arguments: palgen-persune.py -rfc "ITU-R BT.709" -phs -5.0 -blp 0.0 -o 2C02G.pal

$00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0A $0B $0C $0D $0E $0F
$10 $11 $12 $13 $14 $15 $16 $17 $18 $19 $1A $1B $1C $1D $1E $1F
$20 $21 $22 $23 $24 $25 $26 $27 $28 $29 $2A $2B $2C $2D $2E $2F
$30 $31 $32 $33 $34 $35 $36 $37 $38 $39 $3A $3B $3C $3D $3E $3F

Other tools for generating a palette include one by Bisqwit and one by Drag. These simulate generating a large area of one flat color and then decoding that with the adjustment knobs set to various settings.

2C07

The PAL PPU (2C07) generates a composite PAL video signal, which has a -15 degree hue shift relative to the 2C02 due to a different colorburst reference phase generated by the PPU ($x7 rather than $x8), in addition to the PAL colorburst phase being defined as -U ± 45 degrees. The following palette was generated using Persune's palette generator with the following arguments: palgen-persune.py -rfc "ITU-R BT.709" -blp 0.0 -cbr 7 -pal -o 2C07.pal

$00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0A $0B $0C $0D $0E $0F
$10 $11 $12 $13 $14 $15 $16 $17 $18 $19 $1A $1B $1C $1D $1E $1F
$20 $21 $22 $23 $24 $25 $26 $27 $28 $29 $2A $2B $2C $2D $2E $2F
$30 $31 $32 $33 $34 $35 $36 $37 $38 $39 $3A $3B $3C $3D $3E $3F

2C03 and 2C05

This palette is intentionally similar to the NES's standard palette, but notably is missing the greys in entries $2D and $3D. The 2C03 is used in Vs. Duck Hunt, Vs. Tennis, all PlayChoice games, the Famicom Titler, and the Famicom TV. The 2C05 is used in some later Vs. games as a copy protection measure. Both have been used in RGB mods for the NES, as a circuit implementing A0' = A0 xor (A1 nor A2) can swap PPUCTRL and PPUMASK to make a 2C05 behave as a 2C03.

The formula for mapping the DAC integer channel value to 8-bit per channel color is C = 255 * DAC / 7.

$x0 $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $xA $xB $xC $xD $xE $xF
$0x 333 014 006 326 403 503 510 420 320 120 031 040 022 000 000 000
$1x 555 036 027 407 507 704 700 630 430 140 040 053 044 000 000 000
$2x 777 357 447 637 707 737 740 750 660 360 070 276 077 000 000 000
$3x 777 567 657 757 747 755 764 772 773 572 473 276 467 000 000 000

Note that some of the colors are duplicates: $0B and $1A = 040, $2B and $3B = 276.

Kevtris's dumped palettes imply that the monochrome bit has unique function on the 2C03 and 2C05: instead of just using the leftmost column of the palette (and forcing the lower four bits to zero), instead the top two bits index into a palette with colors: 000, 333, 777, 777. Given that the 2C04 does not do this, this exception feels weird, and corroboration would be appreciated.

2C04

All four 2C04 PPUs contain the same master palette, but in different permutations. It's almost a superset of the 2C03/5 palette, adding four greys, six other colors, and making the bright yellow more pure.

Note that using the greyscale bit in PPUMASK will remap the palette by index, not by color. This means that with the scrambled palettes, each row will remap to the colors in the $0X column for that 2C04 version.

Visualization tool: RGB PPU Palette Converter

No version of the 2C04 was ever made with the below ordering, but it shows the similarity to the 2C03:

333 014 006 326 403 503 510 420 320 120 031 040 022 111 003 020
555 036 027 407 507 704 700 630 430 140 040 053 044 222 200 310
777 357 447 637 707 737 740 750 660 360 070 276 077 444 000 000
777 567 657 757 747 755 764 770 773 572 473 276 467 666 653 760

The PPUMASK monochrome bit has the same implementation as on the 2C02, and so it has an unintuitive effect on the 2C04 PPUs; rather than forcing colors to grayscale, it instead forces them to the first column.

RP2C04-0001

MAME's source claims that Baseball, Freedom Force, Gradius, Hogan's Alley, Mach Rider, Pinball, and Platoon require this palette.

755,637,700,447,044,120,222,704,777,333,750,503,403,660,320,777
357,653,310,360,467,657,764,027,760,276,000,200,666,444,707,014
003,567,757,070,077,022,053,507,000,420,747,510,407,006,740,000
000,140,555,031,572,326,770,630,020,036,040,111,773,737,430,473
Palette colors
$x0 $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $xA $xB $xC $xD $xE $xF
$0x 755 637 700 447 044 120 222 704 777 333 750 503 403 660 320 777
$1x 357 653 310 360 467 657 764 027 760 276 000 200 666 444 707 014
$2x 003 567 757 070 077 022 053 507 000 420 747 510 407 006 740 000
$3x 000 140 555 031 572 326 770 630 020 036 040 111 773 737 430 473

RP2C04-0002

MAME's source claims that Castlevania, Mach Rider (Endurance Course), Raid on Bungeling Bay, Slalom, Soccer, Stroke & Match Golf (both versions), and Wrecking Crew require this palette.

000,750,430,572,473,737,044,567,700,407,773,747,777,637,467,040
020,357,510,666,053,360,200,447,222,707,003,276,657,320,000,326
403,764,740,757,036,310,555,006,507,760,333,120,027,000,660,777
653,111,070,630,022,014,704,140,000,077,420,770,755,503,031,444
Palette colors
$x0 $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $xA $xB $xC $xD $xE $xF
$0x 000 750 430 572 473 737 044 567 700 407 773 747 777 637 467 040
$1x 020 357 510 666 053 360 200 447 222 707 003 276 657 320 000 326
$2x 403 764 740 757 036 310 555 006 507 760 333 120 027 000 660 777
$3x 653 111 070 630 022 014 704 140 000 077 420 770 755 503 031 444

RP2C04-0003

MAME's source claims that Balloon Fight, Dr. Mario, Excitebike (US), Goonies, and Soccer require this palette.

507,737,473,555,040,777,567,120,014,000,764,320,704,666,653,467
447,044,503,027,140,430,630,053,333,326,000,006,700,510,747,755
637,020,003,770,111,750,740,777,360,403,357,707,036,444,000,310
077,200,572,757,420,070,660,222,031,000,657,773,407,276,760,022
Palette colors
$x0 $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $xA $xB $xC $xD $xE $xF
$0x 507 737 473 555 040 777 567 120 014 000 764 320 704 666 653 467
$1x 447 044 503 027 140 430 630 053 333 326 000 006 700 510 747 755
$2x 637 020 003 770 111 750 740 777 360 403 357 707 036 444 000 310
$3x 077 200 572 757 420 070 660 222 031 000 657 773 407 276 760 022

RP2C04-0004

MAME's source claims that Clu Clu Land, Excitebike (Japan), Ice Climber (both versions), and Super Mario Bros. require this palette.

430,326,044,660,000,755,014,630,555,310,070,003,764,770,040,572
737,200,027,747,000,222,510,740,653,053,447,140,403,000,473,357
503,031,420,006,407,507,333,704,022,666,036,020,111,773,444,707
757,777,320,700,760,276,777,467,000,750,637,567,360,657,077,120
Palette colors
$x0 $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $xA $xB $xC $xD $xE $xF
$0x 430 326 044 660 000 755 014 630 555 310 070 003 764 770 040 572
$1x 737 200 027 747 000 222 510 740 653 053 447 140 403 000 473 357
$2x 503 031 420 006 407 507 333 704 022 666 036 020 111 773 444 707
$3x 757 777 320 700 760 276 777 467 000 750 637 567 360 657 077 120

Games compatible with multiple different PPUs

Some games don't require that arcade owners have the correct physical PPU.

At the very least, the following games use some of the DIP switches to support multiple different PPUs:

  • Atari RBI Baseball
  • Battle City
  • Star Luster
  • Super Sky Kid
  • Super Xevious
  • Tetris (Tengen)
  • TKO Boxing

LUT approach

Emulator authors may implement the 2C04 variants as a LUT indexing the "ordered" palette. This has the added advantage of being able to use preexisting .pal files if the end user wishes to do so.

Repeating colors such as 000 and 777 may index into the same entry of the "ordered" palette, as this is functionally identical.

 const unsigned char PaletteLUT_2C04_0001 [64] ={
    0x35,0x23,0x16,0x22,0x1C,0x09,0x1D,0x15,0x20,0x00,0x27,0x05,0x04,0x28,0x08,0x20,
    0x21,0x3E,0x1F,0x29,0x3C,0x32,0x36,0x12,0x3F,0x2B,0x2E,0x1E,0x3D,0x2D,0x24,0x01,
    0x0E,0x31,0x33,0x2A,0x2C,0x0C,0x1B,0x14,0x2E,0x07,0x34,0x06,0x13,0x02,0x26,0x2E,
    0x2E,0x19,0x10,0x0A,0x39,0x03,0x37,0x17,0x0F,0x11,0x0B,0x0D,0x38,0x25,0x18,0x3A
};

const unsigned char PaletteLUT_2C04_0002 [64] ={
    0x2E,0x27,0x18,0x39,0x3A,0x25,0x1C,0x31,0x16,0x13,0x38,0x34,0x20,0x23,0x3C,0x0B,
    0x0F,0x21,0x06,0x3D,0x1B,0x29,0x1E,0x22,0x1D,0x24,0x0E,0x2B,0x32,0x08,0x2E,0x03,
    0x04,0x36,0x26,0x33,0x11,0x1F,0x10,0x02,0x14,0x3F,0x00,0x09,0x12,0x2E,0x28,0x20,
    0x3E,0x0D,0x2A,0x17,0x0C,0x01,0x15,0x19,0x2E,0x2C,0x07,0x37,0x35,0x05,0x0A,0x2D
};

const unsigned char PaletteLUT_2C04_0003 [64] ={
    0x14,0x25,0x3A,0x10,0x0B,0x20,0x31,0x09,0x01,0x2E,0x36,0x08,0x15,0x3D,0x3E,0x3C,
    0x22,0x1C,0x05,0x12,0x19,0x18,0x17,0x1B,0x00,0x03,0x2E,0x02,0x16,0x06,0x34,0x35,
    0x23,0x0F,0x0E,0x37,0x0D,0x27,0x26,0x20,0x29,0x04,0x21,0x24,0x11,0x2D,0x2E,0x1F,
    0x2C,0x1E,0x39,0x33,0x07,0x2A,0x28,0x1D,0x0A,0x2E,0x32,0x38,0x13,0x2B,0x3F,0x0C
};

const unsigned char PaletteLUT_2C04_0004 [64] ={
    0x18,0x03,0x1C,0x28,0x2E,0x35,0x01,0x17,0x10,0x1F,0x2A,0x0E,0x36,0x37,0x0B,0x39,
    0x25,0x1E,0x12,0x34,0x2E,0x1D,0x06,0x26,0x3E,0x1B,0x22,0x19,0x04,0x2E,0x3A,0x21,
    0x05,0x0A,0x07,0x02,0x13,0x14,0x00,0x15,0x0C,0x3D,0x11,0x0F,0x0D,0x38,0x2D,0x24,
    0x33,0x20,0x08,0x16,0x3F,0x2B,0x20,0x3C,0x2E,0x27,0x23,0x31,0x29,0x32,0x2C,0x09
};

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

During forced blanking, if the current VRAM address ever points to a palette register (i.e., $3F00-$3FFF), then the color in that palette register will be output to the screen instead of the backdrop color, for as long as the VRAM address is pointing there during the forced blanking. This can be used to display colors from the normally unused $3F04/$3F08/$3F0C palette locations. (Looking at the relevant circuitry in Visual 2C02, this happens because the palette RAM's output is not disconnected from the video output circuitry when not rendering.)

A loop that fills the palette will cause each color in turn to be shown on the screen, so to avoid rainbow artifacts while loading the palette, wait for a real vertical blank first using an NMI technique.

Color names

When programmers and artists are communicating, it's often useful to have human-readable names for colors. Many graphic designers who have done web or game work will be familiar with HTML color names.

Luma

  • $0F: Black
  • $00: Dark gray
  • $10: Light gray or silver
  • $20: White
  • $01-$0C: Dark colors, medium mixed with black
  • $11-$1C: Medium colors, similar brightness to dark gray
  • $21-$2C: Light colors, similar brightness to light gray
  • $31-$3C: Pale colors, light mixed with white

Chroma

Names for hues:

  • $x0: Gray
  • $x1: Azure
  • $x2: Blue
  • $x3: Violet
  • $x4: Magenta
  • $x5: Rose
  • $x6: Red or maroon
  • $x7: Orange
  • $x8: Yellow or olive
  • $x9: Chartreuse
  • $xA: Green
  • $xB: Spring
  • $xC: Cyan

RGBI

These NES colors approximate colors in 16-color RGBI palettes, such as the CGA, EGA, or classic Windows palette, though the NES doesn't really have particularly good approximations:

  • $0F: 0/Black
  • $02: 1/Navy
  • $1A: 2/Green
  • $1C: 3/Teal
  • $06: 4/Maroon
  • $14: 5/Purple
  • $18: 6/Olive ($17 for the brown in CGA/EGA in RGB)
  • $10: 7/Silver
  • $00: 8/Gray
  • $12: 9/Blue
  • $2A: 10/Lime
  • $2C: 11/Aqua/Cyan
  • $16: 12/Red
  • $24: 13/Fuchsia/Magenta
  • $28: 14/Yellow
  • $30: 15/White

See also

References