I'm working on a game and had been using sprites for certain objects, but because they don't move very often and because I can have far more than 8 per line, I wanted to store them in the background while they're not active. I wrote the code to do so, which does write the correct sprite to the correct background tile, however, every time I do, the screen will briefly scroll for what seems to be a frame before it will reset back to where it should be a ~frame later (haven't actually confirmed its exactly one frame, but I think scroll gets reset every frame with the code I'm working off of). I tried resetting the scroll in the same cycle manually, but it didn't fix the issue. It seems to mostly scroll vertically, but occasionally it's been diagonally.

I'm assuming there's something quirky about writing to the PPU_ADDRESS that I'm missing OR I'm writing to it at the wrong time? Its being written during NMI. I'm using nesdoug's tutorials/example code as a baseline, so part of it is extracted here.

Code snippet below (for all the code and this exact snippet, its here):

I’m at work and can’t read the code atm, but it sounds like you need to do your updates prior to setting the scroll value, preferrably as part of the nmi routine. You can set some variable during your game logic to let the nmi routine know that this update is pending.

edit: citing the nesdev wiki from under ”common pitfalls”

”PPUSCROLL must always be set after using PPUADDR ($2006). They have a shared internal register and using PPUADDR will overwrite the scroll position.”

I really should change my tutorial. This is a bit 'cargo cult' programming. Changing the PPU Address isn't necessary to set the scroll. Properly, the nametable selection should be a write to 2000 and then only 2 writes to 2005 (scroll) are required.

_________________nesdoug.com -- blog/tutorial on programming for the NES

TIL the term "cargo cult" To be fair, even when doing a project from assembly from scratch, this was a part I definitely saw many issues with and were very difficult to debug.

I'm still confused though. My intention is not to scroll the screen. My intention is to write to one specific background tile, but I'm seeing undesired scrolling afterwards. Even if I just replace "Reset_Scroll" with the two calls to set the X/Y scroll ("SCROLL = 0"), I still see the undesired scrolling. Not sure what 2000 has to do with it.

I'm no expert, but It sounds like one of a couple things might be happening:

1. You're not actually writing the bg tiles during vblank, even though you think you are.2. Somewhere else you're writing to the address register ($2006) without a final write to the scroll register.

I'd pull up a debugger, set a breakpoint on your code in question, and see if it's running during vblank like you expect. (Mesen has a PPU Status panel that indicates whether you're currently in vblanking or not) Step through and see if you're writing to the address register ($2006) outside of vblank, and make sure that any writes to the address register are followed up by setting the scroll before vblank ends.

By the way. Writes to the PPU must happen during v-blank or with rendering off (2001 register). And, Y scroll can't (normally) be set during rendering, and should also be done during v-blank or with rendering off.

(there's a ton of exceptions and complicated tricks, but for simple use, this is generally good advice).

Misaligned scroll can result from poorly timed writes.

_________________nesdoug.com -- blog/tutorial on programming for the NES

The PPU has an address register that it uses to access VRAM, be it when the programmer is reading/writing from/to VRAM or when the PPU itself is reading it for the purpose of rendering a picture to the TV. This means that these things are always connected and can't happen simultaneously, which is why VRAM can only be changed during vblank, a time when the PPU is NOT accessing VRAM.

Setting the scroll is essentially pointing the address register to the NT location that's supposed to appear at the top left corner of the screen (using the more convenient interfere that's register $2005), and if you touch the address register after doing that, you mess up that setting and the scroll will not be what you expect. If you're still getting unintentional scrolling after resetting the scroll in your vblank handler, you might be changing the VRAM address outside of vblank, which doesn't work the same way and should only be done in case of raster effects.

Debug your program in an emulator like Mesen or FCEUX to make sure that all PPU accesses are indeed taking place during vblank, and that the scroll is being reset properly afterward (1 write to $2000 to select the name table, 2 writes to $2005 to set the scroll within that name table), still during vblank time.

I would suggest a few things, starting with a few rules of thumb: never manipulate registers directly in C, and never write timing-sensitive code in C.

So, this recommendation is to do all register interactions in assembly. Don't assign to them directly in C. Yes it technically works sometimes, but not always, and there's a lot of ways it can fail.

Wrap those interactions up in an interface to an assembly library, something like set_ppu_address(0x2110) instead of two assignments to the $2006 register, ppu_write(0x50) or ppu_write_block(buffer, 32) instead of direct $2007 manipulation, etc.

Finally, have an interface for setting the scroll like set_scroll(258,0), but don't have that function immediately write to $2005; keep track of the desired scroll, and use those variables later in your (assembly) code that turns on rendering ($2001) immediately beforehand. There's no need to write the scroll registers at any time earlier than this point, and the further you move it away from that, the more chances there are to accidentally stick something conflicting in between.

Turning on rendering needs to be done with precise timing, specifically within vblank. If you do it anywhere else, you'll get one frame of garbage before it resynchronizes itself. This should be timed using the NMI handler. If you make your library handle the scroll registers at that time, there's no possibility that it well be mistimed or corrupted by accidental intervening code.

From the C side, I'd create a render_on() interface that waits for the next vblank, at which point it sets up the scroll registers and turns rendering on, before returning control back to your C program. This would also be paired with a render_off() that waits for vblank to disable rendering (so you don't get a half-blanked frame) before you start using the set_ppu_address/ppu_write interface suggested above. Even when writing directly in assembly, while I would definitely just write STA $2007 for efficiency, I would still put my timed rendering code in common subroutines like render_on/render_off and call those, to keep the number of places in my code that have to do critically timed accesses to a minimum.

I REALLY like this idea to remove register interactions from the C code. Even without the potential cc65 undesired affects in translating to assembly, I do find it much more confusing having hardware specific code/values in C. And even better, as Doug pointed out, I'm suffering from Cargo Cult programming and this would be a great way to take his example and build my own solution from both sides that I can own and understand fully. I'll give it a try when I have some free time and see if it fixes my issue.

So I didn't have time to fix it the best way yet, but confirmed that some of your suggestions do fix the issue. To prove it, I stole a page out of doug's example code and just kept 2 vars for the memory location of the background tile in the assembly code that I can set from the C code and the assembly will write to the background every frame. So I'll obviously want to make sure this is only when I need it, but it at least proved that you folks were right. Thanks again!

I also encountered a second glitch that I was able to "fix" but wanted to double check I understood. Basically every NMI I set the vertical scroll value to 0xff (to account for sprites being 1 pixel off what you'd "expect") in the assembly, but also had a "reset_scroll" method that I call every time I turn the PPU off then on again. That method was resetting vert scroll to 0x00, but even if I removed that method and relied on my nmi assembly code to set the scroll with the 1 pixel offset, every time I turned the PPU off then back on, there'd be a frame where the scroll would go back to 0x00 . Is this just expected behavior? That would account for why the code I had stolen as a starting point had a reset scroll call everytime the ppu was turned off then on. Just was hoping for a confirmation and any other relevant related info. Thanks!

Who is online

You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot post attachments in this forum