Skip to content

Colour Lives in a Palette

Open up the stage light. On the NES, a tile's colour isn't stored with the tile — it lives in a separate little table, and the tile only points at it. Change the palette and the same block changes colour, with no touch to the screen at all.

Last unit the block came out white, and we never said white — we just wrote two mystery bytes to $3F00 and called them the stage light. Time to open it up.

Here’s the idea that catches every newcomer to the NES: a tile carries no colour of its own. The byte you wrote to the nametable was a tile number — a shape. The shape’s colour is decided somewhere else entirely, in a separate little table called the palette. The tile’s pixels don’t say “white”; they say “colour 1”, and colour 1 is whatever the palette currently says it is. Change the palette, and every pixel pointing at that slot changes with it — though you never touched the screen’s memory.

This is the opposite of how it feels in BASIC, where a thing you draw is a colour. On the NES, shape and colour are two separate maps, and the tile is just a pointer from one into the other.

What you’ll see by the end

A small red block in the top-left corner of a light-blue NES screen.
The very same block as last unit — now red on a light-blue field. Nothing in the screen's memory changed; we only rewrote the palette.

A red block on a light-blue screen. The tile write is identical to Unit 5 — same number, same cell. The only thing that changed is the palette, and that changed both the block’s colour and the colour of everything behind it.

A background palette is four colours

The PPU’s palette table starts at $3F00. A background palette is four entries:

Address Role
$3F00 the backdrop — the colour behind everything, and what “colour 0” draws
$3F01 colour 1
$3F02 colour 2
$3F03 colour 3

Every background pixel is two bits — a number from 0 to 3 — picking one of these four. Our solid block is made entirely of “colour 1” pixels, so it draws in whatever sits at $3F01. Last unit that was white; this unit it’s red ($16). The blank tiles everywhere else are all “colour 0”, so they draw in the backdrop at $3F00 — light blue ($21) this time. Same four pour-throughs of the window you already know:

    lda #$21                ; $3F00 backdrop      = light blue
    sta $2007
    lda #$16                ; $3F01 colour 1      = red  (the block)
    sta $2007
    lda #$27                ; $3F02 colour 2      = orange
    sta $2007
    lda #$30                ; $3F03 colour 3      = white
    sta $2007

(Colours 2 and 3 aren’t used by our block — it’s all colour 1 — but a palette is always four entries, so we fill them in. We’ll use the other slots once a tile has more than one colour in it, next unit.)

The program

The tile write in the middle is exactly Unit 5’s. Only the palette at the top has changed:

; ============================================================================
; Meet the Machine (NES) - Unit 6: Colour Lives in a Palette
;
; The block's colour does not live in the tile. It lives in a separate little
; table - the palette - and the tile only points at it. Change the palette and
; the same tile changes colour, with no change to the screen's memory at all.
; ============================================================================

.segment "HEADER"
    .byte "NES", $1a
    .byte 2
    .byte 1
    .byte $00, $00

.segment "CODE"

reset:
    sei
    cld
    ldx #$40
    stx $4017
    ldx #$ff
    txs
    inx
    stx $2000
    stx $2001
    stx $4010
warm1:
    bit $2002
    bpl warm1
warm2:
    bit $2002
    bpl warm2

    ; --- the palette: a small table of colours the tiles point at ---
    bit $2002
    lda #$3f
    sta $2006
    lda #$00
    sta $2006               ; aim at palette address $3F00
    lda #$21                ; $3F00 backdrop      = light blue
    sta $2007
    lda #$16                ; $3F01 palette-0 col 1 = red   (the block's colour)
    sta $2007
    lda #$27                ; $3F02 palette-0 col 2 = orange (unused here)
    sta $2007
    lda #$30                ; $3F03 palette-0 col 3 = white  (unused here)
    sta $2007

    ; --- the same block as last unit: tile 1 at the top-left cell ---
    bit $2002
    lda #$20
    sta $2006
    lda #$00
    sta $2006
    lda #$01
    sta $2007

    ; --- square the picture up and turn it on ---
    bit $2002
    lda #$00
    sta $2006
    sta $2006
    sta $2005
    sta $2005
    lda #$1e
    sta $2001

forever:
    jmp forever

nmi:
    rti
irq:
    rti

.segment "VECTORS"
    .word nmi
    .word reset
    .word irq

.segment "CHARS"
    .byte $00,$00,$00,$00,$00,$00,$00,$00
    .byte $00,$00,$00,$00,$00,$00,$00,$00
    .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
    .byte $00,$00,$00,$00,$00,$00,$00,$00
    .res 8192 - 32, $00

That’s the whole point this unit makes: colour and shape are independent. You recoloured the block by editing a table that has nothing to do with the screen’s memory.

Assemble and run

ca65 palette.asm -o palette.o && ld65 -C nes.cfg palette.o -o palette.nes

A red block on a light-blue field. Prove the independence to yourself: the nametable write is unchanged from last unit, yet the colour is completely different.

Try this: recolour without touching the screen

Change the $3F01 value — lda #$16 — to another NES colour and run: $2a green, $30 white, $28 yellow, $12 blue. The block changes colour each time, and you never went near the nametable. Shape stays; colour moves.

Try this: change the world behind it

Now change the $3F00 backdrop — lda #$21 — instead. The whole field changes colour, because every blank cell is colour 0, which is the backdrop. Set it to $0f and the background goes black, throwing the block into sharp relief. One byte repaints everything behind your tiles.

Try this: four palettes, and the catch

The NES has four background palettes ($3F00, $3F04, $3F08, $3F0C), so different parts of the screen can use different four-colour sets. Which palette a cell uses isn’t stored in the cell, though — it’s chosen in coarse 16×16-pixel blocks by a separate attribute table. That’s why neighbouring tiles often have to share colours: a famous NES constraint, and a cousin of the Spectrum’s attribute clash. We lean on just palette 0 in the Primer; the attribute table is a game’s concern.

If it doesn’t work

  • The block is invisible. $3F01 probably matches $3F00 — colour 1 is the same as the backdrop, so the block blends in. Give them different values.
  • The colours look wrong or washed. Check you wrote the four palette bytes in order through $2007 after aiming at $3F00, and that you didn’t swap a digit — NES colour codes run $00$3F.
  • Nothing shows at all. As ever, check the picture is turned on (lda #$1e / sta $2001) at the end.

What you’ve learnt

Colour lives in the palette, separate from the screen. A background palette is four entries from $3F00; a tile’s pixels point at one of them by number, so the same tile draws in whatever colour the palette currently holds. Change the palette and the picture recolours without a single write to the nametable.

What’s next

We keep calling tile 1 “a solid block” — but where does that shape come from? Next — Where the Shapes Come From — we open the last mystery in the harness: the pattern table, the 8×8 shapes the PPU draws from. And you’ll draw your own.