Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.
I am working a quick demo that alters the background tiles. The catch is when i change some of the tiles, the screen does a visible skip, then resettles where it should be. This doesn't ruin the game in anyway, but i would preferably remove this before a final release.
anyways this is the code i am using the update the location of the screen with a dynamically changing string from RAM...
; Wait a vblank
lda VBlank
- cmp VBlank
beq -
;kill ppu
lda #0
sta $2000
sta $2001
;Rewrite the word
lda #$22
sta $2006
lda #$f2
sta $2006
ldy #$00
- lda (ActiveString), y
sta $2007
iny
cpy #$0c ;word length is 12
bne -
;This recenters the screen
lda #$20
sta $2006
lda #$00
sta $2006
;reinitialize ppu
lda #%10001000
sta $2000
lda #%00011110
sta $2001
Any advice would be appreciated, as this is the last bug in my otherwise finished demo.
if i comment out the writes to 2006 to recenter and just clear to scroll, i still get the screen hic-up, but it is less noticable. it is now more of a quick flash, with the location i am writing to at the top of the screen, and then it all centers again.
Thanks for the input. i am still going to see if i can fix it better before releasing it. Is there anything else that i am doing wrong (perhaps expecting a background write to happen smoothly?)
frantik wrote:I'm not sure this will help but maybe you should use a vblank IRQ instead of polling the PPU for vblank?
it seems like you shouldn't see anything if you make all your changes before vblank ends, but i can't say for sure
Unless i am misunderstanding what you are telling me, i believe i am.
I have the NMI set to trigger on VBlank, where it is incremented.
Other then that, i push the registers onto the stack, then run the next note from the embedded NSF file, then pull the registers back off the stack, and return from interrupt.
does that code run during the vblank interrupt (like before the RTI command) or do you just increment the VBlank variable, play the note, and return, and then that code executes?
if the graphics code executes after the note is played i think it would cause problems.. but i think you would see more than a "hickup". so i don't really know.. lol
When I was developing Pegs, I had the same issue. I was changing 12 tiles in the background, and when I did so, I got the hiccup you are talking about. I ended up curing this by writing only four tiles at a time, as well as the using the same code that Celius posted, I believe (check the source, because I honestly do not remember at this point). I'm pretty sure I just polled $2002 each time before writing, which as I understand from other posts isn't the best way, but it worked for me, and I've never experienced any lag on that game when played on the system.
It kind of makes me wonder if there is a time when polling $2002 on the NES is acceptable, but has to be done at a certain point in the cycles. I don't know, I'm just speculating : P
Roth wrote:It kind of makes me wonder if there is a time when polling $2002 on the NES is acceptable
Soon after power-on, in order to make sure the PPU reset finished all the way. The typical sequence is poll $2002, clear CPU RAM, poll $2002, set palette, clear nametables. There's about a 1 out of 21 chance that you'll get lag on any frame; this chance is acceptable in something like power-on. But after that, use NMI.
Well this code is meant to run under certain conditions of an 'A' press.
I don't want to rewrite the background every time the NMI is triggered (or at least the 12 tiles.) So unless i create a state system to handle different types of background writes, then embed that code into the NMI to handle those writes, that wont work.
One of my goals in this demo was to create a simple procedural example of coding on the NES, but this adds needless complication for an inexperienced observer.
If the aforementioned state system is my only option, i will take it, but i would prefer handling it in inline code to make it easier on the newbies that happen to read the source, or use it as a reference.
So Vblank is a variable incremented in the NMI? Exactly what is happening when the NMI is fired?
Oh, and really make sure in your NMI routine you save A, X, and Y. If the A register is destroyed, the desired value won't be loaded into A upon returning.
Here's my suggestion: put "STA $5555" at the very end of the posted code. Then open the ROM in FCEUXD. Go to Tools... Debug... Then under the "break points" box click "add". Then in the first text box next to "address" write $5555 and under it, click "write", then "OK". Then press 'A'.
It will break after that code is done. You can see what scanline it ends at and other information.
And writing less probably isn't the answer. I was able to fit 182 PPU writes into 1 Vblank, so you just have to keep track of how many cycles everything is taking. Make sure it doesn't spill out of Vblank. That's probably what's causing the problem here, actually.
You're supposed to handle music after you handle the graphics. This is because the PSG allows writes at any time, not just during blanking.
If you don't use a register in your NMI handler, you don't need to spend cycles saving and loading it. My NES programs' NMI code typically looks like this:
forever:
jsr read_pads ; read each controller twice and keep readings that agree
jsr game_logic ; apply all game rules
jsr prepare_ppu ; prepare data to be copied to OAM and VRAM
lda retraces
:
cmp retraces ; wait for NMI handler to signal the start of vblank
beq :-
jsr update_ppu ; partly-unrolled copies from CPU RAM to tiles, nametables, palette
jsr PLAY_ADDR
jmp forever
is there a reason it's better to increment a variable inside of vblanks iterrupt routine and then handle the variable change, vs just putting the handling code between the interrupt vector and the RTI?
frantik wrote:is there a reason it's better to increment a variable inside of vblanks iterrupt routine and then handle the variable change, vs just putting the handling code between the interrupt vector and the RTI?
No, except that the pattern of an ISR that just sets a flag might be more familiar to developers on platforms where you don't get to write your own ISRs, such as any multitasking OS. You could put the entire game in the NMI; Super Mario Bros. does just this. Just make sure you do the PPU updates as soon as possible after the start of vblank, which means before you run the sound engine.
Polling the VBL count is simpler to code for, because you don't have to deal with re-entrancy and concurrency issues. If the code is executed from NMI, you need a flag to tell it when it can run, and where it's called from the mainline code is less-clear. Unless you're doing split-screen effects, you don't need code guaranteed to run every NMI, so the polling approach is better.