Unwanted scrolling after writing to background

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Unwanted scrolling after writing to background

Post by mitch3a »

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):

Code: Select all

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;
	Reset_Scroll();
}
Any help or info would be greatly appreciated :) Thanks in advance!
User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2064
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: Unwanted scrolling after writing to background

Post by FrankenGraphics »

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.”

https://wiki.nesdev.com/w/index.php/PPU_scrolling
User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Re: Unwanted scrolling after writing to background

Post by mitch3a »

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:

Code: Select all

void Reset_Scroll (void) {
	PPU_ADDRESS = 0;
	PPU_ADDRESS = 0;
	SCROLL = 0;
	SCROLL = 0;
}
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Unwanted scrolling after writing to background

Post by dougeff »

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
User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Re: Unwanted scrolling after writing to background

Post by mitch3a »

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.
User avatar
gauauu
Posts: 779
Joined: Sat Jan 09, 2016 9:21 pm
Location: Central Illinois, USA
Contact:

Re: Unwanted scrolling after writing to background

Post by gauauu »

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.
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Unwanted scrolling after writing to background

Post by dougeff »

Not sure what 2000 has to do with it.
From the wiki on Register 2000, the bits are

VPHB SINN

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.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Unwanted scrolling after writing to background

Post by dougeff »

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
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Unwanted scrolling after writing to background

Post by tokumaru »

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.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Unwanted scrolling after writing to background

Post by rainwarrior »

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.
User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Re: Unwanted scrolling after writing to background

Post by mitch3a »

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!
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Unwanted scrolling after writing to background

Post by dougeff »

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.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Unwanted scrolling after writing to background

Post by dougeff »

interface to an assembly library
You can make a macro that has inline assembly.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Re: Unwanted scrolling after writing to background

Post by mitch3a »

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!
User avatar
mitch3a
Posts: 50
Joined: Mon Jan 23, 2017 8:08 pm
Location: Boston, MA
Contact:

Re: Unwanted scrolling after writing to background

Post by mitch3a »

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!
Post Reply