Jumpy screen - out of CPU time?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Jumpy screen - out of CPU time?

Post by UncleSporky »

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. :)
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Post by Banshaku »

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.
UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Post by UncleSporky »

This code is executed every program loop (but obviously only updates scroll once).

Code: Select all

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...
User avatar
MetalSlime
Posts: 186
Joined: Tue Aug 19, 2008 11:01 pm
Location: Japan

Post by MetalSlime »

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/viewtopic.php?p=47965#47965
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Post by koitsu »

What never made any sense to me is why people say things like this:
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.
UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Post by UncleSporky »

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.
User avatar
MottZilla
Posts: 2837
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla »

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.
UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Post by UncleSporky »

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?
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?
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

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.
UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Post by UncleSporky »

Got it, thanks a lot. :)
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Post by koitsu »

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: Select all

    $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: Select all

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

Post by tokumaru »

koitsu wrote:Doesn't this mean the first write to $2006 overwrites V and H?
Yes.
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.
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).
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.
UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Post by UncleSporky »

I was just about to try to respond to that. :)
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.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

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.
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.
UncleSporky
Posts: 388
Joined: Sat Nov 17, 2007 8:44 pm

Post by UncleSporky »

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