PPU registers: Difference between revisions
Rainwarrior (talk | contribs) (→Mask ($2001) > write: rewriting to include "errata" and more information about rendering on/off) |
|||
Line 62: | Line 62: | ||
Is often referred as PPUMASK. | Is often referred as PPUMASK. | ||
This register controls | This register controls the rendering of sprites and backgrounds, as well as colour effects. | ||
7654 3210 | |||
|||| |||| | |||
|||| |||+- 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 | |||
||+------- Intensify reds (and darken other colors) | |||
|+-------- Intensify greens (and darken other colors) | |||
+--------- Intensify blues (and darken other colors) | |||
==== 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. (See: [[Mirroring#Vertical|Vertical Mirroring]]) | |||
[[Colour-emphasis games|A few games]], which set all three tint bits to darken all colors, are unplayable on these PPUs. | |||
* 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 ('''$2007'''). 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. | |||
* Note: PAL NES [http://forums.nesdev.org/viewtopic.php?p=131889#p131889 swaps green and red]. | |||
* 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. | |||
* The emphasis bits are applied after greyscale, which means they will still tint the color of the grey image. | |||
=== Status ($2002) < read === | === Status ($2002) < read === |
Revision as of 01:33, 3 February 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.
Controller ($2000) > write
Is often referred as PPUCTRL.
Various flags controlling PPU operation
7654 3210 |||| |||| |||| ||++- 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 PPU scroll):
7654 3210 || |+- 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 $2000 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
Is often referred as PPUMASK.
This register controls the rendering of sprites and backgrounds, as well as colour effects.
7654 3210 |||| |||| |||| |||+- 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 ||+------- Intensify reds (and darken other colors) |+-------- Intensify greens (and darken other colors) +--------- Intensify blues (and darken other colors)
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. (See: Vertical Mirroring)
- 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 ($2007). 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.
- Note: PAL NES swaps green and red.
- 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
Is often referred as PPUSTATUS.
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.
7654 3210 |||| |||| |||+-++++- 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 a 0 in D7 but clear the latch anyway, causing the program to miss frames. See NMI for details.
OAM address ($2003) > write
Is often referred as OAMADDR.
Write the address of OAM you want to access here. Most games just write $00 here and then use OAM_DMA ($4014). (DMA is implemented in the 2A03/7 chip and works by repeatedly writing $2004.)
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. 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
OAM data port, often referred as OAMDATA.
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.
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.
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 $4014, 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, 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. 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 (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
Is often referred as PPUSCROLL.
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
Is often referred as PPUADDR.
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
Is often referred as PPUDATA.
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 afterward 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.)