Init code: Difference between revisions
(skip $200,x and explain why) |
(caveat about clearing all RAM first) |
||
Line 9: | Line 9: | ||
*If using a mapper that generates IRQs, disable [[APU Frame Counter|APU timer IRQs]] | *If using a mapper that generates IRQs, disable [[APU Frame Counter|APU timer IRQs]] | ||
*Disable [[APU DMC|DMC]] IRQs [http://nesdev.parodius.com/bbs/viewtopic.php?p=22538#22538] | *Disable [[APU DMC|DMC]] IRQs [http://nesdev.parodius.com/bbs/viewtopic.php?p=22538#22538] | ||
*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). | *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). However, you don't need to set up an area of RAM that only one part of a program uses; you can set that in the same part where it's used. | ||
*Wait at least 30,000 cycles (see [[PPU power up state]]) 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. | *Wait at least 30,000 cycles (see [[PPU power up state]]) 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. | ||
Revision as of 18:59, 13 July 2010
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). However, you don't need to set up an area of RAM that only one part of a program uses; you can set that in the same part where it's used.
- Wait at least 30,000 cycles (see PPU power up state) 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 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. ; 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, which matches what (say) a C compiler ; expects for BSS. Conveniently, X is still 0. txa @clrmem: sta $000,x sta $100,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 ; We skipped $200,x on purpose. Usually, RAM page 2 is used for the ; display list to be copied to OAM. OAM needs to be initialized to ; $EF-$FF, not 0, or you'll get a bunch of garbage sprites at (0, 0). 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.