PPU scrolling
Writing to PPUSCROLL
($2005) and PPUADDR
($2006) affects the same internal PPU registers, but in different ways. The same 15-bit VRAM address value is used both for the user to access the VRAM via PPUDATA
($2007) and for the PPU to make its own internal accesses to fetch name and attribute table data. There's also a 15-bit latch from which the VRAM address register is reloaded at various times. By convention, the VRAM address is often referred to as Loopy_V
, and the latch as Loopy_T
, since "V
" and "T
" were the mnemonics used by Loopy in his original document. In addition to the VRAM address and its corresponding latch, a fine X-scroll value {Loopy_X
) is also utilized. This takes effect immediately, and controls which bit from the PPU's internal pixel shift registers will be selected.
Both $2005
and $2006
are designed to take two consecutive writes. Both registers share the same internal flip-flop for this purpose, and the toggle may be reset by reading PPUSTAT
($2002).
Writes to $2005
The first write to $2005
controls the horizontal scroll offset. This is done by copying the lower three bits of the value written into Loopy_X
, and the upper five bits into D4-D0 of Loopy_T
. The coarse horizontal scroll offset will take effect when the next scanline is rendered, while the fine horizontal scroll offset in Loopy_X
takes effect immediately.
The second write to $2005
controls the vertical scroll offset. The lower three bits of the value written are copied to D14-D12 of Loopy_T
, and the upper three bits are copied to D9-D5 of Loopy_T
. Since these bits are not reloaded into Loopy_V
before rendering a scanline, they won't take effect until the next frame.
Writes to $2006
The first write to $2006
sets the high address byte. Since the PPU's external address bus is only 14 bits in width, the top two bits of the value written are ignored. D14 of Loopy_T
is cleared, while D13-D8 are loaded with the lower six bits of the value written.
The second $2006
write is copied in its entirety to D7-D0 of Loopy_T
. More importantly, Loopy_T
is then copied into Loopy_V
. Many commercial games use writes to $2006
instead of $2005
to handle scrolling, since it is the only way to change the vertical scroll offsets during mid-frame rendering.
Writes to $2000
Writing to the PPUCTRL
register at $2000 also affects the VRAM address latch. The lower two bits of the value written (used for nametable selection) are copied to D11-D10 of Loopy_T
.
Use of Loopy_V
during rendering
At the beginning of each frame, the contents of Loopy_T
are copied into Loopy_V
, as long as background or sprites are enabled. This takes place on PPU cycle #304 of the pre-render scanline [1]. Also, before each scanline is rendered (assuming sprites or background are enabled), D10 and D4-D0 of Loopy_T
is copied into Loopy_V
, since they were altered in Loopy_V
during scanline rendering.