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:
Code:
sei ;disable interrupts (just IRQ/BRK?)
cld ;decimal mode off (pointless?)
ldx #$ff ;initialize stack
txs
jsr vbl ;wait for vblank twice (why?)
jsr vbl
lda #$40
sta $4017 ;disable some kind of IRQ?
Am I missing anything? Is all of this required?
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.
Quote:
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)?
What exactly does the write to $4017 do?
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
Aha. I can't remember where I got that idea from. Thanks for the explanation.
Probably because the APU frame IRQs made it appear as an "IRQ source select".
Can anybody with dev hardware confirm an answer to the following question:
Is 'ldx #0 stx $2000 stx $2001' safe before waiting for two vblanks?
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.
So does the following init code (not tested) skip anything that the real NES requires?
Code:
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
Looks like it has everything. Pretty fast, too. Be sure the OAM is cleared before the screen is turned on, also.
what's the bootsig $700-$702 stuff about (sorry n00b question)?
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).
Bregalad wrote:
Also I don't know it it's needed to initialize the CPU ram between the two VBlank waits.
It's not. A lot of official games do the vblank waits one after another. It's just that the time between the vblanks is just a very convenient time to clear CPU RAM, as what else do you have to do during nearly 30,000 CPU cycles between vblank #1 and vblank #2?
Quote:
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.
Correct.
I read somewhere that initializing the stack is quite pointless too... the explanation said that the SP value didn't matter at all, as the stack page is fixed and it warps around once we get to the end (or gegining) of the page...
So, you'll have your 256 stack bytes, no matter where the stack pointer points too in the begining of the program...
I don't know if this is true, but makes sense to me...
If you're short on memory and know the maximum amount of stack space you're going to use, you can allocate variables within the empty space at the bottom of the stack.
Plus if your stack is split on the page boundary, you'll have problems with reading pushed values:
Code:
TSX
LDA $103,X ; read 3rd-last pushed value -- will not work if SP wrapped
Ok, you might have problems if you are going to do direct manipulations to the stack. But if you just use push an pull you're safe, right?
I can understand why one might want to have the stack aligned to the memory page, but it's not mandatory to do so...
Right, it's not mandatory. But considering that programs I've seen rarely go more than 16 bytes deep or so, the whole page is pretty much going to waste. And that's nearly 1/8th of the CPU's RAM.
I just noticed, using FCEUltra, that Megaman and Megaman 2 doesn't write anything to $4017, but thoose two DOES use the triangle timer, wich is, I belive, based on the 4 steps or 5 steps sequences.
On reset and power-up, $00 is internally written to $4017 before the CPU starts execution. This begins the 4-step sequence and enables the APU's frame IRQ flag (but the I flag is set so it won't occur until a CLI or equivalent).