Skip to content

Reset Sequence

Standard NES initialisation: disable interrupts, wait for PPU, clear RAM. Required boilerplate for every NES program.

Code status: illustrative code. Adapt the listing to your program and check it on your target machine. This page links no assembly or execution record for the listing itself.

Overview

Every NES program needs this exact startup sequence. The reset handler disables interrupts, waits for the PPU to stabilise (two VBlank waits), and clears all RAM. Only after this is it safe to configure the PPU and start your game.

Code

6502 assembly
; =============================================================================
; RESET SEQUENCE - NES
; Standard NES initialisation boilerplate
; Taught: Game 1 (Neon Nexus), Unit 1
; Cost depends on the assembled caller and memory placement.
; =============================================================================

PPUCTRL   = $2000
PPUMASK   = $2001
PPUSTATUS = $2002

.segment "CODE"

reset:
    sei                     ; Disable interrupts
    cld                     ; Clear decimal mode (not used on NES, but safe)

    ldx #$40
    stx $4017               ; Disable APU frame IRQ

    ldx #$FF
    txs                     ; Set up stack at $01FF

    inx                     ; X = 0
    stx PPUCTRL             ; Disable NMI
    stx PPUMASK             ; Disable rendering
    stx $4010               ; Disable DMC IRQs

    bit PPUSTATUS           ; discard a possibly stale power-on status

    ; === First VBlank wait ===
    ; PPU needs time to warm up after power-on
@vblank1:
    bit PPUSTATUS           ; Read status (bit 7 = in VBlank)
    bpl @vblank1            ; Loop until VBlank flag set

    ; === Clear RAM while we wait ===
    ; RAM contents are undefined at power-on
    lda #0
@clear_ram:
    sta $0000, x
    sta $0100, x
    sta $0200, x            ; OAM buffer
    sta $0300, x
    sta $0400, x
    sta $0500, x
    sta $0600, x
    sta $0700, x
    inx
    bne @clear_ram          ; Loop 256 times (X wraps from $FF to $00)

    ; === Hide all sprites in OAM buffer ===
    ; RAM clear set every byte to 0, which puts all 64 sprites at Y=0
    ; (top-left of screen). Write $FF to every Y position to hide them.
    ldx #0
    lda #$FF
@hide_oam:
    sta $0200,x         ; Y position byte for each sprite
    inx
    inx
    inx
    inx                 ; Stride 4 — only the Y byte
    bne @hide_oam

    ; === Second VBlank wait ===
    ; PPU is now fully ready
@vblank2:
    bit PPUSTATUS
    bpl @vblank2

    ; === PPU is ready - continue with game setup ===
    ; Load palettes, set up sprites, etc.
    jsr setup_game

    ; Enable NMI and rendering
    lda #%10001000          ; NMI enable, sprites from pattern table 1
    sta PPUCTRL
    lda #%00011110          ; Show sprites and background
    sta PPUMASK

    ; Fall through to main loop
main_loop:
    jmp main_loop           ; NMI does the work

setup_game:
    ; Your initialisation code here
    rts

Trade-offs

Aspect Cost
CPU Not measured for this illustrative listing; include the caller and hardware scheduling costs
Memory Assemble the selected routines and caller to obtain code and state sizes
Limitation None - this is mandatory

When to use: Every NES program. Copy this exactly.

When to avoid: Never - you always need this.

Why Two VBlank Waits?

The PPU takes time to stabilise after power-on:

  1. First wait: PPU internal state is settling
  2. Clear RAM: Use this time productively
  3. Hide OAM sprites: Cleared RAM means all 64 sprites have Y=0 — they’d pile up at the top of the screen as soon as rendering enables. Writing $FF to every Y position pushes them off-screen.
  4. Second wait: PPU is now fully ready

Skipping the OAM hide step is a famous beginner footgun: rendering enables, and a column of garbage sprites appears at the top of the screen for one frame until the NMI handler updates them.

Skipping the VBlank waits entirely can cause graphics glitches or crashes on real hardware.

What Each Step Does

Step Purpose
sei Prevent interrupts during setup
cld Clear decimal flag (6502 habit, harmless)
stx $4017 Disable APU frame counter IRQ
txs Set stack pointer to $01FF
stx PPUCTRL Disable NMI until ready
stx PPUMASK Disable rendering until ready
stx $4010 Disable DMC channel IRQ
VBlank waits Let PPU stabilise
Clear RAM Initialise all variables to 0

Vector Table

Don’t forget to set up your vectors:

6502 assembly
.segment "VECTORS"
    .word nmi               ; $FFFA - NMI vector
    .word reset             ; $FFFC - Reset vector
    .word irq               ; $FFFE - IRQ vector

Patterns: NMI Game Loop, Palette Loading

Vault: NES

Supply real NMI/IRQ handlers and complete display initialisation before enabling rendering. The reset entry can clear the stack page because it has not called a subroutine yet; do not call this RAM-clear block with JSR. OAM DMA is a separate step, and a stock power-on path and a software reset do not imply identical APU state.