Skip to content
Game 0 Unit 11 of 18 1 hr learning time

A Finger on the Boxes

Stop baking the value into every instruction. Meet indexed addressing — X as a finger you rest on one entry of a table and slide along — the idea behind every lookup, message and animation a game does.

61% of Meet The Machine

Every value you've loaded so far was baked into the instruction: lda #$16, the colour written into the code itself. That's fine for one value. But games are full of lists — a row of colours, the letters of a message, the frames of an animation — and you can't bake a fresh instruction for every item. You keep the list in memory and point at the one you want.

That pointing is indexed addressing. Pick up the X register again — earlier it just held a number; now we use it as a finger resting on one entry of a table:

  • ldx #3 — rest the finger on entry 3 (counting from 0).
  • lda colours,x — load from colours plus X — the entry the finger is on.

The base colours stays put; X is the part that moves. lda colours,x with X at 3 reads the fourth entry. Change X, and the same instruction reaches a different item.

This is the NES programmer's daily bread. On a home computer you might slide a finger along the screen; here the screen fills through the auto-stepping PPU port (you saw it last unit's cousin, and use it next unit), so the finger's real job is reading the data tables a game is built from. Same idea, pointed at your data.

What you'll see by the end

The NES screen filled with mustard yellow.
Yellow because the finger rested on entry 3 of a four-colour table — the same LDA reaches any entry, just by moving X.

A yellow screen — but we never wrote $28 (yellow) anywhere. We wrote a table of four colours and rested the finger on entry 3. Move the finger and the same instruction fetches a different colour.

Resting the finger on an entry

; ============================================================================
; Meet the Machine (NES) - Unit 11: A Finger on the Boxes
;
; Instead of baking a value into the instruction, keep a table of values and
; point a finger at one. X is the finger: lda table,x reads the entry it rests
; on. Here a table of four colours; the finger picks one.
; ============================================================================

.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

    ; ----------------------------------------------------------- YOUR CODE START
    ldx #3                  ; rest the finger on entry 3
    lda colours, x          ; read the colour the finger points at -> $28 yellow
    ; ------------------------------------------------------------- YOUR CODE END

    sta $00
    bit $2002
    lda #$3f
    sta $2006
    lda #$00
    sta $2006
    lda $00
    sta $2007

forever:
    jmp forever

; a table of four colours, sitting in ROM
colours:
    .byte $16, $2a, $12, $28    ; 0:red  1:green  2:blue  3:yellow

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

colours is a table of four bytes sitting in ROM — red, green, blue, yellow. ldx #3 rests the finger on the fourth of them; lda colours,x fetches it into A; and the harness shows it. The value the screen ends up painting was chosen by the index, not written into the load.

Assemble and run

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

A yellow screen — entry 3 of the table, picked by the finger.

Try this: move the finger

Change ldx #3 to ldx #0, #1, #2. The screen comes up red, green, blue, yellow as the finger slides along the table — and you never touched the lda colours,x line. One instruction, four results, because the index moved.

Try this: a longer table

Add two more colours to the table — .byte $16, $2a, $12, $28, $30, $0f (white and black on the end) — then rest the finger on #4 or #5. The table can be as long as you like; the finger reaches any entry by number. This is exactly how a game stores a palette, a tune, or the tiles of a level: a table, and a finger that walks it.

If it doesn't work

  • The colour is wrong or random. Check the finger isn't reaching past the end of the table — ldx #9 on a four-entry table reads whatever bytes happen to follow it. Keep the index inside the table.
  • ca65 errors on lda colours,x. That exact form — load, the label, comma, lowercase x — is right. Check the comma and the x.
  • The screen is black. The colours label and the lda colours,x must spell the table's name identically — a typo means the assembler can't find it.

What you've learnt

X is a movable index: lda table,x reads the entry of table that the finger rests on, and changing X reaches a different entry — so one instruction can read all the way along a table of data. Lists of colours, letters, tiles, tunes: all read this way.

What's next

You moved the finger by hand, one entry at a time. To do something to every entry, you let the machine step the finger for you. Next — The Counted Loop — load a count, run the body, step on, branch back until they're all done. One loop paints a whole row of the screen.