PPU registers: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(→‎Mask ($2001) > write: Thanks, Q. I originally had it there because some people coming from ancient docs with the bits in the wrong order might get confused.)
m (→‎Controller ($2000) > write: reword, otherwise it sounds like it's suggestin writing to $2000 to clear the vblank flag)
(143 intermediate revisions by 20 users not shown)
Line 1: Line 1:
The PPU exposes eight memory-mapped registers to the CPU. These nominally sit at $2000 through $2007 in the CPU's address space, but because they're incompletely decoded, they're [[Mirroring|mirrored]] in every 8 bytes from $2008 through $3FFF, so a write to $3456 is the same as a write to $2006.
The [[PPU]] exposes eight memory-mapped registers to the CPU. These nominally sit at $2000 through $2007 in the CPU's address space, but because their addresses are incompletely decoded, they're [[Mirroring#Memory Mirroring|mirrored]] in every 8 bytes from $2008 through $3FFF. For example, a write to $3456 is the same as a write to $2006.
 
Immediately after powerup, the PPU isn't necessarily in a usable state.
The program needs to do a few things to get it going; see [[PPU power up state]] and [[Init code]].  


After power-on and reset, many of the PPU's registers are not immediately usable until enough time has passed. See [[PPU power up state]] and [[Init code]] for details.
<noinclude>
<noinclude>
__TOC__
__TOC__
</noinclude>
</noinclude>
== Summary ==
{| class="tabular"
! Common Name
! Address
! Bits
! Notes
|-
! [[#PPUCTRL|PPUCTRL]]
! $2000
| <tt style="white-space: nowrap">VPHB SINN</tt> || [[NMI]] enable (V), PPU master/slave (P), sprite height (H), background tile select (B), sprite tile select (S), increment mode (I), nametable select (NN)
|-
! [[#PPUMASK|PPUMASK]]
! $2001
| <tt style="white-space: nowrap">BGRs bMmG</tt> || color emphasis (BGR), sprite enable (s), background enable (b), sprite left column enable (M), background left column enable (m), greyscale (G)
|-
! [[#PPUSTATUS|PPUSTATUS]]
! $2002
| <tt style="white-space: nowrap">VSO- ----</tt> || vblank (V), sprite 0 hit (S), sprite overflow (O); read resets write pair for $2005/$2006
|-
! [[#OAMADDR|OAMADDR]]
! $2003
| <tt style="white-space: nowrap">aaaa aaaa</tt> || [[PPU OAM|OAM]] read/write address
|-
! [[#OAMDATA|OAMDATA]]
! $2004
| <tt style="white-space: nowrap">dddd dddd</tt> || OAM data read/write
|-
! [[#PPUSCROLL|PPUSCROLL]]
! $2005
| <tt style="white-space: nowrap">xxxx xxxx</tt> || fine scroll position (two writes: X scroll, Y scroll)
|-
! [[#PPUADDR|PPUADDR]]
! $2006
| <tt style="white-space: nowrap">aaaa aaaa</tt> || PPU read/write address (two writes: most significant byte, least significant byte)
|-
! [[#PPUDATA|PPUDATA]]
! $2007
| <tt style="white-space: nowrap">dddd dddd</tt> || PPU data read/write
|-
! [[#OAMDMA|OAMDMA]]
! $4014
| <tt style="white-space: nowrap">aaaa aaaa</tt> || OAM DMA high address
|}
== MMIO registers ==
{{Anchor|Ports}}
The PPU has an internal data bus that it uses for communication with the CPU.
This bus, called <code>_io_db</code> in [[Visual 2C02]] and <code>PPUGenLatch</code> in FCEUX,<ref>[http://sourceforge.net/p/fceultra/code/HEAD/tree/fceu/trunk/src/ppu.cpp#l183 ppu.cpp] by Bero and Xodnizel</ref> behaves as an 8-bit dynamic latch due to capacitance of very long traces that run to various parts of the PPU.
Writing any value to any PPU port, even to the nominally read-only PPUSTATUS, will fill this latch.
Reading any readable port (PPUSTATUS, OAMDATA, or PPUDATA) also fills the latch with the bits read.
Reading a nominally "write-only" register returns the latch's current value, as do the unused bits of PPUSTATUS.
This value begins to decay after a frame or so, faster once the PPU has warmed up, and it is likely that values with alternating bit patterns (such as $55 or $AA) will decay faster.<ref>[//forums.nesdev.org/viewtopic.php?p=143801#p143801 Reply to "Riding the open bus"] by lidnariq</ref>


=== Controller ($2000) > write ===  
=== <span id="PPUCTRL"><span id="Reg2000">Controller ($2000) > write</span></span> ===


Is often referred as PPUCTRL.
* Common name: '''PPUCTRL'''
* Description: PPU control register
* Access: write


Various flags controlling PPU operation
Various flags controlling PPU operation
  76543210
  7  bit  0
  ||||||||
---- ----
  ||||||++- Base nametable address
VPHB SINN
  ||||||    (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
  |||| ||||
  |||||+--- VRAM address increment per CPU read/write of PPUDATA
  |||| ||++- Base nametable address
  |||||    (0: increment by 1, going across; 1: increment by 32, going down)
  |||| ||    (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
  ||||+---- Sprite pattern table address for 8x8 sprites
  |||| |+--- VRAM address increment per CPU read/write of PPUDATA
  ||||     (0: $0000; 1: $1000; ignored in 8x16 mode)
  |||| |    (0: add 1, going across; 1: add 32, going down)
  |||+----- Background pattern table address (0: $0000; 1: $1000)
  |||| +---- Sprite pattern table address for 8x8 sprites
  ||+------ Sprite size (0: 8x8; 1: 8x16)
  ||||       (0: $0000; 1: $1000; ignored in 8x16 mode)
  |+------- PPU master/slave select (has no effect on the NES)
  |||+------ Background pattern table address (0: $0000; 1: $1000)
  +-------- Generate an [[NMI]] at the start of the
  ||+------- Sprite size (0: 8x8 pixels; 1: 8x16 pixels – see [[PPU OAM#Byte 1]])
          [[wikipedia:Vertical blanking interval|vertical blanking interval]] (0: off; 1: on)
  |+-------- PPU master/slave select
|          (0: read backdrop from EXT pins; 1: output color on EXT pins)
  +--------- Generate an [[NMI]] at the start of the
            [[wikipedia:Vertical blanking interval|vertical blanking interval]] (0: off; 1: on)


Equivalently, bits 0 and 1 are the most significant bit of the scrolling coordinates (see [[PPU_nametables|Nametables]] and [[#Scroll ($2005) >> write x2|PPU scroll]]):
Equivalently, bits 1 and 0 are the most significant bit of the scrolling coordinates (see [[PPU_nametables|Nametables]] and [[#PPUSCROLL|PPUSCROLL]]):
  76543210
  7  bit  0
      ||
---- ----
      |+- 1: Add 256 to the X scroll position
.... ..YX
      +-- 1: Add 240 to the Y scroll position
        ||
        |+- 1: Add 256 to the X scroll position
        +-- 1: Add 240 to the Y scroll position


==== Note ====
Another way of seeing the explanation above is that when you reach the end of a nametable, you must switch to the next one, hence, changing the nametable address.
Another way of seeing the explanation above is that when you reach the end of a nametable, you must switch to the next one, hence, changing the nametable address.


After power/reset, writes to this register are ignored for about 30000 cycles.
[[PPU power up state|After power/reset]], writes to this register are ignored for about 30,000 cycles.
 
If the PPU is currently in vertical blank, and the [[#PPUSTATUS|PPUSTATUS]] ($2002) vblank flag is still set (1), changing the NMI flag in bit 7 of $2000 from 0 to 1 will immediately generate an NMI.
This can result in graphical errors (most likely a misplaced scroll) if the NMI routine is executed too late in the blanking period to finish on time.
To avoid this problem it is prudent to read $2002 immediately to clear the vblank flag before writing $2000 to enable NMI.
 
For more explanation of sprite size, see: [[Sprite size]]
 
==== Master/slave mode and the EXT pins ====
When bit 6 of PPUCTRL is clear (the usual case), the PPU gets the [[PPU_palettes|palette index]] for the background color from the EXT pins. The stock NES grounds these pins, making palette index 0 the background color as expected. A secondary picture generator connected to the EXT pins would be able to replace the background with a different image using colors from the background palette, which could be used e.g. to implement parallax scrolling.
 
Setting bit 6 causes the PPU to output the lower four bits of the palette memory index on the EXT pins for each pixel (in addition to normal image drawing) – since only four bits are output, background and sprite pixels can't normally be distinguished this way. As the EXT pins are grounded on an unmodified NES, setting bit 6 is discouraged as it could potentially damage the chip whenever it outputs a non-zero pixel value (due to it effectively shorting Vcc and GND together). Looking at the relevant circuitry in [[Visual 2C02]], it appears that the [[PPU palettes|background palette hack]] would not be functional for output from the EXT pins; they would always output index 0 for the background color.
 
==== Bit 0 race condition ====
Be very careful when writing to this register outside vertical blanking if you are using vertical mirroring (horizontal arrangement) or 4-screen VRAM.
For specific CPU-PPU alignments, [//forums.nesdev.org/viewtopic.php?p=112424#p112424 a write that starts] on [[PPU scrolling#At dot 257 of each scanline|dot 257]] will cause only the next scanline to be erroneously drawn from the left nametable.
This can cause a visible glitch, and it can also interfere with sprite 0 hit for that scanline (by being drawn with the wrong background).


=== Mask ($2001) > write ===
The glitch has no effect in horizontal or one-screen mirroring.
Only writes that start on dot 257 and continue through dot 258 can cause this glitch: any other horizontal timing is safe.
The glitch specifically writes the value of open bus to the register, which will almost always be the upper byte of the address. Writing to this register or the mirror of this register at $2100 according to the desired nametable appears to be a [//forums.nesdev.org/viewtopic.php?p=230434#p230434 functional workaround].


Is often referred as PPUMASK.
This produces an occasionally [[Game bugs|visible glitch]] in ''Super Mario Bros.'' when the program writes to PPUCTRL at the end of game logic.
It appears to be turning NMI off during game logic and then turning NMI back on once the game logic has finished in order to prevent the NMI handler from being called again before the game logic finishes.
Another workaround is to use a software flag to prevent NMI reentry, instead of using the PPU's NMI enable.


This register controls screen enable, masking, and intensity.
=== <span id="PPUMASK"><span id="Reg2001">Mask ($2001) > write</span></span> ===
Write $00 here if you want to turn rendering off so that you can update the pattern tables or nametables outside of vertical blanking.
Write $1E when you're done to turn rendering back on.
The other bits do special effects with the colors.
76543210
||||||||
|||||||+- Grayscale (0: normal color; 1: produce a monochrome display)
||||||+-- 1: Show background in leftmost 8 pixels of screen; 0: Hide
|||||+--- 1: Show sprites in leftmost 8 pixels of screen; 0: Hide
||||+---- 1: Show background
|||+----- 1: Show sprites
||+------ Intensify reds (and darken other colors)
|+------- Intensify greens (and darken other colors)
+-------- Intensify blues (and darken other colors)


When grayscale is turned on, the PPU ignores the lower nibble of each palette entry.
* Common name: '''PPUMASK'''
This causes a bitwise AND with $30 on any value read from PPU $3F00-$3FFF, both on the display and through PPUDATA ($2007).
* Description: PPU mask register
* Access: write


Hiding the leftmost 8 pixels is often done to cover [[mirroring]] artifacts when done horizontally.
This register controls the rendering of sprites and backgrounds, as well as colour effects.
Sprite 0 hit does not trigger in any area where the background or sprites are hidden.
If both the background and sprites are hidden, the PPU enters "forced blank" state, where it stops rendering and releases control of the address and data bus.


[[NTSC video]] describes how bits D7-D5 (the "emphasis" bits or the "tint" bits) work on NTSC and PAL PPUs.
7  bit  0
Each bit's color have been confirmed on an NES; some older documents have them wrong.
---- ----
'''Caution:''' The RGB PPU (used on PlayChoice, Famicom Titler, and a couple Japanese TVs) treats the tint bits differently: instead of darkening other RGB components, it forces one RGB component to maximum brightness.
BGRs bMmG
A few games, which set all three tint bits to darken all colors, are unplayable on these PPUs.
|||| ||||
In either case, the tint bits are applied after grayscale, which means they still tint the gray image.
|||| |||+- Greyscale (0: normal color, 1: produce a greyscale display)
|||| ||+-- 1: Show background in leftmost 8 pixels of screen, 0: Hide
|||| |+--- 1: Show sprites in leftmost 8 pixels of screen, 0: Hide
|||| +---- 1: Show background
|||+------ 1: Show sprites
||+------- Emphasize red (green on PAL/Dendy)
|+-------- Emphasize green (red on PAL/Dendy)
+--------- Emphasize blue


=== Status ($2002) < read ===
==== Render Control ====


Is often referred as PPUSTATUS.
* Bits 3 and 4 enable the rendering of background and sprites, respectively.
* Bits 1 and 2 enable rendering of the background and sprites in the leftmost 8 pixel columns. Setting these bits to 0 will mask these columns, which is often useful in horizontal scrolling situations where you want partial sprites or tiles to scroll in from the left.
* A value of $1E or %00011110 enables all rendering, with no color effects. A value of $00 or %00000000 disables all rendering. It is usually best practice to write this register only during vblank, to prevent partial-frame visual artifacts.
* If either of bits 3 or 4 is enabled, at any time outside of the vblank interval the PPU will be making continual use to the PPU address and data bus to fetch tiles to render, as well as internally fetching sprite data from the OAM. If you wish to make changes to PPU memory outside of vblank (via '''$2007'''), you must set ''both'' of these bits to 0 to disable rendering and prevent conflicts.
* Disabling rendering (clear both bits 3 and 4) during a visible part of the frame can be problematic. It can cause a corruption of the sprite state, which will display incorrect sprite data on the next frame. (See: [[Errata#OAM and Sprites|Errata]]) It is, however, perfectly fine to mask sprites but leave the background on (set bit 3, clear bit 4) at any time in the frame.
* Sprite 0 hit does not trigger in any area where the background or sprites are hidden.
 
==== Color Control ====
 
* Bit 0 controls a greyscale mode, which causes the palette to use only the colors from the grey column: $00, $10, $20, $30. This is implemented as a bitwise AND with $30 on any value read from PPU $3F00-$3FFF, both on the display and through [[#PPUDATA|PPUDATA]]. Writes to the palette through [[#PPUDATA|PPUDATA]] are not affected. Also note that black colours like $0F will be replaced by a non-black grey $00.
* Bits 5, 6 and 7 control a color "emphasis" or "tint" effect.  See [[Colour emphasis]] for details.  Note that the emphasis bits are applied independently of bit 0, so they will still tint the color of the grey image.
 
=== <span id="PPUSTATUS"><span id="Reg2002">Status ($2002) < read</span></span> ===
 
* Common name: '''PPUSTATUS'''
* Description: PPU status register
* Access: read


This register reflects the state of various functions inside the PPU.
This register reflects the state of various functions inside the PPU.
It is often used for determining timing.
It is often used for determining timing.
To determine when the PPU has reached a given pixel of the screen, put an opaque pixel of sprite 0 there.
<span id="Sprite_0">To determine when the PPU has reached a given pixel of the screen, put an opaque (non-transparent) pixel of sprite 0 there.</span>
 
 
{{mbox
| type = warning
| text = <font size=+1>Do not read this address to wait for exactly one vertical redraw! On NTSC it will sometimes give false negatives, and on Dendy on some reboots it will always give false negatives.</font> Additionally, on the [[PlayChoice 10]], the Z80 supervisor will reject the game if the game leaves NMIs disabled for too long.
<hr>
Instead, use [[NMI thread|NMI]] for timing.}}


  76543210
  7  bit  0
  ||||||||
---- ----
  |||+++++- Least significant bits previously written into a PPU register
VSO. ....
|||      (due to register not being updated for this address)
  |||| ||||
  ||+------ Sprite overflow. The PPU can handle only eight sprites on one
  |||+-++++- [[Open_bus_behavior#PPU_open_bus|PPU open bus]]. Returns stale PPU bus contents.
||        scanline and sets this bit if it starts dropping sprites.
  ||+------- Sprite overflow. The intent was for this flag to be set
  ||       Normally, this triggers when there are 9 sprites on a scanline,
  ||         whenever more than eight sprites appear on a scanline, but a
  ||       but the actual behavior is significantly more complicated.
  ||         hardware bug causes the actual behavior to be more complicated
  |+------- Sprite 0 Hit.  Set when a nonzero pixel of sprite 0 'hits'
||        and generate false positives as well as false negatives; see
  |         a nonzero background pixel.  Used for raster timing.
||        [[PPU sprite evaluation]]. This flag is set during sprite
  +-------- Vertical blank has started (0: not in VBLANK; 1: in VBLANK)
||        evaluation and cleared at dot 1 (the second dot) of the
||        pre-render line.
  |+-------- [[PPU_OAM#Sprite_zero_hits|Sprite 0 Hit]].  Set when a nonzero pixel of sprite 0 overlaps
  |         a nonzero background pixel; cleared at dot 1 of the pre-render
|          line.  Used for raster timing.
  +--------- Vertical blank has started (0: not in vblank; 1: in vblank).
            Set at dot 1 of line 241 (the line *after* the post-render
            line); cleared after reading $2002 and at dot 1 of the
            pre-render line.


==== Notes ====
==== Notes ====
* Reading the status register will clear D7 mentioned above and also the address latch used by [[#Scroll ($2005) >> write x2|PPUSCROLL]] and [[#Address ($2006) >> write x2|PPUADDR]].
* Reading the status register will clear bit 7 mentioned above and also the address latch used by [[#PPUSCROLL|PPUSCROLL]] and [[#PPUADDR|PPUADDR]]. It does not clear the sprite 0 hit or overflow bit.
* Once the sprite 0 hit flag is set, it will not be cleared until the end of the next vertical blank.  If attempting to use this flag for raster timing, it is important to ensure that the sprite 0 hit check happens outside of vertical blank, otherwise the CPU will "leak" through and the check will fail.  The easiest way to do this is to place an earlier check for bit 6 = 0, which will wait for the pre-render scanline to begin.
* If using sprite 0 hit to make a bottom scroll bar below a vertically scrolling or freely scrolling playfield, be careful to ensure that the tile in the playfield behind sprite 0 is opaque.
* Sprite 0 hit is not detected at x=255, nor is it detected at x=0 through 7 if the background or sprites are hidden in this area.
* See: [[PPU rendering]] for more information on the timing of setting and clearing the flags.
* Some [[Vs. System]] PPUs return a constant value in bits 4–0 that the game checks.
* '''Race Condition Warning:''' Reading PPUSTATUS within two cycles of the start of vertical blank will return 0 in bit 7 but clear the latch anyway, causing NMI to not occur that frame. See [[NMI]] and [[PPU_frame_timing]] for details.


* '''Caution:''' Reading PPUSTATUS at the exact start of vertical blank will return a 0 in D7 but clear the latch anyway, causing the program to miss frames. See [[NMI]] for details.
=== <span id="OAMADDR"><span id="Reg2003">OAM address ($2003) > write</span></span> ===


=== OAM address ($2003) > write ===
* Common name: '''OAMADDR'''
* Description: OAM address port
* Access: write


Is often referred as OAMADDR.
Write the address of [[PPU OAM|OAM]] you want to access here. Most games just write $00 here and then use [[#OAMDMA|OAMDMA]]. (DMA is implemented in the 2A03/7 chip and works by repeatedly writing to [[#OAMDATA|OAMDATA]])


==== Values during rendering ====


Write the address of [[PPU_OAM |OAM]] you want to access here.  Most games just write $00 here and then use OAM_DMA ($4014).
OAMADDR is set to 0 during each of ticks 257–320 (the sprite tile loading interval) of the pre-render and visible scanlines. This also means that at the end of a normal complete rendered frame, OAMADDR will always have returned to 0.


This register also seems to affect Sprite 0 Hit, though it is not yet understood exactly how it does. The upper 5 bits of this register seem to select which SPR-RAM data is used for sprites 0 and 1 (instead of the first 8 bytes of SPR-RAM), though actual behavior varies between resets.
If rendering is enabled mid-scanline<ref name="OAMADDR Clarification"/>, there are further consequences of an OAMADDR that was not set to 0 before OAM sprite evaluation begins at tick 65 of the visible scanline. The value of OAMADDR at this tick determines the starting address for sprite evaluation for this scanline, which can cause the sprite at OAMADDR to be treated as it was sprite 0, both for [[sprite-0 hit]] and priority. If OAMADDR is unaligned and does not point to the Y position (first byte) of an OAM entry, then whatever it points to (tile index, attribute, or X coordinate) will be reinterpreted as a Y position, and the following bytes will be similarly reinterpreted. No more sprites will be found once the end of OAM is reached, effectively hiding any sprites before the starting OAMADDR.


=== OAM data ($2004) <> read/write ===
==== OAMADDR precautions ====


OAM data port, often referred as OAMDATA.
On the 2C02G, writes to OAMADDR corrupt OAM. The exact corruption isn't fully described, but this usually seems to copy sprites 8 and 9 (address $20) over the 8-byte row at the target address. The source address for this copy seems to come from the previous value on the CPU BUS (most often $20 from the $2003 operand).<ref name="OAMADDR Clarification">[//forums.nesdev.org/viewtopic.php?p=285674#p285674 OAMDATA $2003 corruption clarification?] - forum thread</ref><ref name = "OAMglitch">[//forums.nesdev.org/viewtopic.php?t=10189 Manual OAM write glitchyness] thread by blargg</ref> There may be other possible behaviors as well. This can then be worked around by writing all 256 bytes of OAM, though due to the limited time before [[PPU OAM#Dynamic RAM decay|OAM decay]] will begin this should normally be done through OAMDMA.


Write OAM data here. Writes will increment [[#OAM address ($2003) > write|OAMADDR]] after the write; reads during vertical or forced blanking return the value from OAM at that address but do not increment.
It is also the case that if OAMADDR is not less than eight when rendering starts, the eight bytes starting at <tt>OAMADDR & 0xF8</tt> are copied to the first eight bytes of OAM; it seems likely that this is related. On the Dendy, the latter bug is required for 2C02 compatibility.


Most games access this register through $4014 instead. Reading OAMDATA while the PPU is rendering will expose internal OAM accesses during sprite evaluation and loading; Micro Machines does this.
It is known that in the 2C03, 2C04, 2C05<ref name="noOAMglitch">[//forums.nesdev.org/viewtopic.php?p=179676#p179676 Writes to $2003 appear to not cause OAM corruption] post by lidnariq</ref>, and 2C07, OAMADDR works as intended. It is not known whether this bug is present in all revisions of the 2C02.


Note that reading OAM data isn't reliable in many cases, even when rendering is disabled. It is best to treat this as a write-only register.
=== <span id="OAMDATA"><span id="Reg2004">OAM data ($2004) <> read/write</span></span> ===


=== Scroll ($2005) >> write x2 ===
* Common name: '''OAMDATA'''
* Description: OAM data port
* Access: read, write


Is often referred as PPUSCROLL.
Write OAM data here. Writes will increment [[#OAMADDR|OAMADDR]] after the write; reads do not. Reads during vertical or forced blanking return the value from OAM at that address.


This register is used to tell the PPU which pixel of the nametable selected through [[#Controller ($2000) > write|PPUCTRL]] should be at the top left corner of the rendered screen. Typically, this register is written to during VBlank, so that the next frame starts rendering from the desired location, but it can also be modified during rendering in order to split the screen. Changes made to the vertical scroll during rendering will only take effect on the next frame.
'''Do not write directly to this register in most cases.''' Because changes to OAM should normally be made only during vblank, writing through OAMDATA is only effective for partial updates (it is too slow), and as described above, partial writes cause corruption. Most games will use the DMA feature through [[#OAMDMA|OAMDMA]] instead.


After reading [[#Status ($2002) < read|PPUSTATUS]] to reset the address latch, write the horizontal and vertical scroll offsets here just before turning on the screen:
* Reading OAMDATA while the PPU is rendering will expose internal OAM accesses during sprite evaluation and loading; ''Micro Machines'' does this.
* Writes to OAMDATA during rendering (on the pre-render line and the visible lines 0–239, provided either sprite or background rendering is enabled) do not modify values in OAM, but do perform a glitchy increment of [[#OAMADDR|OAMADDR]], bumping only the high 6 bits (i.e., it bumps the ''[n]'' value in [[PPU sprite evaluation]] – it's plausible that it could bump the low bits instead depending on the current status of sprite evaluation). This extends to DMA transfers via [[#OAMDMA|OAMDMA]], since that uses writes to $2004. For emulation purposes, it is probably best to completely ignore writes during rendering.
* It used to be thought that reading from this register wasn't reliable<ref>[//forums.nesdev.org/viewtopic.php?t=6424 $2004 reading reliable?] thread by blargg</ref>, however more recent evidence seems to suggest that this is solely due to corruption by [[#OAMADDR|OAMADDR]] writes.
* In the oldest instantiations of the PPU, as found on earlier Famicoms and NESes, this register is not readable<ref>[//forums.nesdev.org/viewtopic.php?p=62137#p62137 $2004 not readable on early revisions] reply by jsr</ref>. The readability was added on the RP2C02G, found on most NESes and later Famicoms.<ref>[//forums.nesdev.org/viewtopic.php?p=150926#p150926 hardware revisions and $2004 reads] reply by Great Hierophant</ref>
* In the 2C07, sprite evaluation can ''never'' be fully disabled, and will always start 20 scanlines after the start of vblank<ref>[//forums.nesdev.org/viewtopic.php?t=11041 2C07 PPU sprite evaluation notes] thread by thefox</ref> (same as when the prerender scanline would have been on the 2C02). As such, you must upload anything to OAM that you intend to within the first 20 scanlines after the 2C07 signals vertical blanking.
 
=== <span id="PPUSCROLL"><span id="Reg2005">Scroll ($2005) >> write x2</span></span> ===
 
* Common name: '''PPUSCROLL'''
* Description: PPU scrolling position register
* Access: write twice
 
This register is used to change the [[PPU scrolling|scroll position]], telling the PPU which pixel of the nametable selected through [[#PPUCTRL|PPUCTRL]] should be at the top left corner of the rendered screen. PPUSCROLL takes two writes: the first is the X scroll and the second is the Y scroll. Whether this is the first or second write is tracked internally by the [[#Internal_registers|w register]], which is shared with [[#PPUADDR|PPUADDR]]. Typically, this register is written to during vertical blanking to make the next frame start rendering from the desired location, but it can also be modified during rendering in order to split the screen. Changes made to the vertical scroll during rendering will only take effect on the next frame. Together with the nametable bits in PPUCTRL, the scroll can be thought of as 9 bits per component, and PPUCTRL must be updated along with PPUSCROLL to fully specify the scroll position.
 
{{mbox
| type = warning
| text = <font size=+1>The PPU scroll registers [[PPU_scrolling#PPU_internal_registers|share internal state]] with the PPU address registers. Because of this, you must write PPUSCROLL and the nametable bits in PPUCTRL after you're done writing to PPUADDR.}}
 
After reading [[#PPUSTATUS|PPUSTATUS]] to clear [[#Internal_registers|w (the write latch)]], write the horizontal and vertical scroll offsets to PPUSCROLL just before turning on the screen:
 
  ; Set the high bit of X and Y scroll.
  lda ppuctrl_value
  ora current_nametable
  sta PPUCTRL
  ; Set the low 8 bits of X and Y scroll.
   bit PPUSTATUS
   bit PPUSTATUS
  ; possibly other code goes here
   lda cam_position_x
   lda cam_position_x
   sta PPUSCROLL
   sta PPUSCROLL
   lda cam_position_y
   lda cam_position_y
   sta PPUSCROLL
   sta PPUSCROLL
Horizontal offsets range from 0 to 255. "Normal" vertical offsets range from 0 to 239, while values of 240 to 255 are treated as -16 through -1 in a way, but tile data is incorrectly fetched from the attribute table.


By writing different values here across several frames and modifying the nametables accordingly one can achieve the effect of a camera panning over a large background.
Horizontal offsets range from 0 to 255. "Normal" vertical offsets range from 0 to 239, while values of 240 to 255 cause the attributes data at the end of the current nametable to be used incorrectly as tile data. The PPU normally skips from 239 to 0 of the next nametable automatically, so these "invalid" scroll positions only occur if explicitly written.
 
By changing the scroll values here across several frames and writing tiles to newly revealed areas of the nametables, one can achieve the effect of a camera panning over a large background.


=== Address ($2006) >> write x2 ===
=== <span id="PPUADDR"><span id="Reg2006">Address ($2006) >> write x2</span></span> ===


Is often referred as PPUADDR.
* Common name: '''PPUADDR'''
* Description: PPU address register
* Access: write twice


Because the CPU and the PPU are on separate buses, neither has direct access to the other's memory.
Because the CPU and the PPU are on separate buses, neither has direct access to the other's memory. The CPU writes to VRAM through a pair of registers on the PPU by first loading an address into [[#PPUADDR|PPUADDR]] and then it writing data repeatedly to [[#PPUDATA|PPUDATA]]. The 16-bit address is written to PPUADDR one byte at a time, upper byte first. Whether this is the first or second write is tracked internally by the [[#Internal_registers|w register]], which is shared with [[#PPUSCROLL|PPUSCROLL]].
The CPU writes to VRAM through a pair of registers on the PPU.
First it loads an address into PPUADDR, and then it writes repeatedly to PPUDATA to fill VRAM.


After reading [[#Status ($2002) < read|PPUSTATUS]] to reset the address latch, write the 16-bit address of VRAM you want to access here, upper byte first.
After reading [[#PPUSTATUS|PPUSTATUS]] to clear [[#Internal_registers|w (the write latch)]], write the 16-bit address of VRAM you want to access here, upper byte first.
For example, to set the VRAM address to $2108:
For example, to set the VRAM address to $2108:
   lda #$21
   lda #$21
   sta PPUADDR
   sta PPUADDR
Line 146: Line 285:
   sta PPUADDR
   sta PPUADDR


Valid addresses are $0000-$3FFF; higher addresses will be [[mirroring|mirrored]] down.
Valid addresses are $0000–$3FFF; higher addresses will be [[mirroring|mirrored]] down.
 
==== Note ====
Access to [[#PPUSCROLL|PPUSCROLL]] and [[#PPUADDR|PPUADDR]] during screen refresh produces interesting raster effects; the starting position of each scanline can be set to any pixel position in nametable memory. For more information, see [[PPU scrolling]].
 
==== Palette corruption ====
In specific circumstances, entries of the PPU's palette can be corrupted. It's unclear exactly how or why this happens, but all revisions of the NTSC PPU seem to be at least somewhat susceptible.<ref>[//forums.nesdev.org/viewtopic.php?t=23209 Problem with palette discoloration when PPU is turned off during rendering] thread by N·K</ref>
 
When done writing to palette memory, the workaround is to always
# Update the address, if necessary, so that it's pointing at $3F00, $3F10, $3F20, or any other mirror.
# Only then change the address to point outside of palette memory.
 
A code fragment to implement this workaround is present in vast numbers of games:<ref>[//forums.nesdev.org/viewtopic.php?p=280899#p280899 Weird PPU writes] thread by Fiskbit</ref>
 
  lda #$3F
  sta PPUADDR
  lda #0
  sta PPUADDR
  sta PPUADDR
  sta PPUADDR
 
==== Bus conflict ====
During raster effects, if the second write to PPUADDR happens at specific times, at most one axis of scrolling will be set to the bitwise AND of the written value and the current value. The only safe time to finish the second write is during blanking; see [[PPU scrolling]] for more specific timing. [//forums.nesdev.org/viewtopic.php?p=230391#p230391]


==== note ====
=== <span id="PPUDATA"><span id="Reg2007">Data ($2007) <> read/write</span></span> ===
Access to [[#Scroll ($2005) >> write x2|PPUSCROLL]] and PPUADDR during screen refresh produces interesting raster effects; the starting position of each scanline can be set to any pixel position in nametable memory. For more information, see "The Skinny on NES Scrolling" by loopy, available from [http://nesdev.parodius.com/ the main site], and [http://nesdev.parodius.com/bbs/viewtopic.php?p=64111#64111 tokumaru's sample code on the BBS].


''' Editor's note:''' Last comment about external page should be re-directed to the getting started section instead.
* Common name: '''PPUDATA'''
* Description: PPU data port
* Access: read, write


=== Data ($2007) <> read/write ===
VRAM read/write data register. After access, the video memory address will increment by an amount determined by bit 2 of $2000.


Is often referred as PPUDATA.
When the screen is turned off by disabling the background/sprite rendering flag with the [[#PPUMASK|PPUMASK]] or during vertical blank, you can read or write data from VRAM through this port. Since accessing this register increments the VRAM address, it should not be accessed outside vertical or forced blanking because it will cause graphical glitches, and if writing, write to an unpredictable address in VRAM. However, two games are known to [[Reading 2007 during rendering|read from PPUDATA during rendering]]: see [[Tricky-to-emulate games]].


VRAM data register.
VRAM reading and writing shares the same internal address register that rendering uses. So after loading data into video memory, the program should reload the scroll position afterwards with [[#PPUSCROLL|PPUSCROLL]] and [[#PPUCTRL|PPUCTRL]] (bits 1…0) writes in order to avoid wrong scrolling.


When the screen is turned off by disabling the background/sprite rendering flag with the [[#Mask ($2001) >> write|PPUMASK]] or during vertical blank, you can read or write data from VRAM through this port.
==== The PPUDATA read buffer (post-fetch) ====


==== Note ====
When reading PPUDATA while the VRAM address is in the range 0–$3EFF (i.e., before the palettes), the read will return the contents of an internal read buffer. This read buffer is updated on every PPUDATA read, but only '''after''' the previous contents have been returned to the CPU. This is because PPU bus reads are too slow and cannot complete in time to service the CPU read. Because of this, after the VRAM address has been set through PPUADDR, one should first read PPUDATA to prime the read buffer (ignoring the result) before then reading the desired data from it.
When reading while the VRAM address is in the range 0-$3EFF, the read will return the contents of an internal buffer. After the CPU reads, the PPU will then immediately read the byte at the current VRAM address into this internal buffer. Thus, after setting the VRAM address, one should first read this register and discard the result. This behavior doesn't occur when the VRAM address is in the $3F00-$3FFF palette range; reads come directly from palette RAM and don't affect the internal buffer.
 
Some PPUs support reading palette data from $3F00-3FFF. These reads work differently, as palette RAM is a separate memory space internal to the PPU that is overlaid onto the PPU address space. The referenced 6-bit palette data is returned immediately instead of going to the internal read buffer, and hence no priming read is required. Simultaneously, the PPU also performs a normal read from the PPU memory at the specified address, "underneath" the palette data, and the result of this read goes into the read buffer as normal. The old contents of the read buffer are discarded when reading palettes, but by changing the address to point outside palette RAM and performing one read, the contents of this shadowed memory ([[PPU memory map|usually mirrored nametables]]) can be accessed. On PPUs that do not support reading palette RAM, this memory range behaves the same as the rest of PPU memory.
 
Note that the internal read buffer is updated '''only''' on PPUDATA reads. It is not affected by other PPU processes such as rendering, and it maintains its value indefinitely until the next read.
 
==== Read conflict with DPCM samples ====
 
If currently playing DPCM samples, there is a chance that an interruption from the APU's sample fetch will cause an extra read cycle if it happened at the same time as an instruction that reads $2007. This will cause an extra increment and a byte to be skipped over, corrupting the data you were trying to read. See: [[APU_DMC#Conflict with controller and PPU read|APU DMC]]
 
=== <span id="Reg4014"><span id="OAMDMA">OAM DMA ($4014) > write</span></span> ===
 
* Common name: '''OAMDMA'''
* Description: OAM DMA register (high byte)
* Access: write
 
This port is located on the CPU. Writing $XX will upload 256 bytes of data from CPU page $XX00–$XXFF to the internal PPU OAM. This page is typically located in internal RAM, commonly $0200–$02FF, but cartridge RAM or ROM can be used as well.
 
* The CPU is suspended during the transfer, which will take 513 or 514 cycles after the $4014 write tick. (1 wait state cycle while waiting for writes to complete, +1 if on a put cycle, then 256 alternating get/put cycles. See [[DMA]] for more information.)
* The OAM DMA is the only effective method for initializing all 256 bytes of OAM. Because of the decay of OAM's dynamic RAM when rendering is disabled, the initialization should take place within vblank. Writes through [[#OAMDATA|OAMDATA]] are generally too slow for this task.
* The [[DMA]] transfer will begin at the current OAM write address. It is common practice to initialize it to 0 with a write to [[#OAMADDR|OAMADDR]] before the DMA transfer. Different starting addresses can be used for a simple OAM cycling technique, to alleviate sprite priority conflicts by flickering. If using this technique, after the DMA [[#OAMADDR|OAMADDR]] should be set to 0 before the end of vblank to prevent potential OAM corruption (see [[errata]]). However, due to OAMADDR writes also having a "corruption" effect,<ref name = "OAMglitch" /> this technique is not recommended.
 
== Internal registers ==
 
The PPU also has 4 internal registers, described in detail on [[PPU scrolling#PPU internal registers|PPU scrolling]]:
* '''v''': During rendering, used for the scroll position. Outside of rendering, used as the current VRAM address.
* '''t''': During rendering, specifies the starting coarse-x scroll for the next scanline and the starting y scroll for the screen. Outside of rendering, holds the scroll or VRAM address before transferring it to v.
* '''x''': The fine-x position of the current scroll, used during rendering alongside v.
* '''w''': Toggles on each write to either [[#PPUSCROLL|PPUSCROLL]] or [[#PPUADDR|PPUADDR]], indicating whether this is the first or second write. Clears on reads of [[#PPUSTATUS|PPUSTATUS]]. Sometimes called the 'write latch' or 'write toggle'.


Since accessing this register increments the VRAM address, it should not be accessed outside vblank when rendering is enabled, because it will cause graphical glitches, and if writing, write to an unpredictable address in VRAM.
== References ==
<references />

Revision as of 13:56, 13 December 2023

The PPU exposes eight memory-mapped registers to the CPU. These nominally sit at $2000 through $2007 in the CPU's address space, but because their addresses are incompletely decoded, they're mirrored in every 8 bytes from $2008 through $3FFF. For example, a write to $3456 is the same as a write to $2006.

After power-on and reset, many of the PPU's registers are not immediately usable until enough time has passed. See PPU power up state and Init code for details.

Summary

Common Name Address Bits Notes
PPUCTRL $2000 VPHB SINN NMI enable (V), PPU master/slave (P), sprite height (H), background tile select (B), sprite tile select (S), increment mode (I), nametable select (NN)
PPUMASK $2001 BGRs bMmG color emphasis (BGR), sprite enable (s), background enable (b), sprite left column enable (M), background left column enable (m), greyscale (G)
PPUSTATUS $2002 VSO- ---- vblank (V), sprite 0 hit (S), sprite overflow (O); read resets write pair for $2005/$2006
OAMADDR $2003 aaaa aaaa OAM read/write address
OAMDATA $2004 dddd dddd OAM data read/write
PPUSCROLL $2005 xxxx xxxx fine scroll position (two writes: X scroll, Y scroll)
PPUADDR $2006 aaaa aaaa PPU read/write address (two writes: most significant byte, least significant byte)
PPUDATA $2007 dddd dddd PPU data read/write
OAMDMA $4014 aaaa aaaa OAM DMA high address

MMIO registers

The PPU has an internal data bus that it uses for communication with the CPU. This bus, called _io_db in Visual 2C02 and PPUGenLatch in FCEUX,[1] behaves as an 8-bit dynamic latch due to capacitance of very long traces that run to various parts of the PPU. Writing any value to any PPU port, even to the nominally read-only PPUSTATUS, will fill this latch. Reading any readable port (PPUSTATUS, OAMDATA, or PPUDATA) also fills the latch with the bits read. Reading a nominally "write-only" register returns the latch's current value, as do the unused bits of PPUSTATUS. This value begins to decay after a frame or so, faster once the PPU has warmed up, and it is likely that values with alternating bit patterns (such as $55 or $AA) will decay faster.[2]

Controller ($2000) > write

  • Common name: PPUCTRL
  • Description: PPU control register
  • Access: write

Various flags controlling PPU operation

7  bit  0
---- ----
VPHB SINN
|||| ||||
|||| ||++- Base nametable address
|||| ||    (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
|||| |+--- VRAM address increment per CPU read/write of PPUDATA
|||| |     (0: add 1, going across; 1: add 32, going down)
|||| +---- Sprite pattern table address for 8x8 sprites
||||       (0: $0000; 1: $1000; ignored in 8x16 mode)
|||+------ Background pattern table address (0: $0000; 1: $1000)
||+------- Sprite size (0: 8x8 pixels; 1: 8x16 pixels – see PPU OAM#Byte 1)
|+-------- PPU master/slave select
|          (0: read backdrop from EXT pins; 1: output color on EXT pins)
+--------- Generate an NMI at the start of the
           vertical blanking interval (0: off; 1: on)

Equivalently, bits 1 and 0 are the most significant bit of the scrolling coordinates (see Nametables and PPUSCROLL):

7  bit  0
---- ----
.... ..YX
       ||
       |+- 1: Add 256 to the X scroll position
       +-- 1: Add 240 to the Y scroll position

Another way of seeing the explanation above is that when you reach the end of a nametable, you must switch to the next one, hence, changing the nametable address.

After power/reset, writes to this register are ignored for about 30,000 cycles.

If the PPU is currently in vertical blank, and the PPUSTATUS ($2002) vblank flag is still set (1), changing the NMI flag in bit 7 of $2000 from 0 to 1 will immediately generate an NMI. This can result in graphical errors (most likely a misplaced scroll) if the NMI routine is executed too late in the blanking period to finish on time. To avoid this problem it is prudent to read $2002 immediately to clear the vblank flag before writing $2000 to enable NMI.

For more explanation of sprite size, see: Sprite size

Master/slave mode and the EXT pins

When bit 6 of PPUCTRL is clear (the usual case), the PPU gets the palette index for the background color from the EXT pins. The stock NES grounds these pins, making palette index 0 the background color as expected. A secondary picture generator connected to the EXT pins would be able to replace the background with a different image using colors from the background palette, which could be used e.g. to implement parallax scrolling.

Setting bit 6 causes the PPU to output the lower four bits of the palette memory index on the EXT pins for each pixel (in addition to normal image drawing) – since only four bits are output, background and sprite pixels can't normally be distinguished this way. As the EXT pins are grounded on an unmodified NES, setting bit 6 is discouraged as it could potentially damage the chip whenever it outputs a non-zero pixel value (due to it effectively shorting Vcc and GND together). Looking at the relevant circuitry in Visual 2C02, it appears that the background palette hack would not be functional for output from the EXT pins; they would always output index 0 for the background color.

Bit 0 race condition

Be very careful when writing to this register outside vertical blanking if you are using vertical mirroring (horizontal arrangement) or 4-screen VRAM. For specific CPU-PPU alignments, a write that starts on dot 257 will cause only the next scanline to be erroneously drawn from the left nametable. This can cause a visible glitch, and it can also interfere with sprite 0 hit for that scanline (by being drawn with the wrong background).

The glitch has no effect in horizontal or one-screen mirroring. Only writes that start on dot 257 and continue through dot 258 can cause this glitch: any other horizontal timing is safe. The glitch specifically writes the value of open bus to the register, which will almost always be the upper byte of the address. Writing to this register or the mirror of this register at $2100 according to the desired nametable appears to be a functional workaround.

This produces an occasionally visible glitch in Super Mario Bros. when the program writes to PPUCTRL at the end of game logic. It appears to be turning NMI off during game logic and then turning NMI back on once the game logic has finished in order to prevent the NMI handler from being called again before the game logic finishes. Another workaround is to use a software flag to prevent NMI reentry, instead of using the PPU's NMI enable.

Mask ($2001) > write

  • Common name: PPUMASK
  • Description: PPU mask register
  • Access: write

This register controls the rendering of sprites and backgrounds, as well as colour effects.

7  bit  0
---- ----
BGRs bMmG
|||| ||||
|||| |||+- Greyscale (0: normal color, 1: produce a greyscale display)
|||| ||+-- 1: Show background in leftmost 8 pixels of screen, 0: Hide
|||| |+--- 1: Show sprites in leftmost 8 pixels of screen, 0: Hide
|||| +---- 1: Show background
|||+------ 1: Show sprites
||+------- Emphasize red (green on PAL/Dendy)
|+-------- Emphasize green (red on PAL/Dendy)
+--------- Emphasize blue

Render Control

  • Bits 3 and 4 enable the rendering of background and sprites, respectively.
  • Bits 1 and 2 enable rendering of the background and sprites in the leftmost 8 pixel columns. Setting these bits to 0 will mask these columns, which is often useful in horizontal scrolling situations where you want partial sprites or tiles to scroll in from the left.
  • A value of $1E or %00011110 enables all rendering, with no color effects. A value of $00 or %00000000 disables all rendering. It is usually best practice to write this register only during vblank, to prevent partial-frame visual artifacts.
  • If either of bits 3 or 4 is enabled, at any time outside of the vblank interval the PPU will be making continual use to the PPU address and data bus to fetch tiles to render, as well as internally fetching sprite data from the OAM. If you wish to make changes to PPU memory outside of vblank (via $2007), you must set both of these bits to 0 to disable rendering and prevent conflicts.
  • Disabling rendering (clear both bits 3 and 4) during a visible part of the frame can be problematic. It can cause a corruption of the sprite state, which will display incorrect sprite data on the next frame. (See: Errata) It is, however, perfectly fine to mask sprites but leave the background on (set bit 3, clear bit 4) at any time in the frame.
  • Sprite 0 hit does not trigger in any area where the background or sprites are hidden.

Color Control

  • Bit 0 controls a greyscale mode, which causes the palette to use only the colors from the grey column: $00, $10, $20, $30. This is implemented as a bitwise AND with $30 on any value read from PPU $3F00-$3FFF, both on the display and through PPUDATA. Writes to the palette through PPUDATA are not affected. Also note that black colours like $0F will be replaced by a non-black grey $00.
  • Bits 5, 6 and 7 control a color "emphasis" or "tint" effect. See Colour emphasis for details. Note that the emphasis bits are applied independently of bit 0, so they will still tint the color of the grey image.

Status ($2002) < read

  • Common name: PPUSTATUS
  • Description: PPU status register
  • Access: read

This register reflects the state of various functions inside the PPU. It is often used for determining timing. To determine when the PPU has reached a given pixel of the screen, put an opaque (non-transparent) pixel of sprite 0 there.


7  bit  0
---- ----
VSO. ....
|||| ||||
|||+-++++- PPU open bus. Returns stale PPU bus contents.
||+------- Sprite overflow. The intent was for this flag to be set
||         whenever more than eight sprites appear on a scanline, but a
||         hardware bug causes the actual behavior to be more complicated
||         and generate false positives as well as false negatives; see
||         PPU sprite evaluation. This flag is set during sprite
||         evaluation and cleared at dot 1 (the second dot) of the
||         pre-render line.
|+-------- Sprite 0 Hit.  Set when a nonzero pixel of sprite 0 overlaps
|          a nonzero background pixel; cleared at dot 1 of the pre-render
|          line.  Used for raster timing.
+--------- Vertical blank has started (0: not in vblank; 1: in vblank).
           Set at dot 1 of line 241 (the line *after* the post-render
           line); cleared after reading $2002 and at dot 1 of the
           pre-render line.

Notes

  • Reading the status register will clear bit 7 mentioned above and also the address latch used by PPUSCROLL and PPUADDR. It does not clear the sprite 0 hit or overflow bit.
  • Once the sprite 0 hit flag is set, it will not be cleared until the end of the next vertical blank. If attempting to use this flag for raster timing, it is important to ensure that the sprite 0 hit check happens outside of vertical blank, otherwise the CPU will "leak" through and the check will fail. The easiest way to do this is to place an earlier check for bit 6 = 0, which will wait for the pre-render scanline to begin.
  • If using sprite 0 hit to make a bottom scroll bar below a vertically scrolling or freely scrolling playfield, be careful to ensure that the tile in the playfield behind sprite 0 is opaque.
  • Sprite 0 hit is not detected at x=255, nor is it detected at x=0 through 7 if the background or sprites are hidden in this area.
  • See: PPU rendering for more information on the timing of setting and clearing the flags.
  • Some Vs. System PPUs return a constant value in bits 4–0 that the game checks.
  • Race Condition Warning: Reading PPUSTATUS within two cycles of the start of vertical blank will return 0 in bit 7 but clear the latch anyway, causing NMI to not occur that frame. See NMI and PPU_frame_timing for details.

OAM address ($2003) > write

  • Common name: OAMADDR
  • Description: OAM address port
  • Access: write

Write the address of OAM you want to access here. Most games just write $00 here and then use OAMDMA. (DMA is implemented in the 2A03/7 chip and works by repeatedly writing to OAMDATA)

Values during rendering

OAMADDR is set to 0 during each of ticks 257–320 (the sprite tile loading interval) of the pre-render and visible scanlines. This also means that at the end of a normal complete rendered frame, OAMADDR will always have returned to 0.

If rendering is enabled mid-scanline[3], there are further consequences of an OAMADDR that was not set to 0 before OAM sprite evaluation begins at tick 65 of the visible scanline. The value of OAMADDR at this tick determines the starting address for sprite evaluation for this scanline, which can cause the sprite at OAMADDR to be treated as it was sprite 0, both for sprite-0 hit and priority. If OAMADDR is unaligned and does not point to the Y position (first byte) of an OAM entry, then whatever it points to (tile index, attribute, or X coordinate) will be reinterpreted as a Y position, and the following bytes will be similarly reinterpreted. No more sprites will be found once the end of OAM is reached, effectively hiding any sprites before the starting OAMADDR.

OAMADDR precautions

On the 2C02G, writes to OAMADDR corrupt OAM. The exact corruption isn't fully described, but this usually seems to copy sprites 8 and 9 (address $20) over the 8-byte row at the target address. The source address for this copy seems to come from the previous value on the CPU BUS (most often $20 from the $2003 operand).[3][4] There may be other possible behaviors as well. This can then be worked around by writing all 256 bytes of OAM, though due to the limited time before OAM decay will begin this should normally be done through OAMDMA.

It is also the case that if OAMADDR is not less than eight when rendering starts, the eight bytes starting at OAMADDR & 0xF8 are copied to the first eight bytes of OAM; it seems likely that this is related. On the Dendy, the latter bug is required for 2C02 compatibility.

It is known that in the 2C03, 2C04, 2C05[5], and 2C07, OAMADDR works as intended. It is not known whether this bug is present in all revisions of the 2C02.

OAM data ($2004) <> read/write

  • Common name: OAMDATA
  • Description: OAM data port
  • Access: read, write

Write OAM data here. Writes will increment OAMADDR after the write; reads do not. Reads during vertical or forced blanking return the value from OAM at that address.

Do not write directly to this register in most cases. Because changes to OAM should normally be made only during vblank, writing through OAMDATA is only effective for partial updates (it is too slow), and as described above, partial writes cause corruption. Most games will use the DMA feature through OAMDMA instead.

  • Reading OAMDATA while the PPU is rendering will expose internal OAM accesses during sprite evaluation and loading; Micro Machines does this.
  • Writes to OAMDATA during rendering (on the pre-render line and the visible lines 0–239, provided either sprite or background rendering is enabled) do not modify values in OAM, but do perform a glitchy increment of OAMADDR, bumping only the high 6 bits (i.e., it bumps the [n] value in PPU sprite evaluation – it's plausible that it could bump the low bits instead depending on the current status of sprite evaluation). This extends to DMA transfers via OAMDMA, since that uses writes to $2004. For emulation purposes, it is probably best to completely ignore writes during rendering.
  • It used to be thought that reading from this register wasn't reliable[6], however more recent evidence seems to suggest that this is solely due to corruption by OAMADDR writes.
  • In the oldest instantiations of the PPU, as found on earlier Famicoms and NESes, this register is not readable[7]. The readability was added on the RP2C02G, found on most NESes and later Famicoms.[8]
  • In the 2C07, sprite evaluation can never be fully disabled, and will always start 20 scanlines after the start of vblank[9] (same as when the prerender scanline would have been on the 2C02). As such, you must upload anything to OAM that you intend to within the first 20 scanlines after the 2C07 signals vertical blanking.

Scroll ($2005) >> write x2

  • Common name: PPUSCROLL
  • Description: PPU scrolling position register
  • Access: write twice

This register is used to change the scroll position, telling the PPU which pixel of the nametable selected through PPUCTRL should be at the top left corner of the rendered screen. PPUSCROLL takes two writes: the first is the X scroll and the second is the Y scroll. Whether this is the first or second write is tracked internally by the w register, which is shared with PPUADDR. Typically, this register is written to during vertical blanking to make the next frame start rendering from the desired location, but it can also be modified during rendering in order to split the screen. Changes made to the vertical scroll during rendering will only take effect on the next frame. Together with the nametable bits in PPUCTRL, the scroll can be thought of as 9 bits per component, and PPUCTRL must be updated along with PPUSCROLL to fully specify the scroll position.

After reading PPUSTATUS to clear w (the write latch), write the horizontal and vertical scroll offsets to PPUSCROLL just before turning on the screen:

 ; Set the high bit of X and Y scroll.
 lda ppuctrl_value
 ora current_nametable
 sta PPUCTRL

 ; Set the low 8 bits of X and Y scroll.
 bit PPUSTATUS
 lda cam_position_x
 sta PPUSCROLL
 lda cam_position_y
 sta PPUSCROLL

Horizontal offsets range from 0 to 255. "Normal" vertical offsets range from 0 to 239, while values of 240 to 255 cause the attributes data at the end of the current nametable to be used incorrectly as tile data. The PPU normally skips from 239 to 0 of the next nametable automatically, so these "invalid" scroll positions only occur if explicitly written.

By changing the scroll values here across several frames and writing tiles to newly revealed areas of the nametables, one can achieve the effect of a camera panning over a large background.

Address ($2006) >> write x2

  • Common name: PPUADDR
  • Description: PPU address register
  • Access: write twice

Because the CPU and the PPU are on separate buses, neither has direct access to the other's memory. The CPU writes to VRAM through a pair of registers on the PPU by first loading an address into PPUADDR and then it writing data repeatedly to PPUDATA. The 16-bit address is written to PPUADDR one byte at a time, upper byte first. Whether this is the first or second write is tracked internally by the w register, which is shared with PPUSCROLL.

After reading PPUSTATUS to clear w (the write latch), write the 16-bit address of VRAM you want to access here, upper byte first. For example, to set the VRAM address to $2108:

  lda #$21
  sta PPUADDR
  lda #$08
  sta PPUADDR

Valid addresses are $0000–$3FFF; higher addresses will be mirrored down.

Note

Access to PPUSCROLL and PPUADDR during screen refresh produces interesting raster effects; the starting position of each scanline can be set to any pixel position in nametable memory. For more information, see PPU scrolling.

Palette corruption

In specific circumstances, entries of the PPU's palette can be corrupted. It's unclear exactly how or why this happens, but all revisions of the NTSC PPU seem to be at least somewhat susceptible.[10]

When done writing to palette memory, the workaround is to always

  1. Update the address, if necessary, so that it's pointing at $3F00, $3F10, $3F20, or any other mirror.
  2. Only then change the address to point outside of palette memory.

A code fragment to implement this workaround is present in vast numbers of games:[11]

  lda #$3F
  sta PPUADDR
  lda #0
  sta PPUADDR
  sta PPUADDR
  sta PPUADDR

Bus conflict

During raster effects, if the second write to PPUADDR happens at specific times, at most one axis of scrolling will be set to the bitwise AND of the written value and the current value. The only safe time to finish the second write is during blanking; see PPU scrolling for more specific timing. [1]

Data ($2007) <> read/write

  • Common name: PPUDATA
  • Description: PPU data port
  • Access: read, write

VRAM read/write data register. After access, the video memory address will increment by an amount determined by bit 2 of $2000.

When the screen is turned off by disabling the background/sprite rendering flag with the PPUMASK or during vertical blank, you can read or write data from VRAM through this port. Since accessing this register increments the VRAM address, it should not be accessed outside vertical or forced blanking because it will cause graphical glitches, and if writing, write to an unpredictable address in VRAM. However, two games are known to read from PPUDATA during rendering: see Tricky-to-emulate games.

VRAM reading and writing shares the same internal address register that rendering uses. So after loading data into video memory, the program should reload the scroll position afterwards with PPUSCROLL and PPUCTRL (bits 1…0) writes in order to avoid wrong scrolling.

The PPUDATA read buffer (post-fetch)

When reading PPUDATA while the VRAM address is in the range 0–$3EFF (i.e., before the palettes), the read will return the contents of an internal read buffer. This read buffer is updated on every PPUDATA read, but only after the previous contents have been returned to the CPU. This is because PPU bus reads are too slow and cannot complete in time to service the CPU read. Because of this, after the VRAM address has been set through PPUADDR, one should first read PPUDATA to prime the read buffer (ignoring the result) before then reading the desired data from it.

Some PPUs support reading palette data from $3F00-3FFF. These reads work differently, as palette RAM is a separate memory space internal to the PPU that is overlaid onto the PPU address space. The referenced 6-bit palette data is returned immediately instead of going to the internal read buffer, and hence no priming read is required. Simultaneously, the PPU also performs a normal read from the PPU memory at the specified address, "underneath" the palette data, and the result of this read goes into the read buffer as normal. The old contents of the read buffer are discarded when reading palettes, but by changing the address to point outside palette RAM and performing one read, the contents of this shadowed memory (usually mirrored nametables) can be accessed. On PPUs that do not support reading palette RAM, this memory range behaves the same as the rest of PPU memory.

Note that the internal read buffer is updated only on PPUDATA reads. It is not affected by other PPU processes such as rendering, and it maintains its value indefinitely until the next read.

Read conflict with DPCM samples

If currently playing DPCM samples, there is a chance that an interruption from the APU's sample fetch will cause an extra read cycle if it happened at the same time as an instruction that reads $2007. This will cause an extra increment and a byte to be skipped over, corrupting the data you were trying to read. See: APU DMC

OAM DMA ($4014) > write

  • Common name: OAMDMA
  • Description: OAM DMA register (high byte)
  • Access: write

This port is located on the CPU. Writing $XX will upload 256 bytes of data from CPU page $XX00–$XXFF to the internal PPU OAM. This page is typically located in internal RAM, commonly $0200–$02FF, but cartridge RAM or ROM can be used as well.

  • The CPU is suspended during the transfer, which will take 513 or 514 cycles after the $4014 write tick. (1 wait state cycle while waiting for writes to complete, +1 if on a put cycle, then 256 alternating get/put cycles. See DMA for more information.)
  • The OAM DMA is the only effective method for initializing all 256 bytes of OAM. Because of the decay of OAM's dynamic RAM when rendering is disabled, the initialization should take place within vblank. Writes through OAMDATA are generally too slow for this task.
  • The DMA transfer will begin at the current OAM write address. It is common practice to initialize it to 0 with a write to OAMADDR before the DMA transfer. Different starting addresses can be used for a simple OAM cycling technique, to alleviate sprite priority conflicts by flickering. If using this technique, after the DMA OAMADDR should be set to 0 before the end of vblank to prevent potential OAM corruption (see errata). However, due to OAMADDR writes also having a "corruption" effect,[4] this technique is not recommended.

Internal registers

The PPU also has 4 internal registers, described in detail on PPU scrolling:

  • v: During rendering, used for the scroll position. Outside of rendering, used as the current VRAM address.
  • t: During rendering, specifies the starting coarse-x scroll for the next scanline and the starting y scroll for the screen. Outside of rendering, holds the scroll or VRAM address before transferring it to v.
  • x: The fine-x position of the current scroll, used during rendering alongside v.
  • w: Toggles on each write to either PPUSCROLL or PPUADDR, indicating whether this is the first or second write. Clears on reads of PPUSTATUS. Sometimes called the 'write latch' or 'write toggle'.

References