The skinny on NES scrolling: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
m (on scanline -1, v= t at dot 304, plus dot 258 copies the H position bits, AFAIK.)
(merge to PPU scrolling)
 
(84 intermediate revisions by 7 users not shown)
Line 1: Line 1:
__TOC__
#REDIRECT [[PPU scrolling]]
== Preface ==
"[http://nesdev.parodius.com/loopyppu.zip The skinny on NES scrolling]" was posted by loopy on 1999-04-13 to what eventually became the NESdev Yahoo! Group.
It was the first to publicly tell how exactly how the PPU uses addresses written to [[PPU registers|its ports]].
After over a decade, it is ''still believed accurate.''
Some people get turned off by the fact that it's provided as monospaced text inside a zipfile, that addresses have nothing to distinguish them from years, and that the diagrams of what bits get copied where are allegedly difficult to read.
What follows is this document, reformatted to web standards, with a few minor things made slightly clearer.
 
== PPU registers ==
 
Games using complex raster effects require a complete understanding of how the various address registers inside the PPU work.
Here are the related registers:
;v: Current VRAM address (15 bits)
;t: Temporary VRAM address (15 bits)
;x: Fine X scroll (3 bits)
Registers ''v'' and ''t'' are 15 bits, but because emulators commonly store them in 16-bit machine words, they are shown with an extra bit that's never used.
 
The PPU uses the current VRAM address for both reading and writing PPU memory thru $2007, and for fetching nametable data to draw the background.
As it's drawing the background, it updates the address to point to the nametable data currently being drawn.
Bits 10-11 hold the nametable address minus $2000.
Bits 12-14 are the Y offset of a scanline within a tile.
 
== Stuff that affects register contents ==
 
In the following, ''d'' refers to the data written to the port, and ''A'' through ''H'' to individual bits of this value.
 
$2000 write:
t:....BA.. ........ = d:......BA
$2005 first write:
t:........ ...HGFED = d:HGFED...
x:              CBA = d:.....CBA
$2005 second write:
t:......HG FED..... = d:HGFED...
t:.CBA.... ........ = d:.....CBA
$2006 first write:
t:..FEDCBA ........ = d:..FEDCBA
t:.G...... ........ = 0
$2006 second write:
t:........ HGFEDCBA = d:HGFEDCBA
v                  = t
At dot 258 of each scanline, if rendering is enabled, the PPU copies all bits related to horizontal position from ''t'' to ''v'':
v:.....H.. ...EDCBA = t:.....H.. ...EDCBA
And at dot 304 of the pre-render scanline, if rendering is enabled, the PPU copies all bits from ''t'' to ''v'':
v                    = t
 
Note: $2005 and $2006 share the toggle that selects between first/second writes.
Reading $2002 will clear it.
 
All of this info agrees with the tests Loopy has run on an NES console.
If there's something you don't agree with, please let [http://nesdev.org/bbs/ the BBS] know so that a member can verify it.
 
== Wrapping around ==
 
You can think of bits 4-0 of the VRAM address as the "coarse x scroll"(*8) that the PPU increments as it draws.
As it wraps from 31 to 0, bit 10 is switched.
You should see how this causes horizontal wrapping between nametables (0,1) and (2,3).
 
You can think of bits 9-5 as the "coarse y scroll"(*8).
This functions slightly different from the X.
It wraps to 0 and bit 11 is switched when it's incremented from 29 instead of 31.
There are some odd side effects from this.
If you manually set the value above 29 (from either $2005 or $2006), the wrapping from 29 obviously won't happen, and attribute data will be used as nametable data.
The "y scroll" still wraps to 0 from 31, but without switching bit 11.
This explains why writing 240+ to 'Y' in $2005 appeared as a negative scroll value.
 
== Examples ==
 
Below are examples using actual 6502 code, indicating (tracking) what happens to all relevant variables described above, both before and after the 6502 instructions are executed.
 
Individual bits written to a PPU register are colour-coded to reflect where they end up in ''t''.
 
Finally, assume all 6502 code is run sequentially in the order shown, one instruction after the next.
 
=== Original skinny example ===
 
This is based completely off of the above [[#Stuff_that_affects_register_contents|Stuff that affects register contents]] section.
 
{| class="wikitable"
!colspan="3" style="background-color:palegreen" | Before
!rowspan="2" | Instructions
!colspan="3" style="background-color:lightcoral" | After
!rowspan="2" | Notes
|-
!style="background-color:palegreen" | t
!style="background-color:palegreen" | v
!style="background-color:palegreen" | x
!style="background-color:lightcoral" | t
!style="background-color:lightcoral" | v
!style="background-color:lightcoral" | x
|-
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ...
|style="font-family:monospace; white-space:nowrap;" | LDA #$00 (%000000<span style="background-color:lime">00</span>)<br />STA $2000
|style="font-family:monospace; white-space:nowrap;" | ...<span style="background-color:lime">00</span>.. ........
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ...
|
|-
|style="font-family:monospace; white-space:nowrap;" | ...00.. ........
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ...
|style="font-family:monospace; white-space:nowrap;" | LDA #$7D (%<span style="background-color:lime">01111</span><span style="background-color:yellow">101</span>)<br />STA $2005
|style="font-family:monospace; white-space:nowrap;" | ...00.. ...<span style="background-color:lime">01111</span>
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | <span style="background-color:yellow">101</span>
|
|-
|style="font-family:monospace; white-space:nowrap;" | ...00.. ...01111
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | 101
|style="font-family:monospace; white-space:nowrap;" | LDA #$5E (%<span style="background-color:lime">01</span><span style="background-color:yellow">011</span><span style="background-color:cyan">110</span>)<br />STA $2005
|style="font-family:monospace; white-space:nowrap;" | <span style="background-color:cyan">110</span>00<span style="background-color:lime">01</span> <span style="background-color:yellow">011</span>01111
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | 101
|
|-
|style="font-family:monospace; white-space:nowrap;" | 1100001 01101111
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | 101
|style="font-family:monospace; white-space:nowrap;" | LDA #$3D (%00<span style="background-color:lime">111101</span>)<br />STA $2006
|style="font-family:monospace; white-space:nowrap;" | 0<span style="background-color:lime">111101</span> 01101111
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | 101
|Bit 15 of ''t'' gets set to zero
|-
|style="font-family:monospace; white-space:nowrap;" | 0111101 01101111
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | 101
|style="font-family:monospace; white-space:nowrap;" | LDA #$F0 (%<span style="background-color:lime">11110000</span>)<br />STA $2006
|style="font-family:monospace; white-space:nowrap;" | 0111101 <span style="background-color:lime">11110000</span>
|style="font-family:monospace; white-space:nowrap;" | 0111101 11110000
|style="font-family:monospace; white-space:nowrap;" | 101
|After ''t'' is updated, contents of ''t'' copied into ''v''
|}
 
=== 2006-2005-2005-2006 example ===
 
This is based on [http://nesdev.parodius.com/bbs/viewtopic.php?p=78593#78593 Drag's example on the nesdev forum] where writes to PPU registers are done in the order of $2006, $2005, $2005, $2006, and what the effect is.
 
{{mbox
| type = warning
| text = To understand this example, you need to keep in mind the following note from the above section:<br />'''Note: $2005 and $2006 share the toggle that selects between first/second writes. Reading $2002 will clear it.'''
}}
 
{| class="wikitable"
!colspan="3" style="background-color:palegreen" | Before
!rowspan="2" | Instructions
!colspan="3" style="background-color:lightcoral" | After
!rowspan="2" | Notes
|-
!style="background-color:palegreen" | t
!style="background-color:palegreen" | v
!style="background-color:palegreen" | x
!style="background-color:lightcoral" | t
!style="background-color:lightcoral" | v
!style="background-color:lightcoral" | x
|-
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ...
|style="font-family:monospace; white-space:nowrap;" | LDA #$27 (%00<span style="background-color:lime">100111</span>)<br />STA $2006
|style="font-family:monospace; white-space:nowrap;" | 0<span style="background-color:lime">100111</span> ........
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ...
| Bit 15 of ''t'' set to zero
|-
|style="font-family:monospace; white-space:nowrap;" | 0100111 ........
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ...
|style="font-family:monospace; white-space:nowrap;" | LDA #$3E (%<span style="background-color:lime">00</span><span style="background-color:yellow">111</span><span style="background-color:cyan">110</span>)<br />STA $2005
|style="font-family:monospace; white-space:nowrap;" | <span style="background-color:cyan">110</span>01<span style="background-color:lime">00</span> <span style="background-color:yellow">111</span>.....
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ...
| Behaviour of 2nd $2005 write
|-
|style="font-family:monospace; white-space:nowrap;" | 1100100 111.....
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | ...
|style="font-family:monospace; white-space:nowrap;" | LDA #$7D (%<span style="background-color:lime">01111</span><span style="background-color:yellow">101</span>)<br />STA $2005
|style="font-family:monospace; white-space:nowrap;" | 1100100 111<span style="background-color:lime">01111</span>
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | <span style="background-color:yellow">101</span>
| Behaviour of 1st $2005 write
|-
|style="font-family:monospace; white-space:nowrap;" | 1100100 11101111
|style="font-family:monospace; white-space:nowrap;" | ....... ........
|style="font-family:monospace; white-space:nowrap;" | 101
|style="font-family:monospace; white-space:nowrap;" | LDA #$99 (<span style="background-color:lime">%10011001</span>)<br />STA $2006
|style="font-family:monospace; white-space:nowrap;" | 1100100 <span style="background-color:lime">10011001</span>
|style="font-family:monospace; white-space:nowrap;" | 1100100 10011001
|style="font-family:monospace; white-space:nowrap;" | 101
|After ''t'' is updated, contents of ''t'' copied into ''v''
|}

Latest revision as of 02:41, 25 March 2015

Redirect to: