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

Address register clearing multiple times with sprite0 split?

Address register clearing multiple times with sprite0 split?
by on (#157439)
According to the Nerdy Nights tutorial, before the scrolling is set, the following has to be done:
Code:
   ; The PPU address register is cleared.
   LDA #$00
   STA $2006
   STA $2006


Then you set the scrolling for the status bar:
Code:
   LDA #$00
   STA $2005
   STA $2005


Then comes the whole "wait for sprite 0" stuff.
And then the actual scrolling value:
Code:
   LDA scrollingPosition
   STA $2005 ; Horizontal
   LDA #$00
   STA $2005 ; Vertical


My question: Does the PPU address register clearing have to be done before each change of the scrolling position (i.e. before setting the value to 0 for the status bar and before setting the value for the actual action scene) or is this done only once per frame?

So, is it
1. Clear PPU address register
2. Set scrolling to 0
3. Wait for sprite 0
4. Clear PPU address register
5. Set scrolling to value
?

Or is it
1. Clear PPU address register
2. Set scrolling to 0
3. Wait for sprite 0
4. Set scrolling to value
?
Re: Address register clearing multiple times with sprite0 sp
by on (#157441)
"PPU address clearing" doesn't have to be done at all.

Setting the scroll at the start of the frame should be done with two writes to $2005 to set the scroll position and one write to $2000 to set the starting nametable.

Setting the scroll mid-frame, such as with a scroll split or turning the display on late, should be done with the $2006-$2005-$2005-$2006 sequence where the last two writes occur during horizontal blanking.
Re: Address register clearing multiple times with sprite0 sp
by on (#157443)
DRW wrote:
My question: Does the PPU address register clearing have to be done before each change of the scrolling position (i.e. before setting the value to 0 for the status bar and before setting the value for the actual action scene)

Definitely not. Like tepples said, it doesn't have to be done at all, but it's harmless if done during vblank. It's not harmless if done mid-frame though, so don't do it.

Those $2006 writes you have there are the wrong way (that still works) of selecting a name table for display. The correct way would be to use the lower bits of $2000. If the status bar is in a different name table than the part of playfield that appears on the left of the screen, you also have to modify $2000 when you modify $2005.

tepples wrote:
Setting the scroll mid-frame, such as with a scroll split or turning the display on late, should be done with the $2006-$2005-$2005-$2006 sequence where the last two writes occur during horizontal blanking.

But that's only if you need to set the vertical scroll as well. If all you need is a status bar for a game that doesn't scroll vertically, you can get away with the regular $2000/$2005 mid-screen writes.
Re: Address register clearing multiple times with sprite0 sp
by on (#157447)
tepples wrote:
"PPU address clearing" doesn't have to be done at all.

Then what is it good for? Why should anyone ever include it here?

tepples wrote:
Setting the scroll at the start of the frame should be done with two writes to $2005 to set the scroll position and one write to $2000 to set the starting nametable.

O.k., I omitted the nametable in my example, but it's in my code.

tepples wrote:
Setting the scroll mid-frame, such as with a scroll split or turning the display on late, should be done with the $2006-$2005-$2005-$2006 sequence where the last two writes occur during horizontal blanking.

I didn't understand that.

Here's the way I would do scrolling with a status bar:
Code:
   ; Not necessary?
   ;LDA #$00
   ;STA $2006
   ;STA $2006

   ; Status bar scrolling
   ; A is still at $00, so no new LDA necessary.
   ; LDA #$00
   STA $2005
   STA $2005

   ; The status bar appears in nametable 0, so we set the default value.
   LDA #PpuCtrlDefault
   STA $2000

@waitForNotSprite0:
   LDA $2002
   AND #%01000000
   BNE @waitForNotSprite0

@waitForSprite0:
   LDA $2002
   AND #%01000000
   BEQ @waitForSprite0

   ; Actual scrolling
   LDA scrollingPosition
   STA $2005
   LDA #$00
   STA $2005

   ; Actual nametable
   LDA #PpuCtrlDefault
   ORA nameTable
   STA $2000


So, do you mean that the second scrolling should be done like this?
Code:
   LDA #$00
   STA $2006

   LDA scrollingPosition
   STA $2005
   LDA #$00
   STA $2005

   ; A is still $00, but otherwise we would set it.
   ; LDA #$00
   STA $2006

   LDA #PpuCtrlDefault
   ORA nameTable
   STA $2000

Also, how do I check for horizontal blank? And why should I do that? Is it really just for vertical scrolling and I can omit it if I just have horizontal?

For example, in my parallax scrolling, I consciously placed the sprite 0 (which consists of only one pixel) right at the left side of the screen. And the whole line where this pixel is set consist of one continuous color. So that I can change the scrolling when the rendering is right in the middle of the line and there's still no graphical glitches since that one line has only one color.


tokumaru wrote:
Those $2006 writes you have there are the wrong way (that still works) of selecting a name table for display. The correct way would be to use the lower bits of $2000. If the status bar is in a different name table than the part of playfield that appears on the left of the screen, you also have to modify $2000 when you modify $2005.

Yeah, I forgot the $2000 stuff in my example, but it's in my actual code. So, is the above example correct so far?

tokumaru wrote:
But that's only if you need to set the vertical scroll as well. If all you need is a status bar for a game that doesn't scroll vertically, you can get away with the regular $2000/$2005 mid-screen writes.

My game only scrolls horizontally. It has a status bar split used with the nine sprites overflow. (Which I omitted here for simplicity.) In it, I left the row below the status bar and above the highest background object intentionally blank, so that I don't have to fiddle with wait loops etc. and the split is guaranteed to happen without graphical glitches.
And in the parallax scrolling part which is done with sprite 0, there is one single pixel line that's always guaranteed to have only one color from left to right. That's where the one pixel sprite 0 is placed. So, here again, the split can happen anywhere without glitches.
Re: Address register clearing multiple times with sprite0 sp
by on (#157448)
PPU address clearing is just another way of scrolling to 0,0 on nametable 0. The only part that isn't updated is 'fine x' scroll.
Re: Address register clearing multiple times with sprite0 sp
by on (#157449)
Doesn't that mean that
Code:
   LDA #$00
   STA $2006
   STA $2006
is preferrable to
Code:
   LDA #$00
   STA $2005
   STA $2005

   LDA #PpuCtrlDefault
   STA $2000
for the status bar scrolling position since it includes less code?
Re: Address register clearing multiple times with sprite0 sp
by on (#157451)
Writing to JUST $2006 doesn't allow you to change the fine X scroll, and it can only specify values whether the fine Y ranges from 0 to 3 (instead of 0 to 7)...

So in the very limited case of "just scrolling to the upper left corner if you haven't ever changed the fine X scroll", sure, that's better.
Re: Address register clearing multiple times with sprite0 sp
by on (#157453)
If I understand correctly, fine X is the position in pixels between two full tiles, right?
Re: Address register clearing multiple times with sprite0 sp
by on (#157455)
Yes.
Re: Address register clearing multiple times with sprite0 sp
by on (#157464)
I'm under the impression that clearing the PPU address register was something that people started doing out of desperation when they saw their backgrounds were misplaced after they performed VRAM updates. "Since messing with $2006 for the updates moves the background, clearing $2006 should put it back in place", or something along those lines. This actually works in some cases, so it was passed around as the definitive solution for misplaced backgrounds.

The truth is that the PPU has one set of registers ($2000, $2005) for controlling the scroll (i.e. point in the name tables where rendering starts) and another set ($2006, $2007) for writing to and reading from VRAM, but since the PPU uses its address register for all VRAM accesses, all of those registers ($2000, $2005, $2006, $2007) affect the address register in some way.

You're supposed to set the scroll through $2000 (name table selection) and $2005 (coarse and fine X and Y scroll within the selected name table), which allow you to straightforwardly select any pixel within the 512x480-pixel area formed by the 4 name tables (even though 2 of them are normally mirrored). Writes to $2006 are meant to set the address for writing and reading data to/from VRAM, but since the address register is also used for rendering, this affects the scroll as well, but in a much less intuitive way. The bits are interpreted like this:

Code:
--yyNNYYYYYXXXXX
yy: incomplete fine Y scroll (the 3rd bit is dropped);
NN: name table;
YYYYY: coarse Y scroll;
XXXXX: coarse X scroll;

As you can see, the fine Y scroll is mangled, and the fine X scroll isn't affected at all. Not to mention that the bits are all scrambled around. I don't know why anyone would prefer to set the scrolling like this unless they had to (and we do have to if we want to modify the vertical scroll mid-frame).