I was debugging some code that is supposed to load tiles into a PPU nametable when I noticed something weird on the debugger. The video below shows it.
https://www.youtube.com/watch?v=yxfQaq5olgA
Basically, the VRAM address keeps changing even when I'm not making any reference to it. Also, when I send the nametable address I want to store the tiles at (using PPUADDR), the address changes right after the instructions. And when I store a tile in the PPU, the address goes up by like $1000. Is that normal? If not, what could I have done to cause such behavior?
VRAM question
Moderator: Moderators
- Controllerhead
- Posts: 314
- Joined: Tue Nov 13, 2018 4:58 am
- Location: $4016
- Contact:
Re: VRAM question
It's drawing the screen =p
Turn off sprite and background rendering bits in PPU_MASK to keep it still.
Re: VRAM question
Oh >_< Of course, that makes sense. This code isn't executed during NMI. I interrupted NMI interrupts before executing this code because I don't want the screen to be drawn while it's running. But I guess it's not enough to make the screen not be drawn. Is there any way to make it happen? So, basically what I want to do is:
- Disable NMI and wait until there's no drawing (so vram isn't updated)
- Draw all bg stuff (I already have a code that makes this happen)
- Re-enable NMI
I can enable and disable NMI using bit 7 on $2000, but how do I wait until there's no more drawing? Will this wait for vblank routine work even with disabled NMI?
- Disable NMI and wait until there's no drawing (so vram isn't updated)
- Draw all bg stuff (I already have a code that makes this happen)
- Re-enable NMI
I can enable and disable NMI using bit 7 on $2000, but how do I wait until there's no more drawing? Will this wait for vblank routine work even with disabled NMI?
Code: Select all
WAITFORVBLANK: ; subroutine that waits for vblank. Wait until we're ready for the next screen redraw
BIT PPUSTATUS
BPL WAITFORVBLANK
RTS
- Controllerhead
- Posts: 314
- Joined: Tue Nov 13, 2018 4:58 am
- Location: $4016
- Contact:
Re: VRAM question
Disabling the NMI bit in $2000 won't stop the screen from drawing: It will stop the code from jumping to the NMI vector when vBlank starts at the beginning of scanline 241 (or later possibly). It's still a good idea, but, it's not enough.
PPU_MASK is what you're looking for: Store 0 in bits 4 and 5 of $2001: #%xxx00xxx
https://wiki.nesdev.com/w/index.php/PPU ... _.3E_write
You may also want to read PPU_STATUS ($2002) before re-enabling drawing and NMI: This will clear the "vBlank is happening status bit" and stop your code from jumping away if the middle of vBlank is happening: Depending on how your code is structured, or how far you are into vBlank, this could cause problems.
PPU_MASK is what you're looking for: Store 0 in bits 4 and 5 of $2001: #%xxx00xxx
https://wiki.nesdev.com/w/index.php/PPU ... _.3E_write
You may also want to read PPU_STATUS ($2002) before re-enabling drawing and NMI: This will clear the "vBlank is happening status bit" and stop your code from jumping away if the middle of vBlank is happening: Depending on how your code is structured, or how far you are into vBlank, this could cause problems.
Re: VRAM question
Rather than disable NMIs, many programmers prefer to make VRAM, OAM and scroll updates conditional in the NMI handler and keep NMIs always on. The most obvious benefit of doing this is that you don't stop the other tasks that also run in the NMI handler, such as the audio driver, meaning that you can keep the music going even during screen transitions or other big VRAM updates if you wish.
Another benefit of keeping NMIs always on is that you can have a spot you NMI handler that takes care of enabling and disabling rendering always in vblank (during the main loop you update the copy of PPUMASK, and the NMI handler writes that to the actual register during vblank), avoiding screen jumps and explicit waits for vblank.
The final reason for keeping NMIs always on is that you get to easily avoid some hardware quirks, such as the occasional scroll glitches caused by mid-screen PPUCTRL or the unwanted NMIs fired by enabling NMIs during vblank. There are workarounds for these, but you can simply avoid these problems altogether by keeping NMIs always on.
I've been doing this for a long time now: I have a copy of PPUMASK in RAM that my NMI handler copies to the actual PPUMASK (this doesn't disrupt any VRAM operations that might be happening in the main thread), and then I check the rendering bits - if both are off, I simply skip all PPU operations in the NMI handler.
Another benefit of keeping NMIs always on is that you can have a spot you NMI handler that takes care of enabling and disabling rendering always in vblank (during the main loop you update the copy of PPUMASK, and the NMI handler writes that to the actual register during vblank), avoiding screen jumps and explicit waits for vblank.
The final reason for keeping NMIs always on is that you get to easily avoid some hardware quirks, such as the occasional scroll glitches caused by mid-screen PPUCTRL or the unwanted NMIs fired by enabling NMIs during vblank. There are workarounds for these, but you can simply avoid these problems altogether by keeping NMIs always on.
I've been doing this for a long time now: I have a copy of PPUMASK in RAM that my NMI handler copies to the actual PPUMASK (this doesn't disrupt any VRAM operations that might be happening in the main thread), and then I check the rendering bits - if both are off, I simply skip all PPU operations in the NMI handler.
Re: VRAM question
Ah, thanks for the information. I didn't know disabling the NMI would have that many side effects. Your idea of keeping the ppumask bits in RAM is similar to what I've been doing with ppuctrl, so I guess I'll do that too!
Re: VRAM question
It's not as bad as it seems, after all there are workarounds for all of the drawbacks. I do think that keeping NMIs always on is a much cleaner approach, though.
Yeah, it's very common to buffer this pair of registers, since most of the time you only want them to change between frames, during vblank.Your idea of keeping the ppumask bits in RAM is similar to what I've been doing with ppuctrl, so I guess I'll do that too!