|
|
(88 intermediate revisions by 7 users not shown) |
Line 1: |
Line 1: |
| "[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.
| | #REDIRECT [[PPU scrolling]] |
| 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 the end of each scanline, if rendering is turned on, the PPU copies all bits related to horizontal position from ''t'' to ''v'':
| |
| v:.....H.. ...EDCBA = t:.....H.. ...EDCBA
| |
| And at the end of the pre-render scanline, if rendering is turned on, 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.
| |