PPU signals

From NESdev Wiki
Jump to navigationJump to search

The PPU has many internal signals for controlling its various actions during the frame. Each signal can control many things in the chip, and each of these can have a different amount of delay between the signal being generated and being used. These delays can be critical for correct operation.

H decoder

These signals are all generated from the current dot number. Some of these signals are combinations of multiple H decoder signals, and most of them are disabled during vblank (scanlines 240-260) and fblank. For two of them, they are disabled specifically during scanlines 240-261, but since this is so uncommon, it is called out as needed rather than in a dedicated column.

Dots Disabled during vblank/fblank Names Purpose
dot % 8 == 0..1 Yes Breaks: /F_NT Pattern address register control
  • (Delay: 1 dot) Pattern address register: During 2nd half even dots, this produces a signal that is used in the next half dot to latch data into the register used to generate pattern addresses. This signal latches OAM buffer bit 0 into the bit 12 register, bits 7-0 of the PPU data bus (delayed 0.5 dots) and OAM buffer bytes into the bits 11-4 registers, and bits 3-0 of the OAM in-range result (delayed 0.5 dots) into bits 3-0. The address register selects which data to use for output based on the fetch sprites (256-319) signal.
  • (Delay: 1 dot) Sprite fetch: During 2nd half even dots, this latches the OAM buffer value's in-range result to be used while loading sprite shifters.

Commentary: This signal is used for creating pattern addresses for both background and sprites. The latching is delayed half a dot to allow the OAM tile byte to be loaded into the OAM buffer. Much of the data used for this register is latched, but the live inputs are used for the pattern table selection, sprite size, and background fine y.

dot % 8 == 2..3
(0..255, 320..335)
Yes Breaks: F_AT Background attributes control
  • (Delay: 0.5 dots) VRAM address mux: When a pattern address is not selected, this causes an attributes address to be used instead of a nametable address.
  • (Delay: 1 dot) Background rendering: During 2nd half even dots, this transfers the PPU data bus value into the attributes register.

Commentary: This signal is relevant for palette corruption. Palette corruption happens when the palette address changes during 2nd half dots, which normally happens from rendering toggles. The output from the VRAM address mux is used to select between the rendering pipeline output and the low 5 bits of v for the palette RAM address. The low 4 bits are not synchronized and can change any time. Rendering state changes the addresses sent to the VRAM address mux, allowing the mux's output to immediately change and switch the low 4 bits of the palette RAM address. Rendering state controls bits 13-12 of the attributes address sent to the mux, making them either a fixed %10 or bits 13-12 of v. If rendering turns off during an attributes fetch, this mutated address can cause corruption by changing the selected palette RAM address even if v itself wasn't pointing into palette RAM; this happens any time v & $3C00 == $3C00. At other times, the nametable address (which becomes v) becomes selected when rendering disables, so v has to point into palette RAM to trigger corruption (v & $3F00 == $3F00).

dot % 8 == 4..5
(0..255, 320..335)
Yes Breaks: F_TA Background low pattern register control
  • (Delay: 1 dot) Background rendering: During 2nd half even dots, this transfers the PPU data bus value into the low pattern bits register.
dot % 8 == 6..7
(0..255, 320..335)
Yes Breaks: F_TB Background sliver commit
  • (Delay: 1 dot) Background rendering: During 2nd half even dots, this increments coarse x and transfers data from the PPU data bus and pattern and attributes registers into the background shift registers.
0..7 No Breaks: CLIP_O / CLIP_B Clipping enable
  • (Delay: 2 dots) Background rendering: This signal permits the left column background masking bit in PPUCTRL to clear the pixels coming out of the background shift registers.
  • (Delay: 1.5 dots) Sprite rendering: This signal permits the left column sprite masking bit in PPUCTRL to clear the pixels coming out of the sprite shifters.

Commentary: This same clipping mechanism is used to block pixel output during not-visible pixels and when rendering components are disabled. This signal is actually a combination of a 0-7 or 256-263 signal and a !(0-255 on scanlines 0-239) signal, but only the 0-7 part of it can actually have any impact on the screen.

0..63 Yes Breaks: I_OAM2 OAM2 init
  • (Delay: 1 dot) OAM2 init: This forces $FF into the OAM buffer instead of the value read from OAM (so that $FF can be written to OAM2).
  • (Delay: 1.5 dots) OAM2 init: This suppresses OAM1ADDR increments that normally happen automatically during the 1st half visible odd dots.
  • (Delay: 1.5 dots) OAM2 init: During 1st half odd dots, this increments OAM2ADDR.
  • (Delay: 1 dot) Sprite evaluation: This forces the OAM in-range input to the sprite evaluation in-range circuit to 0 (out of range).
  • (Delay: 1 dot) Sprite evaluation: This continuously clears the eval_done signal (which, when set, forces the sprite evaluation version of the OAM in-range result to 0 and turns OAM2 writes into reads).
0..255
(scanlines 0-239)
Yes Breaks: /VIS Handle visible dots
  • (Delay: 1.5 dots) Background rendering: Outside this range, background pixels are clipped (forced to transparent).
  • (Delay: 2.5 dots) Sprite rendering: Outside this range, sprite pixels are clipped (forced to transparent).
  • (Delay: 1.5 dots) Sprite evaluation: During 1st half odd dots, this causes OAM1ADDR to increment unless suppressed by OAM2 init.
  • (Delay: 1 dot) Sprite evaluation: This signal causes odd dots to use OAM1ADDR to address OAM. Otherwise, all dots during rendering use OAM2ADDR.
  • (Delay: 1 dot) Sprite evaluation: When this signal is false, it forces the OAM in-range input to the sprite evaluation in-range circuit to 0 (out of range).
  • (Delay: 1 dot) Sprite evaluation / Sprite fetch: When this signal is false, the automatic writes to OAM on even dots (for writing to OAM2 during OAM2 init and sprite evaluation) are instead reads.
  • (Delay: 1 dot) Sprite rendering: During 2nd half odd dots, this allows sprite shifters to output-and-shift pixels for a dot if they are not counting.
  • (Delay: 1 dot) Sprite 0 hit: When this signal is false, the sprite 0 hit flag cannot be set.

Commentary: Most H decoder signals are enabled during the pre-render scanline, but this one is not. This produces unusual behavior for sprite rendering during the pre-render scanline in that it disables OAM2 init and sprite evaluation, but not sprite fetch. This usually (but not always) causes scanline 0 to be sprite-free.

0..255, 256..319 Yes Breaks: /FO Handle background
  • (Delay: 1 dot) Background rendering: During 2nd half dots, this clocks the background shift registers.

Commentary: This is the signal that limits the 'Every n..n+1' signals for background handling.

256..319 Yes Breaks: OBJ_READ Sprite fetch
  • (Delay: 1 dot) Sprite evaluation: This continuously resets OAM1ADDR ($2003).
  • (Delay: 1 dot) Sprite 0 hit: During 1st half dots, this enables the sprite_0_on_this_scanline flip-flop so it can store the current output of the sprite_0_on_next_scanline flip-flop.
  • (Delay: 1 dot) Sprite fetch: During 1st half dots, this increments OAM2ADDR (if it's not being otherwise suppressed) if bit 2 of the dot (delayed 0.5 dots) is 0. Because this sprite fetch signal also changes during 1st half dots, this creates asynchronous timing at the start of dot 321, where the signal goes from 1 to 0 while OAM2ADDR is using it to increment. In practice, this reliably causes an extra increment.
  • (Delay: 1 dot) Sprite fetch: This enables a demux that takes the low 3 bits of the dot number (delayed 1 dot) and produces 4 signals that tell the sprite shifters when to load each piece of data. The signals from the demux are latched during 2nd half dots.
  • (Delay: 1 dot) Pattern address register: During 2nd half dots, this selects between background and sprite data for the bits of the pattern address register.

Commentary: The extra OAM2ADDR increment that occurs on dot 321 is critical for software because it provides a wide window during sprite idle where rendering can be safely turned off without setting up the conditions for OAM corruption later on. Games rely on this behavior.

63, 255, 339 Yes Breaks: /EVAL OAM2ADDR reset
  • (Delay: 1.5 dots) OAM2 init / Sprite evaluation / Sprite fetch: During 1st half dots, this clears OAM2ADDR and oam2_overflow.
  • (Delay: 1.5 dots) Sprite evaluation / Sprite fetch: This prevents OAM2ADDR from being able to increment.
65 Yes Breaks: S_EV Sprite 0 detection
  • (Delay: 1 dot) Sprite 0 hit: During 1st half dots, this enables the sprite_0_on_next_scanline flip-flop so it can store the sprite evaluation version of the OAM in-range result.
255 Yes Breaks: E_EV Increment v
  • (Delay: 1 dot) Background rendering: During 2nd half dots, this increments the y component of v.
  • (Delay: 2 dots) Background rendering: During 2nd half dots, this copies the coarse x component of t to v.
339 Yes Breaks: 0_HPOS Start sprite shifter counters
  • (Delay: 2.5 dots) Sprite rendering: This sets an SR latch in each sprite shifter that makes the shifter count instead of output-and-shift pixels. When the counter expires, the latch is cleared.

Commentary: When the last dot of pre-render is skipped, this signal arrives at the sprite shifters one dot too late, so every shifter outputs its first pixel at X=0 and then they start counting on the next dot, outputting the remaining 7 pixels at their usual time.

339 Yes Breaks: /HPLA_5 End of scanline (odd frames)
  • (Delay: 0 dots) Frame end: This signal, along with a scanline 261 (delayed 1 dot) signal, allows an even/odd frame toggle to act as another input to the same end-of-scanline circuit as dot 340.
340 No Breaks: HPLA_23 End of scanline
  • (Delay: 0 dots) Scanline end: This signal increments the scanline counter.
  • (Delay: 0.5 dots) Scanline end / Frame end: This signal clears the dot counter. If the scanline is 261 (delayed 0.5 dots), it also clears the scanline counter.