PPU glitches: Difference between revisions
(' not *) |
|||
Line 1: | Line 1: | ||
=Early Writes= | =Early Writes= | ||
The 6502, and thus also the 2A03, guarantee that R/W and address bus are stable while φ2 (or M2) are high, but do | The 6502, and thus also the 2A03, guarantee that R/W and address bus are stable while φ2 (or M2) are high, but do '''not''' guarantee the data bus is stable. | ||
Unfortunately, the PPU generally assumes that the data bus is stable during the entire time, and this causes a number of glitches in the PPU's behavior. | Unfortunately, the PPU generally assumes that the data bus is stable during the entire time, and this causes a number of glitches in the PPU's behavior. | ||
Line 21: | Line 21: | ||
In the 2C02A, it's known that the $18 bits in PPUMASK are also processed in an asynchronous manner, and suspected that all the other bits do also: | In the 2C02A, it's known that the $18 bits in PPUMASK are also processed in an asynchronous manner, and suspected that all the other bits do also: | ||
* $04s bit: On the 2C02A, it's believed that this will have no effect, because it would have to be in the middle of incrementing the PPU address | * $04s bit: On the 2C02A, it's believed that this will have no effect, because it would have to be in the middle of incrementing the PPU address while rendering is disabled and a write to PPUCTRL happened. | ||
* $08s bit: On the 2C02A, it's believed that a write during horizontal blanking could cause exactly one bitplane of one sliver of one sprite to be fetched from the wrong pattern table. | * $08s bit: On the 2C02A, it's believed that a write during horizontal blanking could cause exactly one bitplane of one sliver of one sprite to be fetched from the wrong pattern table. | ||
Line 35: | Line 35: | ||
* $01s bit: Any write to PPUCTRL can, at any time, turn off the "monochrome" flag for one pixel. [https://forums.nesdev.org/viewtopic.php?p=256431#p256431] | * $01s bit: Any write to PPUCTRL can, at any time, turn off the "monochrome" flag for one pixel. [https://forums.nesdev.org/viewtopic.php?p=256431#p256431] | ||
* $80s bit: Any write to PPUCTRL can, regardless of subpixel phase, turn off "blue emphasis" for | * $80s bit: Any write to PPUCTRL can, regardless of subpixel phase, turn off "blue emphasis" for '''half''' of one pixel. [https://forums.nesdev.org/viewtopic.php?p=256737#p256737] | ||
In the 2C02A, it's known that the $18 bits in PPUMASK are also processed in an asynchronous manner, and suspected that all the other bits do also: | In the 2C02A, it's known that the $18 bits in PPUMASK are also processed in an asynchronous manner, and suspected that all the other bits do also: |
Revision as of 02:05, 14 July 2022
Early Writes
The 6502, and thus also the 2A03, guarantee that R/W and address bus are stable while φ2 (or M2) are high, but do not guarantee the data bus is stable.
Unfortunately, the PPU generally assumes that the data bus is stable during the entire time, and this causes a number of glitches in the PPU's behavior.
These can be mostly worked around by writing to the PPU register mirror where the relevant bit is either the same as the old value or the new value.
PPUCTRL
In the 2C02G, the $C3 bits are processed in an asynchronous manner, and this can cause various problems:
- $01s bit: On every active scanline, a write to PPUCTRL at the exact wrong time (write starting on dot 257, ending on dot 258) can cause the left nametable to be drawn for the upcoming scanline. This is because the contents of open bus - $20 - are used by the PPU on dot 257, setting the "base nametable" to the left one instead of the intended one. As a work-around, write to the PPU address mirror where the bottom 2 bits of the upper byte of the address match the data that will be written.
- $02s bit: On every active field, a write to PPUCTRL at the exact wrong time (starting on prerender scanline dot 304, ending on dot 305) can cause the top nametable to be drawn for the upcoming field. Workaround is same as above.
- $40s bit: Any write to PPUCTRL during the active field can temporarily disable "output colors on EXT pins" for one pixel. This is believed to be the cause of certain bugs reported in the HDNES.
- $80s bit: Any write to PPUCTRL during the vertical blanking interval can cause the NMI output to be asserted, or deasserted, for about 80ns. However, this glitch is invisible, because the 6502 ignores its NMI input during this time.
In the 2C02A, it's known that the $18 bits in PPUMASK are also processed in an asynchronous manner, and suspected that all the other bits do also:
- $04s bit: On the 2C02A, it's believed that this will have no effect, because it would have to be in the middle of incrementing the PPU address while rendering is disabled and a write to PPUCTRL happened.
- $08s bit: On the 2C02A, it's believed that a write during horizontal blanking could cause exactly one bitplane of one sliver of one sprite to be fetched from the wrong pattern table.
- $10s bit: On the 2C02A, it's believed that a write during active redraw could cause exactly one bitplane of one sliver of one background tile to be fetched from the wrong pattern table.
- $20s bit: On the 2C02A, it's believed that a write at any time could cause one sprite sliver could be drawn incorrectly, specifics are unclear.
PPUMASK
On the 2C02G, the $81 bits are processed in an asynchronous manner and this can cause unimportant glitches:
- $01s bit: Any write to PPUCTRL can, at any time, turn off the "monochrome" flag for one pixel. [1]
- $80s bit: Any write to PPUCTRL can, regardless of subpixel phase, turn off "blue emphasis" for half of one pixel. [2]
In the 2C02A, it's known that the $18 bits in PPUMASK are also processed in an asynchronous manner, and suspected that all the other bits do also:
- $18 bits: On the 2C02A, it's known that any write to PPUCTRL during active redraw will turn off rendering for one pixel, causing all the documented hazards with disabling rendering.
- $06 bits??
- $60 bits: on the 2C02A, it is suspected that any write to PPUMASK will turn off "red" or "green" emphasis for half of one pixel
OAMADDR
On the 2C02G, it is suspected that issues with writes to OAMADDR causing sprite corruption are due to this behavior.
PPUADDR, PPUSCROLL
On the 2C02G, any write starting on dot 257 and ending on dot 258 that updates coarse X in "t" can cause the same symptoms as writes to PPUCTRL.
PPU-internal bus conflicts
Any the PPU tries to both increment "v" at the same time it tries to reload "v" from "t" causes a bus conflict, resulting in the AND of the two inputs.
This can happen (at least) two different ways:
- A second write of PPUADDR (loading a new scroll location) at the same time that the Y bits are incremented (dot 256) or the same times at the coarse X bits are incremented.
- A read or write of PPUDATA (incrementing the fine Y and coarse X bits) at the correct amount of time before rendering would naturally reload "v" from "t"