Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.
I've been working on a simple code library to make my life a bit easier, and I was wondering what my code has to do on startup. Currently, my startup routine does this:
CLD won't make a difference. SEI should be safe to omit if you don't use IRQ's anywhere, ever, but I'd probably keep it anyways.
It'd probably be a good idea to init some other registers at the beginning, too. I always write zero to $2000, $2001, and $4015 before doing the vblank waits. Keeping in mind it's the reset routine, it could be from someone hitting the reset button rather than power. Not that 1/30th of a second of glitching would be noticable, but hey.
As to why you have to wait a couple vblanks, it's pretty much because the PPU needs to warm up. After one full frame it's ready to go (so by waiting for 2, I guess it'll wait 1.1 frames at the least). I don't know what it's doing exactly, but a program that I wrote that started writing to VRAM without waiting had totally corrupt results. Just adding the waits fixed it.
Memblers wrote:It'd probably be a good idea to init some other registers at the beginning, too.
My startup code will clear CPU RAM between the vblank waits.
I always write zero to $2000, $2001, and $4015 before doing the vblank waits.
I thought the program wasn't supposed to do any PPU writes until the PPU warmed up. But if your technique works on real hardware, shouldn't lda #0 sta $2001 be enough to hide everything?
tepples wrote:
I thought the program wasn't supposed to do any PPU writes until the PPU warmed up. But if your technique works on real hardware, shouldn't lda #0 sta $2001 be enough to hide everything?
Right, but it's a register, not a VRAM access. Seems to be ok to write registers right away, though I haven't tested that much.
The reason I clear $2000 though is to disable NMIs.
BTW Memblers, are you going to set up a new board so people can start posting routines that work on real hardware (as was discussed on the older boards)?
Hyde wrote:BTW Memblers, are you going to set up a new board so people can start posting routines that work on real hardware (as was discussed on the older boards)?
We might as well do it in the main NESdev forum here, once the code checks out ok would could add it to the site's wiki or something.
abonetochew:
There's a couple things $4017 writes do that I can think of, the big one is the IRQ source select. It can be set to either use internally-generated IRQs, or external (cartridge) ones. The other writable setting is for the 'frame IRQ' interval or whatever it was called. It affects the timing on the sound channels. But I don't remember which bit is which, that's probably the only NES register I don't have memorized actually, heheh. Because I pretty much only write it once in a program, and more often not at all.
Er, not quite. $4017 has only one function - it controls the APU's frame timer. If D7 is set, it runs in a 5-step sequence (48Hz) for pitch bends, timers, etc.; if clear, it runs in a 4-step sequence (60Hz). Clearing D6 causes IRQs to be generated at the end of the 4-step sequence (but not the 5-step one, oddly enough, so setting D6 *or* D7 will turn them off).
[Edit] Whoops, fixed a little bit
Last edited by Quietust on Tue Nov 02, 2004 2:00 pm, edited 1 time in total.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
tepples wrote:Is 'ldx #0 stx $2000 stx $2001' safe before waiting for two vblanks?
It most certainly is; in fact, MANY licensed games do exactly this.
It's definitely something you want to do, since otherwise the PPU will possibly be rendering garbage AND generating NMIs during those two frames.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
BOOTEDSIG0 = $b0
BOOTEDSIG1 = $07
BOOTEDSIG2 = $ed
PPUCTRL = $2000
PPUMASK = $2001
PPUSTATUS = $2002
PPUADDR = $2006
PPUDATA = $2007
SNDCHN = $4015
JOY2 = $4017
nes_start:
sei
cld
;; OMIT: Should reset the mapper here.
;; Disable frame IRQ, picture, and sound, and set up stack pointer
ldx #$40
stx JOY2
ldx #0
stx SNDCHN
stx PPUCTRL
stx PPUMASK
dex
txs
;; OMIT: Should initialize the lockout defeat here on carts that
;; need it controlled in software (e.g. Color Dreams).
;; Wait for the PPU to warm up. Must read two vblanks from $2002
;; before any writes to $2003-$2007 or $4014.
@vbl1:
bit PPUSTATUS
bpl @vbl1
;; While waiting for the PPU to warm up, prepare the CPU.
;; First clear most of CPU RAM.
lda #0
tax
@clrloop:
sta $00,x
sta $100,x
sta $200,x
sta $300,x
sta $400,x
sta $500,x
sta $600,x
inx
bne @clrloop
;; Clear reset-saved CPU RAM
lda #BOOTEDSIG1
ldx #BOOTEDSIG2
ldy #BOOTEDSIG3
cmp $700
bne @clrsave
cpx $701
bne @clrsave
cpy $702
beq @vbl2
@clrsave:
sta $700
stx $701
sty $702
ldx #3
lda #0
@clrsaveloop:
sta $700,x
inx
bne @clrsaveloop
;; Second of two waits for the PPU to warm up
@vbl2:
bit PPUSTATUS
bpl @vbl2
;; Now we're free to write to the PPU, so clear the nametables.
;; Except on 1-screen mapper and 4-screen mappers, writing to
;; $2400-$2BFF should clear everything.
ldx #$24
stx PPUADDR
ldx #$00
stx PPUADDR
lda #' '
@ppuclrloop:
sta PPUDATA
sta PPUDATA
sta PPUDATA
sta PPUDATA
sta PPUDATA
sta PPUDATA
sta PPUDATA
sta PPUDATA
inx
bne @ppuclrloop
;; Current state: palette not written, OAM address set but data not
;; written, blank nametables, CHR RAM data not written (on applicable
;; carts), blank CPU RAM (except $703-$7FF if soft reset), video and
;; sound turned off, SP = $01FF, 4-step APU frame counter w/o IRQ,
;; PPUCTRL = #$00
jmp main
I'd just reccomand to write #$00 to $2000 and $2001 as soon as possible after the reset vector (I does this just after the sei; cld myself) because a NMI can be trigerred between the reset signal and the write to $2000
Also I don't know it it's needed to initialize the CPU ram between the two VBlank waits. I think initialise it before both of them make more sense to me (however it doesn't do any difference).
About the $700-$702 stuff, I think he's just checking for particular value in theese 3 bytes, to check if it's the first boot or not. So, he doesn't clear $700-$7ff if it isn't the first boot. I think only one byte would be enough, if the value checked as as much bits set than clear (so it's the values the RAM has the less chances to take on power up). Typically, $aa or $55 works well (Final Fantasy does a similar thing to determine if it has to show the intro or not, and it uses $4b for this value).