I am attempting to play around with writing the VRAM address and tile offsets using $2006. From documentation, it looks like bits 12-14 in the VRAM address are the tile y offset. That means the first time we write to $2006, the upper nibble of the byte is essentially the tile y offset, correct? (with the 15th bit ignored) When I try this out and check out the scrolling with FCEUXD, I am able to increment the tile y offset from 0 to 3, but when I set it to 4, it appears to wrap to 0, as though the 14th bit is being ignored when I write.
This is because the first write to $2006 only uses bits 0-5 (high 2 bits are forced to zero). This makes fully specifying the fine Y scroll impossible with $2006 alone.
To do this (assuming you can't just use $2005 -- like if you want to change V scroll mid frame or something), you need to alternate $2006 and $2005 writes. Do the writes in the following order:
$2006 - write NT scroll (NT scroll in bits 2,3)
$2005 - fine Y scroll
$2005 - fine X scroll
$2006 - write low byte of PPU address (coarse X scroll + 3 bits of coarse Y scroll)
Thanks. Now I understand skinny.txt! I didn't quite understand Loopy's shorthand until I read your response.
Will writing in this order have the same effect?
;$2006 - write NT scroll (NT scroll in bits 2,3)
;$2006 - write low byte of PPU address (coarse X scroll + 3 bits of coarse Y scroll)
;$2005 - fine X scroll
;$2005 - fine Y scroll
We switch the writes to $2005 since $2006 and $2005 share the toggle for high/low byte.
The thing is that these writes only affect the temporary PPU address (loopy_t) and not the real PPU address (loopy_v). The only way the real PPU address can be changed is automatically by the PPU at frame start (provided rendering is enabled) -- or on the second $2006 write. $2005 writes never update the real address.
This is why the second $2006 write must be the last in the series. Doing it your way would work for setting the temp address, but the $2005 writes would have no effect on the real PPU address.
And if you're using $2005 this way, you might as well just ditch $2006 completely and use the NT scroll bits from $2000 instead.
Ah! Now I understand the v=t under the second write to $2006 in skinny.txt. That is when the real address gets written. Thank you, this has been very helpful!
I notice in skinny.txt that v=t also on frame start. That would seem to imply that one could set the scrolling with $2006, and then $2005 as the last thing you do in your vblank, so it is ready for the next vblank. Is this common also?
Not really, because if you're doing this in VBlank, there's no reason to use $2006 at all. You can use the low 2 bits of $2000 to set the NT scroll, and use $2005 to set the remaining scroll bits normally. $2000+$2005 do it all... the only thing they don't do is explicitly change loopy_v.
The only time you need to use $2006 to change the scroll is if you're changing the Y scroll mid-frame (like for a status bar or something -- or if you're turning rendering on late and miss the PPU's automatic v=t)
This is probably the best "summarised" explanation (for programmers) I've ever seen. For years I've wondered why games I'd disassembled wrote to $2005/2006 in the order they did, figuring it wasn't important...
Maybe I'll have to go back and fix my FF2e intro for Demiforce.
Thanks a ton, Disch. You have no idea how much I appreciate what you've posted. :-)
koitsu wrote:
This is probably the best "summarised" explanation (for programmers) I've ever seen. For years I've wondered why games I'd disassembled wrote to $2005/2006 in the order they did, figuring it wasn't important...
I guess this serves as one more evidence that loopy's doc just isn't clear enough. A few years back I came here for explanations because I simply couldn't understand his document. I finally did though, with a little help.
If someone decides to present that information in a more friendly way (with a wiki entry maybe, instead of just a link to the original doc), they should also make clear that tricky $2005/$2006 writes are only necessary for messing with the scroll *after* the start of the frame. Many people seem to think that using $2006 for scrolling is required, while $2000 and $2005 were designed for manipulating the scroll under normal circunstances (during VBlank).
tokumaru wrote:
I guess this serves as one more evidence that loopy's doc just isn't clear enough.
I think that's kind of an understatement. It's not really as unclear as much as it's like a riddle or something. Basically the document gives you obscure clues or hints rather than answers.
I still have to go through this thread to fully understand the relationship between $2005/$2006 for changing the scroll mid frame. But I can already tell just by skimming through it that it's a nicely condensed explanation of this.
Well I always had trouble understanding loopy's docs and also wrote some kind of summary, tough it's really on the practical side and not on the theorical side (it does covers all regs, but a long paragraph is about $2005, $2006 and $2000 updates).
You can get a
html version and a
txt version.
Bregalad/Blargg, thanks for those, that is very helpful!
Question: writes to $2007 will always use loopy_v, and never loopy_t, correct? Therefore you have to use $2006 within vblank in order to write (where you want) to the nametable, correct?
Hey Koitsu, I copied that down when I saw what site he posted it on. If you wanna see it, here it is in my temp directory... this also means it will be 404 sometime in the future ; )
http://robertlbryant.com/temp/blarggs_scroll.txt
Necropost...anyone stil have this?
Used the archive.org to find blargg's summary
Code:
The PPU contains several registers:
Name Bits Function
------------------------------------------
V 15 Main address register
T 15 Hidden temporary register
F 3 Horizontal fine scroll
- 1 High/low flag shared by $2005/$2006
The high/low flag is toggled whenever $2005 or $2006 is written to, and
reset whenever $2002 is read from.
During the following events, bits from the source on the left are copied
to the indicated bits of the register on the right. W refers to the byte
written. For example, a $2000 write copies bits 0 and 1 of the value
written to bits 10 and 11 of T.
$2000 write:
W:------VH -> T:---VH-- -------- Selects nametable
$2005 first write:
W:XXXXXxxx -> T:------- ---XXXXX Sets X scroll
-> F:xxx
$2005 second write:
W:YYYYYyyy -> T:yyy--YY YYY----- Sets Y scroll
$2006 first write:
W:--hhhhhh -> T:0hhhhhh -------- Sets high 7 bits of T
$2006 second write:
W:llllllll -> T:------- llllllll Sets low 8 bits of T,
T -> V then copies to V
Beginning of frame:
T -> V Copies T to V
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
When drawing the background, X is incremented as each tile is drawn.
When this wraps back to 0, H is toggled. If vertical mirroring is used,
this results in the other nametable being used for the remaining right
side of the screen.
Y is incremented as each row is finished. When Y is 29 and incremented,
it becomes zero and V is toggled. If Y is set to 30 or 31 manually, when
it wraps around to 0, V is NOT toggled. This means that 30 and 31 are
sort of like negative scroll values, except they interpret attribute
data as pattern indicies.
[Monospace fixed by MOD]
strat wrote:
Used the archive.org to find blargg's summary
Cool, but please put it in a code tag instead.