It is currently Thu Oct 19, 2017 9:35 am

All times are UTC - 7 hours

Post new topic Reply to topic  [ 4 posts ] 
Author Message
PostPosted: Mon Oct 17, 2005 8:23 am 
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
Loopy's scroll docs, while great, are somewhat difficult to understand at first. Since that post in the "What drove koitsu away from nesdev" thread where people showed confusion about the docs and wanted clarification, I figured I'd print this up to see if I could help.

You gotta know a few things:

1) The PPU address you set with $2006 is the same address the PPU uses to fetch tiles for rendering. In effect, it holds the address of the next tile to draw... which is why changing $2006 during rendering will muck up the screen, even if you don't write anything to $2007. This also means that the address is constantly changing during rendering (more details later). This address will hereon be referred to as "LoopyV".

2) In addition to that main address... there's also a temporary value which holds the desired scroll address which the PPU uses to refresh LoopyV with from time to time (like say, every scanline to reset the X Scroll). This temporary value is another value entirely and may contain a completely different value than what's in LoopyV. Hereon, this temporary address will be referred to as "LoopyT".

3) Both LoopyV and LoopyT have an effective range of $0000-7FFF... so they're 15-bits wide (they may actually be 16-bits wide, but it's impossible to set the high bit... and it doesn't do anything anyway so whatever).

4) LoopyT does NOTHING... aside from reload LoopyV at certain times. If you want to actually affect where the PPU draws from (ie, change the scrolling)... or change the address so you can write to $2007... LoopyV is what you need to change. LoopyV does it all. This is why you can't use just $2005 to change the vertical scroll mid-frame (since $2005 never changes LoopyV) and why clever alternating $2006/$2005 writes are required...since the second $2006 write is the only way to set LoopyV aside from the scroll reset at frame start.

I won't get into first/second writes and the toggle used to keep track of which is which. If someone is still confused about that you can ask in this thread and I'll explain, but right now I'll just assume you know.

In Loopy's docs:

'v' represents LoopyV
't' represents LoopyT
'x' represents the fine Horizontal scroll (0-7 pixels)
'd' represents the data being written to the register

These letters are usually followed by a colon, then various 0's and 1's to indicate the bits involved in this operation. 0 bits are to be ignored.... and '1' bits are to be involved in the write.

So now you might have looked at loopy's scroll doc to see the following and went "omgwtf":

2000 write:

But it's really very simple... He's just saying the low 2 bits of the data written to $2000 set bits 10 and 11 of LoopyT. To put this in a more friendly layout:

t ->   ....XX..........
d -> ......XX

The '.' or '0' bits in my and loopy's explainations are irrelevent to scrolling. The unused LoopyT bits here remain unchanged from their previous state, and the other 'd' bits go on to do other things (see a $2000 register description for details on that)

Hopefully that should clear up his system of write explainations.

One thing that should be mentioned:


These bits (bits 12, 13, 14) in LoopyV determine the fine-vertical scroll (as I will try to explain below).

LoopyV and LoopyT during rendering:

Note... everything I talk about here assumed the PPU is active (BG or Sprite rendering enabled... if both are disabled, the PPU is "off", and LoopyV is NEVER changed (except on the second $2006 write))

As I briefly stated before, the PPU will constantly update LoopyV as it fetches tiles to render. This is so it knows where to get the next tile to draw. The logic for this may be a little hard to follow... but stick with me.

At Frame Start: On frame start, the PPU will do what I sometimes call "reset the scroll". At the end of VBlank, LoopyT (which probably contains what you just wrote to $2005 for your desired scroll values) gets copied over to LoopyV. This is the address where the PPU will start rendering tiles from.

As tiles are rendered: When 1 tile is fetched and rendered, LoopyV is incremented by 1 so that the next tile will be loaded next. When it hits the right edge of the nametable, it wraps around to the start of the next nametable. Example:
0000, 0001, 0002, 0003 .... 001E, 001F, 0400, 0401 .... 041E, 041F, 0000, 0001 ... etc

At the end of the scanline: Two things are done once a full scanline has been fetched/rendered. The Y position needs to increment, and the X scroll needs to reset to its original position. X scroll resetting is done by reloading the X-related bits from LoopyT back into LoopyV... as portrayed by Loopy's doc:

scanline start (if background and sprites are enabled):

Y position is incremented.. this is a bit more complicated than X scrolling. Fine Y scroll (previous mentioned as bits 12, 13, 14) in incremented first, and when it wraps at 8, THEN the coarse Y is incremented, changing the nametable byte to be fetched to the next row of tiles. So somewhat put this in example form:

0000, 1000, 2000 .... 6000, 7000, 0020, 1020 ... 7020, 0040 .. etc

Since nametables aren't as tall as they are wide, coarse Y scrolling is cut off after 29 ($3A0), rather than 31 ($3E0):

0380, 03A0, 0800, 0820 ... 0B80, 0BA0, 0000, 0020 ... etc

If you manage to set the coarse Y scrolling manually so that it's above 29, it will still wrap at 31, however the nametable (bit 11) will not be changed:

03C0, 03E0, 0000, 0020 ... 0380, 03A0, 0800 ... etc

That's about it! I know it's pretty funky but it isn't all that hard once you get the idea. Feel free to ask Qs and I'll answer as best I can.

 Post subject:
PostPosted: Mon Oct 17, 2005 9:54 am 

Joined: Sun Nov 14, 2004 11:24 am
Posts: 330

You might want to add that to he wiki,

 Post subject:
PostPosted: Tue Oct 18, 2005 6:04 am 
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10058
Location: Rio de Janeiro - Brazil
Boy, if I had read this a couple of years ago...!

It took me ages to understand the 1s (in d) meant bits that would end up where the other 1s are.

At first I thought the doc was describing some sort of example, that I coudn't see where it was going...

 Post subject: 0's + 1's
PostPosted: Thu Nov 10, 2005 1:11 pm 
Just a little point, but in assembler / hardware docs, it's traditional to
represent "don't care" bits as x (lower case, yes).

So, xxxx11xx would make more sense, I think, and be more readable
than using 0s. To me, a set of 1s and 0s is a binary value.

Anyway, just a readability tip from a visitor.


Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC - 7 hours

Who is online

Users browsing this forum: No registered users and 11 guests

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group