Init code

From NESdev Wiki
Revision as of 17:55, 20 December 2009 by Tepples (talk | contribs) (Because 'sei' is the first instruction, disabling IRQs need not sit in the fixed bank or the replicated portion)
Jump to navigationJump to search

When the NES is powered on or reset, the program should do the following within a fixed bank:

  • Set IRQ ignore bit (not strictly necessary as the 6502 sets this flag on all interrupts, including RESET, but it allows program code to simulate a reset by JMP ($FFFC))
  • Disable PPU NMIs and rendering
  • Initialize stack pointer
  • Initialize the mapper (if any)

The init code after this point may be placed either in the fixed bank or in a separate bank using a bankswitch followed by a JMP:

  • Disable decimal mode (not strictly necessary as the 2A03 has no decimal mode, but it maintains compatibility with generic 6502 debuggers)
  • If using a mapper that generates IRQs, disable APU timer IRQs
  • Disable DMC IRQs [1]
  • Set all RAM that your program uses to a known state. This often involves clearing internal RAM (@ $0000-$07FF) (and PRG RAM if needed (@ $6000-$7FFF)), except that which is intended to survive a reset (such as high scores).
  • Wait at least 30,000 cycles (see Power-up state of PPU) before reading or writing registers $2003 through $2007. This is commonly done by waiting for the PPU to signal the start of vertical blank twice through $2002.

Some mappers have no fixed bank because they switch all 32 KB of PRG at a time. These include AxROM, BxROM, GxROM, and some configurations of Nintendo MMC1. You'll have to put the interrupt vectors and the code up to the end of the JMP in a separate section that is duplicated in each bank.

Sample implementation:

reset:
    sei        ; ignore IRQs
    cld        ; disable decimal mode
    ldx #$40
    stx $4017  ; disable APU frame IRQ
    ldx #$ff
    txs        ; Set up stack
    inx        ; now X = 0
    stx $2000  ; disable NMI
    stx $2001  ; disable rendering
    stx $4010  ; disable DMC IRQs

    ; Optional (omitted):
    ; Set up mapper and jmp to further init code here.

    ; Clear the vblank flag, so we know that we are waiting for the
    ; start of a vertical blank and not powering on with the
    ; vblank flag spuriously set
    bit $2002

    ; First of two waits for vertical blank to make sure that the
    ; PPU has stabilized
@vblankwait1:  
    bit $2002
    bpl @vblankwait1

    ; We now have about 30,000 cycles to burn before the PPU stabilizes.
    ; One thing we can do with this time is put RAM in a known state.
    ; Here we fill it with $00. X is still 0...
    txa
@clrmem:
    sta $000,x
    sta $100,x
    sta $200,x
    sta $300,x
    sta $400,x
    sta $500,x
    sta $600,x
    sta $700,x  ; Remove this if you're storing reset-persistent data
    inx
    bne @clrmem
   
@vblankwait2:
    bit $2002
    bpl @vblankwait2