This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Unwanted scrolling after writing to background

Unwanted scrolling after writing to background
by on (#219814)
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):

void remove_from_background(int x, int y) {
   index = y/8;
   //Nametable 0
   PPU_ADDRESS = 0x20 + (index/8);
   PPU_ADDRESS = 32*(index % 4) + (x/8);
   PPU_DATA = 0x01;

Any help or info would be greatly appreciated :) Thanks in advance!
Re: Unwanted scrolling after writing to background
by on (#219816)
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.”
Re: Unwanted scrolling after writing to background
by on (#219817)
I vaguely remembered there was a detail like that, so that's why I added the "Reset_Scroll" call. Here is the full code of that method:

void Reset_Scroll (void) {
   SCROLL = 0;
   SCROLL = 0;
Re: Unwanted scrolling after writing to background
by on (#219819)
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.
Re: Unwanted scrolling after writing to background
by on (#219820)
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.

Also, thanks for all the quick replies. These forums are amazing.
Re: Unwanted scrolling after writing to background
by on (#219821)
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.
Re: Unwanted scrolling after writing to background
by on (#219826)
Not sure what 2000 has to do with it.

From the wiki on Register 2000, the bits are


NMI enable (V), PPU master/slave (P), sprite height (H), background tile select (B), sprite tile select (S), increment mode (I), nametable select (NN)

Writing to the 2 lowest bits changes which nametable is rendered.
Re: Unwanted scrolling after writing to background
by on (#219827)
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.
Re: Unwanted scrolling after writing to background
by on (#219830)
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.
Re: Unwanted scrolling after writing to background
by on (#219832)
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.
Re: Unwanted scrolling after writing to background
by on (#219836)
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.

Thanks again to everyone for their thoughts/advice!
Re: Unwanted scrolling after writing to background
by on (#219839)
I'm suffering from Cargo Cult programming

We both are. Obviously I saw this bit of code somewhere, and copied it without considering it first.
Re: Unwanted scrolling after writing to background
by on (#219848)
interface to an assembly library

You can make a macro that has inline assembly.
Re: Unwanted scrolling after writing to background
by on (#219870)
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!
Re: Unwanted scrolling after writing to background
by on (#221673)
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!
Re: Unwanted scrolling after writing to background
by on (#221676)
mitch3a wrote:
Is this just expected behavior?
You're kinda getting into the esoterica of how scrolling works on the NES. nesdevwiki:PPU scrolling.

But ... no, simply disabling then reenabling rendering wouldn't obviously cause a jump of a single scanline. Re-enabling rendering in the middle of a field should cause it to randomly start rendering with a random weird scroll, but I'd be surprised if it was consistently one scanline.
Re: Unwanted scrolling after writing to background
by on (#221677)
To avoid screen jumps of any kind, the recommendation is to only turn rendering on from the vblank handler, after having set the scroll, so that the first rendered frame starts correctly from the top of the screen and with the correct scroll settings.

If you enable rendering whenever, you'll most likely get a partial picture offset from the top of the screen. Turning rendering of should also be done during vblank, so you don't get a frame with the picture particularly cut off.

The approach many people use is to have the main code simply signal the intent to turn rendering on and off, and the NMI handler is responsible for committing those changes.
Re: Unwanted scrolling after writing to background
by on (#221793)
Thanks for all the help. I DEFINITELY plan on taking a lot of this feedback (especially rainwarrior's post) and building a better framework for my next game. In this case, it works and I don't want to do any major rewriting cos I want to move on, but I'll probably put a big disclaimer in the code and the readme so that others don't fall into the same issues.