Back in the day, the main thing that confused me was $2006. It
looks like you can just write the address of some tile within the nametable, and it'll start drawing from there. However, that's not true, because it won't start from the top of the tile unless you subtract #$2000 from what you write, and this confused me for the longest time, until I had something explained to me
The PPU doesn't have an "address" register. Instead, it has various counters: X, Y, y, and N. X and Y are 5-bit counters, and hold the (X, Y) coordinates for the current tile being drawn. N is a 2-bit counter that determines which of the 4 nametables that tile is coming from. "y" is a 3-bit counter that determines which scanline of the tile's pattern we're drawing (Don't forget, the PPU draws the same row of tiles for 8 scanlines, the only thing that changes is the specific byte of the pattern data it draws)
When you line those counters up like this: NNYYYYYXXXXX, you conveniently get the byte address for the nametable tile that those counters are pointing to. This is what's happening when you write to $2006, you're giving an "address", and the PPU is translating it to an (X, Y) coordinate within one of the nametables (N) by stuffing the bits of your "address" into those counters as appropriate.
The tricky part is the fact that the PPU needs 2 additional bits, so it knows whether you're trying to access the pattern tables, the nametables, or the palette. Those 2 additional bits come from "y".
So, when you write to $2006, this is what's happening:
Code: Select all
15 bit 0
$2006 [........ ........]
|||||| ||||||||
|||||| |||+++++--- X
|||||| |||
||||++-+++-------- Y
||||
||++-------------- N
||
++---------------- y
(Note: The highest two bits of $2006 are ignored)
This is why you can't simply write the address for a nametable tile when you want the screen to start drawing from there; the nametables are at $2xxx, so you're setting "y" to 2, which means you'll start 2 pixels down from the top of the tile you want to draw, instead of at the top. If you subtract #$2000 from the tile address you want, then even though the "address" you write points to the pattern tables, the actual
counters are set up properly (especially "y", which is now set to 0), and the tile will correctly be drawn from its top scanline.
The PPU can line these counters up in a variety of different ways. For example, $2005 does it like this:
YYYYYyyy XXXXXxxx
which translates to a specific pixel within a nametable, but you cannot select which nametable you want by using $2005.
While the PPU is rendering, it uses something like this:
NYYYYYyyy NXXXXX
Every 8 pixels, 1 is added to X, which overflows to the low bit of N (so it'll cross to the next nametable, horizontally). On the next scanline, NXXXXX is reset, and 1 is added to y (moving us to the next scanline), which overflows to Y (moving us to the next row of tiles), which overflows to the high bit of N (crossing us over into the next nametable, vertically).
Additionally, there's some extra logic in place to make it so Y overflows (and wraps) after 29, instead of after 31. However, Y only overflows into the top bit of N when overflowing from 29. Otherwise, Y will just wrap from 31 to 0. This is what creates the "negative scrolling" quirk when you set the Y scrolling between E0-FF; Y is being set to 30 or 31, and after 31, it wraps to 0 without touching N.
This was awfully wordy to explain, but hopefully it wasn't too confusing.

When you write to $2006, you're
setting counters, even though it
looks like you're writing an address. That's why the $2006/2005/2005/2006 trick requires you to write a bunch of bullshit to $2006; even though they're not addresses, they're still setting the counters the way you want them.