Jumpy screen - out of CPU time?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Jumpy screen - out of CPU time?
by on (#49321)
I've been messing around, making a NES demo. Recently I've kept running into a strange little glitch that I can't nail down.

You can see what I mean by downloading it here:
http://www.box.net/shared/3u9d5u97gm

"demo.nes" is the normal demo. "demo jitter.nes" has had some extra code inserted that should do nothing but eat up a few more CPU cycles. This makes the whole screen shake slightly for some reason.

I've been very careful with the scrolling since I'm not using it right now, I only write to $2005 to set the initial scroll and nothing else modifies it. Since all I need to do to cause the jitter is add a few more instructions to the program, I have to assume I've run out of cycles before vblank or something? (Already?!) Might that cause some scrolling issues?

By the way, the background is from La-Mulana and the character sprite is from Knytt Stories. :)

by on (#49322)
It's hard to say without seeing any code but I had some scroll glitch that made the screen jumping around if I tried to update the PPU data but didn't update the scroll registers/PPU controller.

This could be a similar case? Just a wild guess.

by on (#49324)
This code is executed every program loop (but obviously only updates scroll once).
Code:
update_scroll:      ;*** this is a temporary function to initialize the screen position
   lda #0
   sta $2006
   sta $2006

   lda scroll
   beq +
   lda #0
   sta scroll
   sta $2005
   sta $2005
   rts


Is that the problem? I need to write to $2005 every loop as well? But it was working fine before...

by on (#49326)
UncleSporky wrote:
Is that the problem? I need to write to $2005 every loop as well? But it was working fine before...


Yes probably this is the problem. :) Read here:

http://nesdev.com/bbs/viewtopi ... 7965#47965

by on (#49339)
What never made any sense to me is why people say things like this:

Quote:
The VRAM read/write port (PPUADDR/PPUDATA, $2006/$2007) uses the same register inside the PPU as the scroll position (PPUSCROLL, $2005). So yes, after you upload new data to the PPU, you need to reset the scroll position.


Not to sound crass, but this is the equivalent of this video -- "Give him the stick... DON'T GIVE HIM THE STICK!" Meaning: the statement has never made any sense to me... but then again, I've never understood loopy's document. That is to say, I understand the concept of the PPU itself having its own addressing bits which $2005/6/7 controls in unique ways, but I don't understand *how*.

More specifically, when someone says "you need to reset the scroll position", my answer is: to what? How do you know what the scroll position *should* be?

I've seen code where people do what UncleSporky does -- set $2005 to $00 twice. I've seen this in FF2j (for sake of example too). But I also remember being told to do stuff like "set $2006 to $23C0 before the end of what you're doing" (supposedly setting the internal PPU address to that of the attribute table), and my first response was "why?" yet no explanation given would make any sense.

Sorry if I sound like I'm ranting or pissing on people's efforts/explanations -- it's just that the one-line answers really don't explain anything, especially when they're made to be simple like "reset the scroll position" (again: to what, and how do you keep track of it?)

EDIT: Okay, this post helps shed some light on things, but it's also a good example of folks (including myself) not understanding the Why, When, and How of this beast. It's a recurring topic.

by on (#49340)
koitsu wrote:
I've seen code where people do what UncleSporky does -- set $2005 to $00 twice. I've seen this in FF2j (for sake of example too).

The first write sets the horizontal scroll (to 0 in my case) and the second sets the vertical scroll (also 0). If I run a loop here and keep decrementing the vertical scroll, I start at the second nametable and the first scrolls in from the top. (This is from NES101.)

But I agree with you on the rest, I thought I was all set. Clear $2006 twice, set the H and V scroll when you need to and you're fine. Apparently not!

Although upon rereading the part you quoted, it does make sense to me: think of 2006/2007 as a normal register like "a." Once it is done being used, the leftover data is still inside, so I need to set it again. At least I think so...but then again I'm still not accessing $2005 at all. What else could be messing with the scroll values? Hmm.

Anyway I will experiment with it and see if I can stop the bouncing. I would like to know more certainly if this problem only cropped up because I am out of time and need to start optimizing, though.

by on (#49351)
As loopy explained it the PPU has a few different address registers of sorts. The names I remember would be Loopy_T, Loopy_V and FineX. When you write to $2005 or $2006 it changes a piece of Loopy_T, which is not where the Loopy_V points to, and Loopy_V is where the PPU is pointing to currently. Only on a second write to $2006 I believe, is Loopy_T put into Loopy_V. You also need to understand there is a flip-flop bit that determines a 1st or 2nd write to $2005 and $2006 to determine High or Low write, and that it is shared between them. That means if you write to $2005 and then $2006 that will change Loopy_V.

Another thing is that you need to set the scroll values every frame. So during your NMI you should read $2002 to reset the flip-flop and then do your VRAM updates, and finally set your scroll position so that when the PPU begins rendering it begins from the proper place. You shouldn't set it once and assume it will stay like that. I suppose it might stay like that if you do not make any writes to $2005 or $2006 so that the Loopy_T never changes as if it does Loopy_T will be copied to Loopy_V at the beginning of the frame causing a scroll you didn't expect.

Hopefully that all makes sense, if not read loopy's document on NES scrolling magic. It's not that hard to understand and it explains everything you need to know.

by on (#49360)
Thanks, from the above threads and this thread I think I finally understand loopy's doc as far as I need to be able to for now. (Blargg's doc is far better.) Still fuzzy on $2006 and $2007...that's just for rewriting your nametables, right?

Quote:
So during your NMI you should read $2002 to reset the flip-flop and then do your VRAM updates, and finally set your scroll position so that when the PPU begins rendering it begins from the proper place.

Is it sufficient to read $2002 continuously to detect vblank, and upon entry it will be properly reset?

Proper practice is to wait for vblank, use $2006 and $2007 to transfer nametable data, and end with $2005 writes? What operations on these registers are available outside of vblank?

If $2005 should come last, what is Disch doing here?

by on (#49364)
The canonical write sequence at the end of vertical blank is $2000, $2005, $2005, or $2005, $2005, $2000. Writes to $2006, $2005, $2005, $2006, like what Disch's example does, are usually intended to change the vertical scroll during draw time if you're trying to do a raster split.

by on (#49365)
Got it, thanks a lot. :)

by on (#49372)
UncleSporky wrote:
... Blargg's doc is far better. ...


I agree -- reading Blargg's document makes a lot more sense to me. Good lord, I had no idea it was this complex. No wonder writes to $2000/5/6 "look so odd" when disassembling commercial games.

That said, there are two things I don't understand from Blargg's doc:

Question #1:

Code:
    $2000 write:
        W:------VH -> T:---VH-- --------    Selects nametable

    $2006 first write:
        W:--hhhhhh -> T:0hhhhhh --------    Sets high 7 bits of T


Doesn't this mean the first write to $2006 overwrites V and H? If so, how do those ever get retained inside of the PPU? More simply: what's the point in writing to $2000 at all if the first write to $2006 defines V and H?

Or is there a copy of T to V going on immediately after the $2000 write?

Question #2:

Code:
    Name    Bits        Function
    ------------------------------------------
    F       3       Horizontal fine scroll

    $2005 first write:
        W:XXXXXxxx -> T:------- ---XXXXX    Sets X scroll
                   -> F:xxx

    Beginning of scanline:
        T:----H-- ---XXXXX -> V:----H-- ---XXXXX    Copies H&X from T to V

During rendering, V is treated as several independent counters:
   
    V:yyyVHYY YYYXXXXX
        yyy     Fine Y scroll
        VH      V & H nametable bits
        YYYYY   Y & X in nametable
        XXXXX


There's no mention of where F comes into play, or how it works/how it gets applied in the PPU.

And a comment in passing:

It doesn't help that this document uses the "V" variable for two different things: vertical/horizontal mirroring, and the label for the Main address register.

So I'm still confused, just less so. :-)

by on (#49373)
koitsu wrote:
Doesn't this mean the first write to $2006 overwrites V and H?

Yes.

Quote:
If so, how do those ever get retained inside of the PPU?

This is the reason you have to write to $2000 and $2005 to properly set the scroll every frame: using $2006 fucks up the scroll.

Quote:
More simply: what's the point in writing to $2000 at all if the first write to $2006 defines V and H?

No need to use $2000 if you are using $2006 to set the scroll (which you should do only if setting the scroll outside of VBlank).

Quote:
There's no mention of where F comes into play, or how it works/how it gets applied in the PPU.

The fine X scroll is not really part of this mess (most likely because it's not needed in order to fetch bytes from PPU memory), it's stored somewhere else and used by the PPU to displace tiles sideways when rendering the background.

by on (#49374)
I was just about to try to respond to that. :)

Quote:
No need to use $2000 if you are using $2006 to set the scroll (which you should only if setting the scroll outside of VBlank).


How do you use $2006 to set the scroll?

EDIT: I had a guess above that was incorrect. Let me try again. As Disch showed us in the other topic, you use $2006 and $2005 together to assemble the full T register according to the scroll you want. In this case $2006 is not being used for an address, but merely as a way to write the nametable bits into the 16-bit register (since $2005 alone can't do that). This way you do not have to touch $2000, and the second write to $2006 copies T to V, implementing that new scroll.

One other problem with Blargg's doc is that we have the useful information of what $2005 does (sets X/Y scroll) and un-useful information for $2006 (sets the high bits of T?). When written that way I immediately think of "sets" in terms of setting a flag, or set carry, meaning it writes 1s. But from other documents I see you're usually entering in an address to access with $2007.

by on (#49375)
UncleSporky wrote:
How do you use $2006 to set the scroll?

This is the whole point in having documents describing where all the writes to $2000, $2005 and $2006 go.

Quote:
Wait, let me see if I might have it...the tile address you put in $2006 becomes 0,0 onscreen, and then $2005 is applied?

Something like that, but not exactly. Look at Blargg's doc. The value in V, along with the 3 bits in F, make the current scroll position. This is what V must hold in the end: 0yyyVHYY YYYXXXXX

You just have to use whatever combination of writes to put the coordinates you want in that format. This is why loopy's and Blargg's documents are important, they tell where each written bit ends up. If the first write to $2006 didn't clear the top 2 bits it would be much easier, but since it does a more complex approach is necessary. Personally, I write in the following order:

1. ----VH-- to $2006;
2. Y scroll to $2005;
3. X scroll to $2005;
4. YYYXXXXX to $2006;

Once V is fully formed, it doesn't necessarily become 0, 0. That V will be used from the point it was set onwards. That is, if you write $00 to $2006 twice by the middle of the screen, you'll see the top of the first name table at the bottom half of the screen.

by on (#49376)
Yep, got that before you posted. :)

Under normal (simple) circumstances with no screen splitting tricks, you would simply wait for vblank, use $2006 and $2007 to write your nametables, then reset with $2000 and set your scroll with $2005.

But if you want to do something like a non-scrolling status bar or parallax, you wait for sprite 0 hit and use $2006 and $2005 as described above to write a new scroll. $2006 is being used differently in this case, to set the nametable and to finalize the changes you make.

It is a big relief to finally understand this, I feel like I can do anything now. I guess everyone on this site has had to go through it at some point! Thanks for your patience.

by on (#49377)
UncleSporky wrote:
This way you do not have to touch $2000, and the second write to $2006 copies T to V, implementing that new scroll.

Please don't think of setting the scroll with $2005/$2006 as a "shortcut". You may "not have to touch $2000", but it's still slower and more complex than using $2000 and $2005, which is the correct, safe way of setting the scroll.

The correct way to set the scroll is selecting the desired name table by writing to $2000 and then specifying how far into that name table rendering should start with 2 consecutive writes to $2005, all of this still during VBlank. This is the correct and safe way to do it if you are a nice boy and like to play by the rules.

But if you like adventures, and for whatever reason you absolutely need to set the scroll outside of VBlank, you have to use those tricks.

The PPU has this internal register it uses to read and write information in it's address space. We control this register by writing to $2006. Curiously enough, the PPU appears to use this same register to keep track of what's displayed during rendering. The conclusion is that, as long as we obey the format in which the PPU handles that information, we can alter what is fetched for display as the screen renders.

That's it. Obey the format the PPU uses, and use whatever combination of writes to put the information where you want it. Do this at the correct time during rendering and you'll successfully manipulate the scroll mid-frame.

by on (#49378)
UncleSporky wrote:
Under normal (simple) circumstances with no screen splitting tricks, you would simply wait for vblank, use $2006 and $2007 to write your nametables, then reset with $2000 and set your scroll with $2005.

Yes, you got that right.

Quote:
But if you want to do something like a non-scrolling status bar or parallax, you wait for sprite 0 hit and use $2006 and $2005 as described above to write a new scroll.

Yes, but it doesn't have to be a sprite 0 hit. It can be an IRQ, timed code, it doesn't matter, as long as you have a way to perform the writes at the correct time.

Also, note that depending on the type of split you want, you don't have to go through all the trouble of writing to $2006. If you are only changing the horizontal scroll (X), you can still use $2000 and $2005 just fine. The whole trick with $2006 is only necessary if you have to change the vertical scroll as well.