NES 2.0 and PPU registers: Difference between pages

From NESdev Wiki
(Difference between pages)
Jump to navigationJump to search
(Where on earth did that come from? We haven't even allocated anything outside of the first plane, there's no need for another four bits. It shouldn't even be mentioned.)
 
 
Line 1: Line 1:
'''NES 2.0''' is an extension to the [[iNES]] ROM format, originally [http://forums.nesdev.org/viewtopic.php?p=17727#p17727 proposed] by Kevin Horton on 2006-09-18.
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.
He has implemented it in his FPGA NES.


*Written by K.Horton
Immediately after powerup, the PPU isn't necessarily in a usable state.
*Thanks to Quietust for ideas and proofing and help
The program needs to do a few things to get it going; see [[PPU power up state]] and [[Init code]].


NES 2.0 is an addition to the standard .NES file format that most emulators use.
<noinclude>
It is designed to disambiguate certain ROMs that formerly could have been only be discerned via a CRC-32 or similar hash check.
__TOC__
Naturally, this caused problems for new ROMs that are not in the database but need special handling, such as fan translations, other ROM hacks, and new homebrew on compatible boards.
</noinclude>


There are four goals for this specification:
== Summary ==
#Retain 100% backwards compatibility with existing emulators/ROMs/etc. (*this includes "dirty ROMs" with crap such as "DiskDude!" in the header and other atrocities*)
#The format must be "future proof".
#The changes made must be VERY CAREFULLY documented and make sense.
#Said changes must make sense from both a hardware and software standpoint.


The name of this specification is "NES 2.0".
{| class="tabular"
Do not confuse with "iNES 2.0" which is an emulator that does not support extended features of NES 2.0 headers.
! Address
! Bits
! Notes
! Common Name
|-
! $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)
| [[#PPUCTRL|PPUCTRL]]
|-
! $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)
| [[#PPUMASK|PPUMASK]]
|-
! $2002
| <tt style="white-space: nowrap">VSO- ----</tt> || VBLANK (V), sprite 0 hit (S), sprite overflow (O), read resets write pair for $2005/2006
| [[#PPUSTATUS|PPUSTATUS]]
|-
! $2003
| <tt style="white-space: nowrap">aaaa aaaa</tt> || OAM read/write address
| [[#OAMADDR|OAMADDR]]
|-
! $2004
| <tt style="white-space: nowrap">dddd dddd</tt> || OAM data read/write
| [[#OAMDATA|OAMDATA]]
|-
! $2005
| <tt style="white-space: nowrap">xxxx xxxx</tt> || fine scroll position (two writes: X, Y)
| [[#PPUSCROLL|PPUSCROLL]]
|-
! $2006
| <tt style="white-space: nowrap">aaaa aaaa</tt> || PPU read/write address (two writes: MSB, LSB)
| [[#PPUADDR|PPUADDR]]
|-
! $2007
| <tt style="white-space: nowrap">dddd dddd</tt> || PPU data read/write
| [[#PPUDATA|PPUDATA]]
|-
! $4014
| <tt style="white-space: nowrap">aaaa aaaa</tt> || OAM DMA high address
| [[#OAMDMA|OAMDMA]]
|}
 
== Notes ==
 
 
=== <span id="PPUCTRL"><span id="Reg2000">Controller ($2000) > write</span></span> ===
 
* 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; 1: 8x16)
|+-------- 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 [[#PPUSCROLL|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.
 
[[PPU power up state|After power/reset]], writes to this register are ignored for about 30000 cycles.
 
==== Master/slave mode and the EXT pins ====
When bit 6 of [[#PPUCTRL|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 bus conflict ====
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, [http://forums.nesdev.org/viewtopic.php?p=112424#p112424 a write near the end of a visible scanline] may cause only the next scanline to be erroneously drawn from the left nametable.
This can cause a visible glitch.
Worse, it can theoretically cause a sprite 0 hit to fail, which may crash a game using a sprite 0 spin loop that's not resilient.
 
Only writes at the exact moment between active picture and horizontal blanking cause this glitch; well-timed mid-scanline writes do not, nor do writes that land well within horizontal blanking.
The glitch has no effect in horizontal or one-screen mirroring.
It also does not appear if bit 0 of the written value is 0; this always correctly sets the left nametable.
 
This produces an occasionally [[Game bugs|visible glitch]] in ''Super Mario Bros.'' when the program writes to [[#PPUCTRL|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.
To work around this in new productions, have your game logic set a flag that your NMI handler checks.
 
=== <span id="PPUMASK"><span id="Reg2001">Mask ($2001) > write</span></span> ===
 
* 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
|||| ||||
|||| |||+- Grayscale (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*
|+-------- Emphasize green*
+--------- Emphasize blue*
 
<nowiki>*</nowiki> NTSC colors. PAL swaps green and red<ref>PAL PPU swaps green and red emphasis bits: http://forums.nesdev.org/viewtopic.php?p=131889#p13188</ref>.
 
==== 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 enables all rendering, with no color effects. A value of $00 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 ====


== Existing header ==
* 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.
The standard specification as popularized by the iNES emulator is presented below:


<pre>
* Bits 5,6,7 control a color "emphasis" or "tint" effect. Each bit emphasizes 1 color while darkening the other two. Setting all three emphasis bits will darken all colors.
0-3: string    "NES"<EOF>
  4: byte      Number of 16384 byte program ROM pages
  5: byte      Number of 8192 byte character ROM pages (0 indicates CHR RAM)
  6: bitfield  Flags 6
  7: bitfield  Flags 7
8-15: byte      These bytes are not used, and should be 00h.
</pre>


The PRG ROM size is in 16384 byte units, and the CHR ROM size is in 8192 byte units.
* See [[NTSC video]] for a description of how bits 5-7 work on NTSC and PAL PPUs.
If the actual game's data is smaller, such as 8192-byte PRG for Galaxian or 4096-byte CHR for Slappin', double it up and call it a day.
For example, if the PRG ROM data is 16384 bytes long, and the first 8192 bytes of the PRG ROM data match the second 8192 bytes, the PRG ROM is an 8Kx8 chip.


=== Byte 6 (Flags 6) ===
* Bit 5 emphasizes red on the NTSC PPU, green on the PAL PPU.
<pre>
* Bit 6 emphasizes green on the NTSC PPU, red on the PAL PPU.
7       0
* Bit 7 emphasizes blue on the NTSC and PAL PPU.
---------
NNNN FTBM


N: Lower 4 bits of the mapper number
* The [[Vs. System|RGB PPU]] used by PlayChoice and some other systems treat the emphasis bits differently. Instead of darkening other RGB components, it forces one component to maximum brightness. [[Colour-emphasis games|A few games]], which set all three tint bits to darken all colors, are unplayable on these PPUs.
F: Four screen mode. 0 = no, 1 = yes. (When set, the M bit has no effect)
T: Trainer. 0 = no trainer present, 1 = 512 byte trainer at 7000-71FFh
B: SRAM at 6000-7FFFh battery backed.  0= no, 1 = yes
M: Mirroring.  0 = horizontal, 1 = vertical.
</pre>
None of this changes in NES 2.0.
KH discovered that very few existing ROMs have a trainer, and most of these aren't real trainers at all but subroutines used by a mapper hack.


=== Byte 7 (Flags 7) ===
* The emphasis bits are applied after greyscale, which means they will still tint the color of the grey image.
Later versions of the iNES emulator introduced a second byte of flags.
<pre>
7      0
---------
NNNN xxPV


N: Upper 4 bits of the mapper number
=== <span id="PPUSTATUS"><span id="Reg2002">Status ($2002) < read</span></span> ===
P: Playchoice 10.  When set, this is a PC-10 game
V: Vs. Unisystem.  When set, this is a Vs. game
x: these bits are not used in iNES.
</pre>
Early ROM processing tools were not aware of Flags 7 because the earliest emulators ignored it.
For example, one tool put 0x44 (ASCII for 'D', the first character of "DiskDude!") here.
This confuses newer emulators, which combine the nibbles from Flags 6 and Flags 7 to form an incorrect mapper number.
NES 2.0 redefines the unused bits to always equal binary 10, which happens not to match any of the patterns used by these ROM processing tools:
<pre>
7      0
---------
NNNN SSPV


N: Upper 4 bits of the mapper number
* Common name: '''PPUSTATUS'''
S: When equal to binary 10, use NES 2.0 rules; otherwise, use other rules.
* Description: PPU status register
P: Playchoice 10.  When set, this is a PC-10 game
* Access: read
V: Vs. Unisystem.  When set, this is a Vs. game
</pre>
That is the "how" of determining whether we are dealing with a valid NES 2.0
file.  Now that that is done, the desired aspects of such a thing need to
be considered.  Kevin has tested over 4000 ROMs and have dumped at least a
thousand more, and reverse engineered probably 60-70 different mappers.
This has given him a front row seat into the shortcomings of the original,
and a good idea of where additional information is needed.


== The problem cases ==
This register reflects the state of various functions inside the PPU.
The new specification needs to clarify five areas:
It is often used for determining timing.
<span id="Sprite_0">To determine when the PPU has reached a given pixel of the screen, put an opaque pixel of sprite 0 there.</span>


=== Vs. Unisystem ===
7  bit  0
The Vs. Unisystem is one of the two Nintendo arcade machine series produced
---- ----
which use "mostly NES/famicom" hardwareThese games will run fine on
VSO. ....
emulators if a little extra things are stored in the header.
|||| ||||
|||+-++++- Least significant bits previously written into a PPU register
|||        (due to register not being updated for this address)
||+------- 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 HitSet 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.


Nintendo wanted to make life difficult for arcade operators when it came to
==== Notes ====
copy protection. Three main schemes were devised. (See the "Vs. system byte"
* Reading the status register will clear D7 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.
description below for a detailed analysis)
* When the sprite 0 hit flag is set on a frame, it will not be cleared until the vertical blank has ended on the next frame.  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 D6 = 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.
* [[Media:Ntsc timing.png|This timing diagram]] might clarify the timing of setting and clearing the flags ([[Media:Ppu.svg|source Inkscape SVG file]]).
* Some [[Vs. System]] PPUs return a constant value in D4-D0 that the game checks.
* '''Caution:''' Reading [[#PPUSTATUS|PPUSTATUS]] at the exact start of vertical blank will return 0 in bit 7 but clear the latch anyway, causing the program to miss frames. See [[NMI]] for details.


=== Big ROMs ===
=== <span id="OAMADDR"><span id="Reg2003">OAM address ($2003) > write</span></span> ===  


PRG ROM has already surpassed 2 MiB, and CHR ROM has already surpassed 1 MiB, especially on pirate multicarts.
* Common name: '''OAMADDR'''
So far, the hack has been to set PRG ROM to 00h to indicate 4Mbytes of ROM (since FFh is 16K short of 4Mbytes).
* Description: OAM address port
And in the case of exceeding the 2Mbyte-8K CHR barrier, ROMs have been allocating the CHR in the PRG space.
* Access: write
This can get very messy for an emulator to sort out.


=== Submappers ===
Write the address of [[PPU OAM|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|OAMDATA]])


Occasionally, two mappers get one number.
==== Obscure details of OAMADDR ====
Sometimes, an emulator can distinguish them easily: [[iNES Mapper 034|34]] covers both [[NINA-001]] and [[BNROM]], but NINA-001 has CHR ROM, and BNROM has CHR RAM.
But other mappers are messier: [[iNES Mapper 083|mapper 83]] (two styles of CHR ROM banking), [[SxROM|mapper 1]] (various ROM/RAM hacks), [[Bandai FCG board|mapper 16]] (EEPROM/WRAM/light pen/etc).


Currently, the only fix for this is to CRC the games and then hack the mapper
OAMADDR is set to 0 during each of ticks 257-320 (the sprite tile loading interval) of the pre-render and visible scanlines.
if the CRC or other hash matches.  This of course fails if the game is not
in the database, especially newly developed homebrew games.


=== Mapper number exhaustion ===
The value of OAMADDR when sprite evaluation starts at tick 65 of the visible scanlines will determine where in OAM sprite evaluation starts, and hence which sprite gets treated as sprite 0. The first OAM entry to be checked during sprite evaluation is the one starting at <tt>OAM[OAMADDR]</tt>. 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 <tt>OAM[OAMADDR]</tt>.


Face it: we're running out.
On the 2C02, writes to OAMADDR reliably corrupt OAM.<ref>Manual OAM write glitchyness http://forums.nesdev.org/viewtopic.php?f=2&t=10189</ref> This can then be worked around by writing all 256 bytes of OAM. 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. The former bug is known to have been fixed in the 2C07; the latter is believed to be.
In the early days of iNES, 16 mappers seemed like it would be enough, but they were quickly exhausted.
Then 256 mappers seemed like a vast space to work on.
But now, we are getting near the end of the line, and running out of mapper numbers.
Mr. Horton alone has assigned at least 50 or 60 of them, which is almost 1/4th of the total mapper space.


=== WRAM ===
=== <span id="OAMDATA"><span id="Reg2004">OAM data ($2004) <> read/write</span></span> ===


Not all carts that support PRG RAM support 8K of it.
* Common name: '''OAMDATA'''
Some support less, some support more, and [[Bandai FCG board|some by Bandai]] even have EEPROM!
* Description: OAM data port
Heck, some carts even battery backed the stupid CHR RAM.
* Access: read, write
This last one was a very recent find at the time NES 2.0 was first written and goes to show that a workable extension needs to reasonably cover all possible bases.


== The proposed solution ==
Write OAM data here. Writes will increment [[#OAMADDR|OAMADDR]] after the write; reads during vertical or forced blanking return the value from OAM at that address but do not increment.


=== Byte 8 (Mapper variant) ===
Because changes to OAM should normally be made only during VBLANK, writing through OAMDATA is only effective for partial updates (it is too slow). Most games will use the DMA feature through [[OAMDMA]] instead.
<pre>
7      0
---------
SSSS MMMM


S: Submapper number.  Mappers not using submappers set this to zero.
* Reading OAMDATA while the PPU is rendering will expose internal OAM accesses during sprite evaluation and loading; Micro Machines does this.
M: Bits 11-8 of mapper number.
</pre>
[[NES 2.0 submappers|Submappers]] define mostly how CHR is wired up.
Most mappers will not use submappers; they set S to 0.


{{RFC 2119}}
* 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|OAMDMA]], since that uses writes to $2004. For emulation purposes, it is probably best to completely ignore writes during rendering.
It's NOT RECOMMENDED yet as of 2014 to assign mapper numbers greater than 255.
There were still a couple dozen existing numbers left in [[mapper|the current iNES mapper space]] as of 2013.
Mapper numbers up to 511 should hold us until they stop making ''Ice Age'' films;
mapper numbers up to 4095 should hold us until the next ''literal'' ice age.


In 2013, there was a [http://forums.nesdev.org/viewtopic.php?f=3&t=9854 proposal on the BBS] to break up the expanded mapper space into "planes", much like those of [[wikipedia:Plane (Unicode)|Unicode when it expanded past UCS-2]].
* It used to be thought that reading from this register wasn't reliable<ref>$2004 reading reliable? http://forums.nesdev.org/viewtopic.php?f=2&t=6424</ref>, however more recent evidence seems to suggest that this is solely due to corruption by [[OAMADDR]] writes.
Each M value would thus correspond to one plane:
* Plane 0 (0-255): Basic Multilingual Plane, the current mess
* Plane 1 (256-511): Supplementary Multilingual Plane (mostly for new homebrew mappers)
* Plane 2 (512-767): Supplementary Ideographic Plane (for new dumps of East Asian games)
* Plane 15: Private use area (not for publicly distributed dumps)


=== Byte 9 (Upper bits of ROM size) ===
* In the oldest instantiations of the PPU, as found on earlier Famicoms, this register is not readable<ref>$2003 not readable on early revisions http://forums.nesdev.org/viewtopic.php?p=62137#p62137</ref>. It's not known exactly which revision of the 2C02 added the readability—it is certainly absent in the RP2C02C, and present by the RP2C02G.
<pre>
7      0
---------
CCCC PPPP


C: 4 more CHR ROM size bits
* In the 2C07, sprite evaluation can ''never'' be fully disabled, and will always start at scanline 20<ref>2C07 PPU sprite evaluation notes: http://forums.nesdev.org/viewtopic.php?f=9&t=11041</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.
P: 4 more PRG ROM size bits
</pre>
These combine with the existing 8 bits of each to form 12 bits total
for the number of PRG and CHR banks... this is enough for 64Mbytes-16K
of PRG data and 32Mbytes-8K of CHR data.


Only a few mappers, mostly multicart mappers, support non-power-of-two sizes for PRG and CHR.
=== <span id="PPUSCROLL"><span id="Reg2005">Scroll ($2005) >> write x2</span></span> ===
The behavior of a ROM with a Nintendo MMC and a non-power-of-two ROM size is undefined.


=== Byte 10 (RAM size) ===
* Common name: '''PPUSCROLL'''
<pre>
* Description: PPU scrolling position register
7      0
* Access: write twice
---------
pppp PPPP


p: Quantity of PRG RAM which is battery backed (or serial EEPROM, see below)
This register is used to change the [[PPU scrolling|scroll position]], that is, to tell the PPU which pixel of the nametable selected through [[#PPUCTRL|PPUCTRL]] should be at the top left corner of the rendered screen. Typically, this register is written to during vertical blanking, 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.
P: Quantity of PRG RAM which is NOT battery backed
</pre>
The PRG RAM need not be actual RAM; it may also be [[ROM|EEPROM]].
Serial EEPROMs are familiar to developers of Game Boy Advance and Nintendo DS emulators.
But they're older than that:
some [[iNES Mapper 016|mapper 16]] (Bandai) games use serial EEPROMs to store the game data,
rather than a battery backed SRAM.
They most often used the 24C01 (with 128 bytes) or 24C02 (with 256 bytes), but there's no practical limitation on total size. Both ICs use the [[wikipedia:I²C|I²C]] bus.


Some games on [[SxROM|SOROM]] or [[ExROM|ETROM]] have two PRG RAM chips, one battery-backed and one not.
After reading [[#PPUSTATUS|PPUSTATUS]] to reset the address latch, write the horizontal and vertical scroll offsets here just before turning on the screen:
Which chip appears earlier in PRG RAM address space depends on the mapper.
For example, SOROM (mapper 1, no CHR ROM, 8192 byte CHR RAM, 8192+8192 byte PRG RAM) puts the battery-backed chip at bank 2 and the other chip at bank 0.


Most discrete logic mappers lacked specific support for PRG RAM.
  bit PPUSTATUS
For these mappers, emulators should continue the iNES tradition of extending the stock mappers' functionality in the same way that ''Family BASIC'' extended that of mapper 0.
  ; possibly other code goes here
A cartridge can have up to 8192 bytes of either battery-backed or non-battery-backed RAM at $6000-$7FFF,[http://forums.nesdev.org/viewtopic.php?p=77288#p77288] decoded with an [[PRG RAM circuit|74HC20 or 74LS139]].
  lda cam_position_x
But some mappers have registers in $6000-$7FFF and do not define a mapping for PRG RAM or serial EEPROM, such as [[INES Mapper 086|Jaleco's JF-13]].
  sta PPUSCROLL
Headers for ROMs using these mappers MUST specify 0 for both PRG RAM sizes.
  lda cam_position_y
  sta PPUSCROLL


Bytes 10 and 11 of the header define the size of the RAM segments exponentially using 4-bit values:
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.
{| class="tabular"
 
|-
By changing the 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.
! value || RAM size in bytes
 
|-
=== <span id="PPUADDR"><span id="Reg2006">Address ($2006) >> write x2</span></span> ===
| 0 || 0
 
|-
* Common name: '''PPUADDR'''
| 1 || 128
* Description: PPU address register
|-
* Access: write twice
| 2 || 256
 
|-
Because the CPU and the PPU are on separate buses, neither has direct access to the other's memory.
| 3 || 512
The CPU writes to VRAM through a pair of registers on the PPU.
|-
First it loads an address into [[#PPUADDR|PPUADDR]], and then it writes repeatedly to [[#PPUDATA|PPUDATA]] to fill VRAM.
| 4 || 1024
|-
| 5 || 2048
|-
| 6 || 4096
|-
| 7 || 8192
|-
| 8 || 16384
|-
| 9 || 32768
|-
| 10 || 65536
|-
| 11 || 131072
|-
| 12 || 262144
|-
| 13 || 524288
|-
| 14 || 1048576
|-
| 15 || Reserved; do not use
|}
Sizes that are not a power of two, such as the 5120 byte battery-backed RAM of [[iNES Mapper 082|Taito's X1-017]], are rounded up.


=== Byte 11 (Video RAM Size) ===
After reading [[#PPUSTATUS|PPUSTATUS]] to reset the address latch, write the 16-bit address of VRAM you want to access here, upper byte first.
<pre>
For example, to set the VRAM address to $2108:
7      0
---------
cccc CCCC


c: Quantity of CHR RAM which is battery backed (yes it exists! see below)
  lda #$21
C: Quantity of CHR RAM which is NOT battery backed
  sta PPUADDR
</pre>
  lda #$08
  sta PPUADDR


The majority of games with no CHR ROM will have $07 (8192 bytes, not battery backed) in this byte.
Valid addresses are $0000-$3FFF; higher addresses will be [[mirroring|mirrored]] down.
Use of $00 with no CHR ROM implies that the game is wired to map nametable memory in CHR space. The value $00 MUST NOT be used if a mapper isn't defined to allow this.


Battery-backed CHR RAM exists. The [http://www.nesmuseum.com/racermate.html RacerMate Challenge II] cartridge has 64K of CHR RAM total:
==== note ====
32K is battery backed, and 32K of it is not.
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 [[The skinny on NES scrolling]] and [http://forums.nesdev.org/viewtopic.php?p=64111#p64111 tokumaru's sample code on the BBS].
They store all the stats and such in it.
KH traced out the circuit and couldn't believe it.
It probably simplified the routing or power off protection.


For backward compatibility, the battery bit in the original [[iNES]] header (byte 6, bit 1) MUST be true if the upper nibble of byte 10 or 11 is nonzero or false otherwise.
''' Editor's note:''' Last comment about external page should be re-directed to the getting started section instead.


=== Byte 12 (TV system) ===
=== <span id="PPUDATA"><span id="Reg2007">Data ($2007) <> read/write</span></span> ===
Different TV systems have different [[clock rate]]s, and a game's raster effects and difficulty tuning might expect one or the other.
<pre>
7      0
---------
xxxx xxBP
</pre>
P: 0 indicates NTSC mode; 1 is for PAL mode.
;NTSC mode
:262 lines, NMI on line 241, 3 dots per CPU clock
;PAL mode
:312 lines, NMI on line 241, 3.2 dots per CPU clock
;Dendy PAL mode
:312 lines, NMI on line 291, 3 dots per CPU clock, designed for maximum compatibility with NTSC ROMs, but NES 2.0 does not yet indicate that a game is designed for this mode


B: When set, indicates this ROM works on both PAL and NTSC machines.
* Common name: '''PPUDATA'''
Some of the Codemasters games actually will adjust the game depending on if it [[Detect TV system|detects you running on a PAL or NTSC machine]] - it adjusts the timing of the game and transposes the music.
* Description: PPU data port
Not many games would have this B flag set; those that do would be labeled (UE) or the like in GoodNES.
* Access: read, write


=== Byte 13 (Vs. hardware) ===
VRAM read/write data register. After access, the video memory address will increment by an amount determined by $2000:2.
<pre>
7      0
---------
MMMM PPPP


This byte is reserved for the Vs. system only.  If this is not
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]].
a Vs. system ROM, the value of this byte must be $00, which
signifies RP2C03B (used in PlayChoice, Famicom Titler, and a
few TVs with built-in Famicom) and no Vs.-specific submapper.


P: PPU. There are 13 Vs. PPUs that can be found on the 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|PPUSCROLL]] writes in order to avoid wrong scrolling.


0 - RP2C03B    (bog standard RGB palette)
==== The PPUDATA read buffer (post-fetch) ====
1 - RP2C03G    (similar pallete to above, might have 1 changed colour)
2 - RP2C04-0001 (scrambled palette + new colours)
3 - RP2C04-0002 (same as above, different scrambling, diff new colours)
4 - RP2C04-0003 (similar to above)
5 - RP2C04-0004 (similar to above)
6 - RC2C03B    (bog standard palette, seems identical to RP2C03B)
7 - RC2C03C    (similar to above, but with 1 changed colour or so)
8 - RC2C05-01  (all five of these have the normal palette...
9 - RC2C05-02  (...but with different bits returned on 2002)
10 - RC2C05-03
11 - RC2C05-04
12 - RC2C05-05
13 - not defined (do not use these)
14 - not defined
15 - not defined


KH has a good cross-section of Vs. games and has dumped bit-for-bit
When reading 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 internal buffer is updated '''only''' when reading [[#PPUDATA|PPUDATA]], and so is preserved across frames. After the CPU reads and gets the contents of the internal buffer, the PPU will immediately update the internal buffer with the byte at the current VRAM address. Thus, after setting the VRAM address, one should first read this register and discard the result.
palettes from all thirteen of these PPUs.  The last 5 PPUs (RC2C05)
have the standard NES palette in them, however they return a specific
word in the lower 5 bits of PPUSTATUS ($2002), and the PPUCTRL ($2000)
and PPUMASK ($2001) ports are flipped around (PPUMASK at $2000 and  
PPUCTRL at $2001).


Nocash and MESS report:
Reading palette data from $3F00-$3FFF works differently. The palette data is placed immediately on the data bus, and hence no dummy read is required. Reading the palettes still updates the internal buffer though, but the data placed in it is the mirrored nametable data that would appear "underneath" the palette. (Checking the [[PPU memory map]] should make this clearer.)
RC2C05-01 (with ID ([2002h] AND ??h)=1Bh)
RC2C05-02 (with ID ([2002h] AND 3Fh)=3Dh)
RC2C05-03 (with ID ([2002h] AND 1Fh)=1Ch)
RC2C05-04 (with ID ([2002h] AND 1Fh)=1Bh)
and cannot find the 2C05-05


=== <span id="Reg4014"><span id="OAMDMA">OAM DMA ($4014) > write</span></span> ===


M: Vs. mode:
* Common name: '''OAMDMA'''
* Description: OAM DMA register (high byte)
* Access: write


0 - Normal- no special mode(s)
Writing $XX to this register will upload 256 bytes of data from CPU page $XX00-$XXFF to the internal PPU OAM. This page is typically located in RAM, but ROM can be used as well.
1 - RBI Baseball  (protection hardware at port 5E0xh)
2 - TKO Boxing    (other protection hardware at port 5E0xh)
3 - Super Xevious (protection hardware at port 5xxxh)
4 - ...
</pre>


Nintendo did a few things to make piracy difficult for arcade operators:
* The CPU is suspended during the transfer, which will take 513 or 514 cycles after the $4014 write tick. (1 idle cycle, +1 if on an odd CPU cycle, then 256 alternating read/write cycles.)
;Different PPUs: There are 13 different PPU chips found on Vs. arcade boards.
;Different controller pinouts: Some games came with new control panels you had to install with the game.  This was pretty basic stuff and just remapped a few of the buttons. (FIXME: which games do use what special control panels?)


=== Byte 14 and 15 (Reserved) ===
* 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.
Reserved, these two bytes must be zero.


== Emulator support ==
* 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 $2003 should be set to 0 before the end of VBLANK to prevent potential OAM corruption (See: [[Errata]]).
*BizHawk as of r6313
*FCEUX as of r3071
*[[Nintendulator]] 0.975 Beta
*MESS (per [http://forums.nesdev.org/viewtopic.php?p=62428#p62428 BBS post 62428])
*no$nes v1.1


[[Category:File formats]]
== References ==
<references />

Revision as of 03:16, 16 March 2015

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 mirrored in every 8 bytes from $2008 through $3FFF, so 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.



Summary

Address Bits Notes Common Name
$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) PPUCTRL
$2001 BGRs bMmG color emphasis (BGR), sprite enable (s), background enable (b), sprite left column enable (M), background left column enable (m), greyscale (G) PPUMASK
$2002 VSO- ---- VBLANK (V), sprite 0 hit (S), sprite overflow (O), read resets write pair for $2005/2006 PPUSTATUS
$2003 aaaa aaaa OAM read/write address OAMADDR
$2004 dddd dddd OAM data read/write OAMDATA
$2005 xxxx xxxx fine scroll position (two writes: X, Y) PPUSCROLL
$2006 aaaa aaaa PPU read/write address (two writes: MSB, LSB) PPUADDR
$2007 dddd dddd PPU data read/write PPUDATA
$4014 aaaa aaaa OAM DMA high address OAMDMA

Notes

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; 1: 8x16)
|+-------- 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 0 and 1 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 30000 cycles.

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 bus conflict

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 near the end of a visible scanline may cause only the next scanline to be erroneously drawn from the left nametable. This can cause a visible glitch. Worse, it can theoretically cause a sprite 0 hit to fail, which may crash a game using a sprite 0 spin loop that's not resilient.

Only writes at the exact moment between active picture and horizontal blanking cause this glitch; well-timed mid-scanline writes do not, nor do writes that land well within horizontal blanking. The glitch has no effect in horizontal or one-screen mirroring. It also does not appear if bit 0 of the written value is 0; this always correctly sets the left nametable.

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. To work around this in new productions, have your game logic set a flag that your NMI handler checks.

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
|||| ||||
|||| |||+- Grayscale (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*
|+-------- Emphasize green*
+--------- Emphasize blue*

* NTSC colors. PAL swaps green and red[1].

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 enables all rendering, with no color effects. A value of $00 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,7 control a color "emphasis" or "tint" effect. Each bit emphasizes 1 color while darkening the other two. Setting all three emphasis bits will darken all colors.
  • See NTSC video for a description of how bits 5-7 work on NTSC and PAL PPUs.
  • Bit 5 emphasizes red on the NTSC PPU, green on the PAL PPU.
  • Bit 6 emphasizes green on the NTSC PPU, red on the PAL PPU.
  • Bit 7 emphasizes blue on the NTSC and PAL PPU.
  • The RGB PPU used by PlayChoice and some other systems treat the emphasis bits differently. Instead of darkening other RGB components, it forces one component to maximum brightness. A few games, which set all three tint bits to darken all colors, are unplayable on these PPUs.
  • The emphasis bits are applied after greyscale, which means 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 pixel of sprite 0 there.

7  bit  0
---- ----
VSO. ....
|||| ||||
|||+-++++- Least significant bits previously written into a PPU register
|||        (due to register not being updated for this address)
||+------- 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 D7 mentioned above and also the address latch used by PPUSCROLL and PPUADDR. It does not clear the sprite 0 hit or overflow bit.
  • When the sprite 0 hit flag is set on a frame, it will not be cleared until the vertical blank has ended on the next frame. 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 D6 = 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.
  • This timing diagram might clarify the timing of setting and clearing the flags (source Inkscape SVG file).
  • Some Vs. System PPUs return a constant value in D4-D0 that the game checks.
  • Caution: Reading PPUSTATUS at the exact start of vertical blank will return 0 in bit 7 but clear the latch anyway, causing the program to miss frames. See NMI 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)

Obscure details of OAMADDR

OAMADDR is set to 0 during each of ticks 257-320 (the sprite tile loading interval) of the pre-render and visible scanlines.

The value of OAMADDR when sprite evaluation starts at tick 65 of the visible scanlines will determine where in OAM sprite evaluation starts, and hence which sprite gets treated as sprite 0. The first OAM entry to be checked during sprite evaluation is the one starting at OAM[OAMADDR]. 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 OAM[OAMADDR].

On the 2C02, writes to OAMADDR reliably corrupt OAM.[2] This can then be worked around by writing all 256 bytes of OAM. 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. The former bug is known to have been fixed in the 2C07; the latter is believed to be.

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 during vertical or forced blanking return the value from OAM at that address but do not increment.

Because changes to OAM should normally be made only during VBLANK, writing through OAMDATA is only effective for partial updates (it is too slow). 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[3], 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, this register is not readable[4]. It's not known exactly which revision of the 2C02 added the readability—it is certainly absent in the RP2C02C, and present by the RP2C02G.
  • In the 2C07, sprite evaluation can never be fully disabled, and will always start at scanline 20[5] (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, that is, to tell the PPU which pixel of the nametable selected through PPUCTRL should be at the top left corner of the rendered screen. Typically, this register is written to during vertical blanking, 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.

After reading PPUSTATUS to reset the address latch, write the horizontal and vertical scroll offsets here just before turning on the screen:

 bit PPUSTATUS
 ; possibly other code goes here
 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 are treated as -16 through -1 in a way, but tile data is incorrectly fetched from the attribute table.

By changing the 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. First it loads an address into PPUADDR, and then it writes repeatedly to PPUDATA to fill VRAM.

After reading PPUSTATUS to reset the address 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 The skinny on NES scrolling and 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.

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 $2000:2.

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 writes in order to avoid wrong scrolling.

The PPUDATA read buffer (post-fetch)

When reading 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 internal buffer is updated only when reading PPUDATA, and so is preserved across frames. After the CPU reads and gets the contents of the internal buffer, the PPU will immediately update the internal buffer with the byte at the current VRAM address. Thus, after setting the VRAM address, one should first read this register and discard the result.

Reading palette data from $3F00-$3FFF works differently. The palette data is placed immediately on the data bus, and hence no dummy read is required. Reading the palettes still updates the internal buffer though, but the data placed in it is the mirrored nametable data that would appear "underneath" the palette. (Checking the PPU memory map should make this clearer.)

OAM DMA ($4014) > write

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

Writing $XX to this register will upload 256 bytes of data from CPU page $XX00-$XXFF to the internal PPU OAM. This page is typically located in RAM, but 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 idle cycle, +1 if on an odd CPU cycle, then 256 alternating read/write cycles.)
  • 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 $2003 should be set to 0 before the end of VBLANK to prevent potential OAM corruption (See: Errata).

References