When the NES is powered on or reset, the program should do the following within a fixed bank:
JMP ($FFFC)
)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
:
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 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. Often, the 256-byte page $FF00-$FFFF contains the vectors, the start of the init code, and a "trampoline" for jumps from code in one bank to code in another.
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. ; The vblank flag is in an unknown state after reset, ; so it is cleared here to make sure that @vblankwait1 ; does not exit immediately. 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, which matches what (say) a C compiler ; expects for BSS. Conveniently, 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 inx bne @clrmem ; Other things you can do between vblank waits are set up audio ; or set up other mapper registers. @vblankwait2: bit $2002 bpl @vblankwait2
At this point, the program can fill the nametables, fill the pattern tables (if the board uses CHR RAM), fill the palette, and start displaying things.
Notes on the clrmem loop: