Skip to content

Palette Loading

Load colour palettes into PPU memory at $3F00. Required setup for any NES graphics.

Taught in Game 1, Unit 1 paletteppucoloursgraphics

Overview

The NES has 32 palette entries: 16 for backgrounds (4 palettes of 4 colours) and 16 for sprites. Colour 0 of each palette is shared as the universal background colour. Load palettes during VBlank by setting the PPU address to $3F00 and writing 32 bytes of colour data.

Code

; =============================================================================
; PALETTE LOADING - NES
; Load 32-byte palette into PPU memory
; Taught: Game 1 (Neon Nexus), Unit 1
; CPU: ~200 cycles | Memory: 32 bytes palette data
; =============================================================================

PPUSTATUS = $2002
PPUADDR   = $2006
PPUDATA   = $2007

.segment "CODE"

; Load palette data into PPU
; Call during VBlank or when rendering is disabled
load_palette:
    bit PPUSTATUS               ; Reset PPU address latch
    lda #$3F
    sta PPUADDR
    lda #$00
    sta PPUADDR                 ; PPU address = $3F00

    ldx #0
@loop:
    lda palette_data, x
    sta PPUDATA
    inx
    cpx #32
    bne @loop

    rts

; Palette data: 4 background palettes + 4 sprite palettes
palette_data:
    ; Background palettes (colours 0-15)
    .byte $0F, $00, $10, $20    ; Palette 0: black, dark grey, grey, light grey
    .byte $0F, $06, $16, $26    ; Palette 1: black, dark red, red, pink
    .byte $0F, $08, $18, $28    ; Palette 2: black, dark yellow, yellow, cream
    .byte $0F, $0A, $1A, $2A    ; Palette 3: black, dark green, green, lime

    ; Sprite palettes (colours 16-31)
    .byte $0F, $30, $20, $10    ; Palette 0: black, white, light grey, dark grey
    .byte $0F, $30, $16, $06    ; Palette 1: black, white, red, dark red
    .byte $0F, $30, $12, $02    ; Palette 2: black, white, blue, dark blue
    .byte $0F, $30, $14, $04    ; Palette 3: black, white, purple, dark purple

Typical initialisation sequence:

reset:
    ; ... standard reset code ...

@vblank1:
    bit PPUSTATUS
    bpl @vblank1

    ; Clear RAM
    ; ...

@vblank2:
    bit PPUSTATUS
    bpl @vblank2

    ; NOW safe to access PPU
    jsr load_palette

    ; ... rest of setup ...

Trade-offs

AspectCost
CPU~200 cycles
Memory32 bytes palette data
LimitationMust be done during VBlank

When to use: During game initialisation and when changing colour schemes.

When to avoid: During active rendering (causes visual glitches).

Palette Layout

$3F00: Universal background colour (shared by all palettes)
$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

Note: $3F04, $3F08, $3F0C, $3F10, $3F14, $3F18, $3F1C mirror $3F00.

NES Colour Reference

Common useful colours:

ValueColour
$0FBlack
$00Dark grey
$10Light grey
$20White-grey
$30White
$06Dark red
$16Red
$26Pink
$02Dark blue
$12Blue
$22Light blue
$0ADark green
$1AGreen
$2ALight green

Patterns: NMI Game Loop

Vault: NES