Introduction The Nintendo Entertainment System (NES) is a classic 8-bit console that has
fascinated hobbyists and game developers for decades. Programming the NES requires a deep
understanding of its architecture, memory-mapped input/output, and graphics pipeline. This
tutorial takes you step by step through writing NES games in 6502 assembly, culminating in the
creation of a basic platformer.
1. Understanding the NES Architecture
● CPU: Ricoh 2A03 (based on the 6502) running at ~1.79 MHz
● PPU: Picture Processing Unit responsible for rendering graphics
● APU: Audio Processing Unit for sound
● Memory:
○ 2 KB internal RAM
○ 2 KB video RAM
○ 16 KB PRG-ROM banks
● Memory Mapping:
○ $0000-$07FF: Internal RAM
○ $2000-$2007: PPU registers
○ $8000-$FFFF: PRG-ROM
2. Hello World in NES Assembly
We'll use the ca65 assembler and NESASM compatible syntax.
Basic ROM Structure:
.segment "HEADER"
.byte 'N', 'E', 'S', $1A ; iNES header
.byte 1 ; 1x 16KB PRG
.byte 1 ; 1x 8KB CHR
.byte 0, 0, 0, 0, 0, 0, 0, 0 ; padding
.segment "STARTUP"
.org $C000
Reset:
SEI ; disable interrupts
CLD ; clear decimal
LDX #$40
STX $4017
LDX #$FF
TXS ; set stack
INX
STX $2000 ; disable NMI
STX $2001 ; disable rendering
Forever:
JMP Forever
.segment "VECTORS"
.word 0, Reset, 0 ; NMI, RESET, IRQ
This is a minimal NES ROM that boots and does nothing.
3. Displaying a Background
NES backgrounds are stored in the Nametable. We write directly to VRAM.
Setting up the PPU:
LDA #$3F ; start of palette
STA $2006
LDA #$00
STA $2006
LDA #$0F ; universal background color
STA $2007
Wait for VBlank:
WaitVBlank:
BIT $2002
BPL WaitVBlank
4. Drawing Sprites
Sprites are defined in Object Attribute Memory (OAM) using $2003/$2004.
Example: A single sprite
LDA #$00
STA $2003
LDA #$80 ; Y position
STA $2004
LDA #$01 ; tile number
STA $2004
LDA #$00 ; attributes
STA $2004
LDA #$80 ; X position
STA $2004
CHR-ROM should include tiles for the sprite, often created using tools like NES Screen Tool or
YY-CHR.
5. Using Palettes
You must write palettes into the PPU's memory space.
Palette:
.byte $0F, $11, $21, $31 ; Background
.byte $0F, $16, $26, $36 ; Sprite 0
To copy to VRAM:
LDA #$3F
STA $2006
LDA #$00
STA $2006
LDY #$00
CopyPal:
LDA Palette,Y
STA $2007
INY
CPY #$08
BNE CopyPal
6. Input Handling
Read input from $4016.
LDA #$01
STA $4016
LDA #$00
STA $4016
LDA $4016
AND #$01 ; A button
7. Building a Simple Platformer
We'll now start with a static screen and a controllable character.
Step 1: Initialize game state
PlayerX: .byte $80
PlayerY: .byte $70
Step 2: Game loop
GameLoop:
JSR WaitVBlank
JSR ReadInput
JSR UpdatePlayer
JSR DrawPlayer
JMP GameLoop
Step 3: Player update and draw
UpdatePlayer:
LDA $4016 ; check right input
AND #$02
BEQ SkipRight
INC PlayerX
SkipRight:
RTS
DrawPlayer:
LDA #$00
STA $2003
LDA PlayerY
STA $2004
LDA #$01
STA $2004
LDA #$00
STA $2004
LDA PlayerX
STA $2004
RTS
8. Expanding the Game
● Add gravity and jumping by adjusting PlayerY.
● Introduce tiles and collision by reading from a level map.
● Create a scrolling effect with $2005/$2006.
9. Tools and Resources
● NES Screen Tool: for editing tiles and nametables
● FCEUX: emulator with debugging tools
● ca65: assembler from cc65 suite
● YY-CHR: CHR-ROM editor
10. Conclusion
Writing NES games in assembly is a rewarding way to learn low-level programming, CPU/PPU
interaction, and game architecture. While challenging, it offers great insights into how early
games were crafted. With time, you can expand your platformer into a full-featured game with
sound, physics, and AI.