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

Assemble and Run

Write a little assembly, turn it into a cartridge with two build tools, run it on the NES, and watch the screen fill with red. Meet the build loop you'll use all course — and the harness a console needs before it shows a single pixel.

6% of Meet The Machine

New to programming entirely? You'll have a smoother time starting with General Programming first — this track assumes you've already met variables, loops and subroutines somewhere. If you have, in any language, you're in the right place.

This is the Primer — Meet the Machine. Its whole job is to make the Nintendo Entertainment System stop feeling like magic, one small idea at a time. No game yet, no sprites, no scrolling, no clever code. Just you and the machine, getting introduced.

If you've come from our Commodore 64 Primer, some of this will feel familiar — the NES runs the same 6502 family of chip, so the thinking carries straight over. What's different is the machine around the chip. The C64 boots into BASIC and hands you a screen you can write to directly. The NES boots into nothing, and its screen is asleep until you wake it. That difference is the whole story of this first unit.

And the first idea is the smallest one there is: there's a build step now — two of them. A tool called an assembler turns your text into machine code, and a second tool called a linker arranges that code into a cartridge image the console can run. Write, assemble, link, run. That's the loop you'll repeat for the rest of the course. This unit is just that loop, done once, with the simplest visible result the NES can give.

What you'll see by the end

The NES screen filled edge to edge with solid red.
A red screen, the instant the program ran — you wrote it, built it, and the machine did exactly what you said.

A red screen. That's it — and that's the win. You wrote it, you built it, the machine did it. The moment the cartridge runs, the whole screen turns red.

The harness, and the one line that's yours

On the C64, turning the screen red took two instructions: load a number, store it at the border address. The NES asks for more — not because it's harder to think about, but because it gives you nothing for free.

There's no BASIC underneath to launch your code, so the cartridge has to begin with a small header that says what kind of cartridge it is. And the chip that draws the screen — a separate processor called the PPU (Picture Processing Unit) — isn't awake yet when the console powers on; you have to wait for it before you can tell it anything. None of that is the idea you're here to learn today. It's ceremony: the same every time.

So we wrap all of it in a harness — a block you type once and treat as a sealed box. It does the set-up, then it does one helpful thing: whatever colour you leave in register A, the harness puts on the screen. Later units pry the box open, one panel at a time — the part that paints the screen is Unit 5, and A itself is the next unit. For now, your whole job is the single line in the middle.

The program

This is the whole thing. Almost all of it is the harness; the one line between the YOUR CODE markers is yours:

; ============================================================================
; Meet the Machine (NES) - Unit 1: Assemble and Run
;
; Everything outside the YOUR CODE block is the harness: the ceremony the NES
; needs before it will show a single pixel. You type it once and treat it as a
; black box. Later units open it up - the part that puts your colour on screen
; is Unit 5. For now, leave the colour you want in A, and the harness shows it.
; ============================================================================

; --- iNES header: tells the emulator what kind of cartridge this is ----------
.segment "HEADER"
    .byte "NES", $1a        ; magic number
    .byte 2                 ; 2 x 16 KB PRG ROM (the program)
    .byte 1                 ; 1 x 8 KB CHR ROM (the shapes)
    .byte $00, $00          ; mapper 0 (NROM), horizontal mirroring

; --- The program -------------------------------------------------------------
.segment "CODE"

reset:
    sei                     ; ignore interrupts for now
    cld                     ; the NES 6502 has no decimal mode
    ldx #$40
    stx $4017               ; quieten the APU's frame interrupt
    ldx #$ff
    txs                     ; set the stack pointer
    inx                     ; x is now 0
    stx $2000               ; PPUCTRL = 0  -> NMI off
    stx $2001               ; PPUMASK = 0  -> rendering off
    stx $4010               ; DMC interrupt off

    ; The PPU is not ready for about two frames after power-on. Wait for two
    ; VBlanks before we touch it. (We meet VBlank properly later in the Primer.)
warm1:
    bit $2002
    bpl warm1
warm2:
    bit $2002
    bpl warm2

    ; ----------------------------------------------------------- YOUR CODE START
    lda #$16                ; $16 = red. Leave the colour you want in A.
    ; ------------------------------------------------------------- YOUR CODE END

    ; --- harness: show the colour now in A as the screen's backdrop ----------
    sta $00                 ; stash the colour in a scratch box
    bit $2002               ; reset the PPU's address latch
    lda #$3f
    sta $2006               ; aim the PPU at palette address $3F00...
    lda #$00
    sta $2006
    lda $00                 ; fetch the colour back
    sta $2007               ; ...and pour it in - the screen takes the colour

forever:
    jmp forever             ; hold the picture (the 6502 never stops on its own)

nmi:
    rti
irq:
    rti

; --- Interrupt vectors: where the CPU looks on reset and interrupt ------------
.segment "VECTORS"
    .word nmi               ; $FFFA
    .word reset             ; $FFFC
    .word irq               ; $FFFE

; --- CHR ROM: the shapes the PPU can draw (we meet this later) ----------------
.segment "CHARS"
    ; Tile 0: blank
    .byte $00,$00,$00,$00,$00,$00,$00,$00
    .byte $00,$00,$00,$00,$00,$00,$00,$00
    ; Tile 1: a solid block
    .byte $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
    .byte $00,$00,$00,$00,$00,$00,$00,$00
    .res 8192 - 32, $00     ; fill the rest of CHR ROM with blank tiles

Look only at the YOUR CODE block:

lda #$16        ; $16 = red. Leave the colour you want in A.

lda #$16 puts the number $16 — the NES's code for red — into register A. That's it. The harness below it takes whatever's in A and pours it onto the screen. You don't need to understand how yet; that's the point of a harness. ($16 is written in hex — the $ just means "this number's in base 16"; we meet hex properly in Unit 3.)

Everything else is set-up you'll meet in its own good time. The forever: jmp forever at the end holds the picture still: it jumps to itself, endlessly. Without it the CPU would run on past our code into whatever bytes came next and do something we never asked for. (The 6502 has no "stop" — it always runs on. We'll meet that idea properly at the end of the Primer.)

One thing for when you type it in: the assembler doesn't care about capitals in instruction names — lda and LDA are the same. We write instructions in lowercase all course, so pick the habit up now.

From this unit's directory, two commands turn the source into a cartridge:

ca65 screen.asm -o screen.o
ld65 -C nes.cfg screen.o -o screen.nes

The first, ca65, is the assembler: it turns screen.asm (your text) into screen.o (machine code, not yet placed in memory). The second, ld65, is the linker: guided by the layout file nes.cfg, it decides where each piece goes inside the cartridge and writes the finished screen.nes. That layout file is part of the harness too — it's the same for every program in this course, so you copy it once and forget it.

Load screen.nes in your emulator and the screen is red the moment it starts. You just did the whole cycle: write → assemble → link → run. Everything else in this course is that loop, again and again, with more interesting things in the middle.

Try this: a different colour

Change the $16 in lda #$16 to another NES colour, then assemble, link, and run again. The NES doesn't have sixteen named colours like the C64 — it has a fixed palette of 64 entries chosen by number. A few safe ones to try: $0f black, $30 white, $11 blue, $2a green, $28 yellow, $24 pink, $00 grey. Predict the colour before you run it, then check. (This is the loop again — change, assemble, link, run — and it's already becoming second nature.)

Try this: shorten the build

Typing two commands every time gets old fast. Most shells let you chain them so the link only runs if the assemble succeeded:

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

The && means "and only if that worked." It's the same two steps — you've just taught your shell to do the loop in one line. Nothing about the program changed; you made the tooling quicker, which is its own kind of progress.

If it doesn't work

  • ca65 reports an error and writes no .o. Read the line number it gives — usually a mistyped instruction or a stray character. Fix that one line and assemble again; the rest of the file is fine.
  • ld65 complains it can't find a segment, or nes.cfg. Run it from this unit's directory so it can see nes.cfg, and check the .segment names in the source match the listing exactly — the linker pairs them up by name.
  • The screen is black, not red. The colour didn't reach A. Check the one line is lda #$16 — with the # (a number, not an address) and the $ (hex). Leave the harness around it exactly as shown.
  • The emulator shows garbage or resets in a loop. Usually the forever: jmp forever is missing or mistyped, so the CPU ran off the end — put it back exactly as shown. If it never starts at all, the VECTORS block at the bottom is what tells the machine where your code begins; it must match the listing.

What you've learnt

Writing NES assembly means write → assemble → link → run — two build tools between your text and a running cartridge, and you've now been all the way round the loop once. You've also met the shape of the machine: a console gives you nothing for free, so a fixed harness handles the ceremony and shows whatever colour you leave in register A. Today you left red there.

What's next

You used register A without stopping to meet it. Next — LDA Is Not LET — we slow down and look at what a register is: not an unlimited pool of named variables like BASIC's LET, but a handful of physical, finite, 8-bit stores inside the chip. It's the first place the machine stops behaving like the language you came from — and it's identical to the C64's, because underneath, it's the same 6502.