Errata: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(→‎Video: separating OAM into its own subheading, adding note about OAM decay)
(→‎Video: Palette corruption)
 
(31 intermediate revisions by 7 users not shown)
Line 6: Line 6:


* Setting the VRAM address using [[PPUADDR]] ($2006) corrupts the [[PPU scrolling|scroll position]]. (Workaround: Reset the scroll position using [[PPUSCROLL]] ($2005) and [[PPUCTRL]] ($2000) after finishing all background updates.)
* Setting the VRAM address using [[PPUADDR]] ($2006) corrupts the [[PPU scrolling|scroll position]]. (Workaround: Reset the scroll position using [[PPUSCROLL]] ($2005) and [[PPUCTRL]] ($2000) after finishing all background updates.)
* Reading [[PPUSTATUS]] ($2002) at the exact same time that [[PPUSTATUS]] bit 7 goes high at the start of vertical blanking [[NMI#Race condition|keeps $2002.D7 from going high at all]] that frame. (Workaround: Use NMI to wait for vertical blanking.)
* Reading [[PPUSTATUS]] ($2002) at the exact same time that [[PPUSTATUS]] bit 7 goes high at the start of vertical blanking [[NMI#Race condition|keeps $2002.D7 from going high at all]] that frame. (Workaround: Use NMI to wait for vertical blanking.)
 
* If the program changes the vblank NMI from disabled to enabled through [[PPUCTRL]] bit 7 while the vblank flag ([[PPUSTATUS]] bit 7) is set, an NMI will trigger immediately. This can cause NMI to occur other than at the start of vblank, or cause more than one NMI in a single vblank, as long as it is still during vertical blanking and the program has not yet read PPUSTATUS. (Workaround: Read PPUSTATUS shortly before enabling NMIs.)
* When writing to [[PPUCTRL]] ($2000) at the exact start of horizontal blanking may cause the PPU to start reading from the left name table instead of the right. (Workarounds: 1- Use horizontal or one-screen mirroring, or 2- Don't write to [[PPUCTRL]] outside vertical or forced blanking except as part of a properly timed raster effect. If you are writing to [[PPUCTRL]] as a way of temporarily preventing the NMI handler from being called while it is already running, don't disable NMI through [[PPUCTRL]]. Instead, use a variable to lock out reentrant NMI, and check this variable at the beginning of your NMI handler.)<ref>Random glitchy line in Super Mario Bros. on real hardware? http://forums.nesdev.org/viewtopic.php?f=2&t=10104</ref>
* Writing to [[PPUCTRL]] ($2000) at the exact start of horizontal blanking may cause the PPU to start reading from the left name table instead of the right. Workarounds:
 
*# Use horizontal or one-screen mirroring.
*# Don't write to [[PPUCTRL]] outside vertical or forced blanking except as part of a properly timed raster effect. If you are writing to [[PPUCTRL]] as a way of temporarily preventing the NMI handler from being called while it is already running, don't disable NMI through [[PPUCTRL]]. Instead, use a variable to lock out reentrant NMI, and check this variable at the beginning of your NMI handler.<ref>[http://forums.nesdev.org/viewtopic.php?f=2&t=10104 Forum]: Random glitchy line in Super Mario Bros. on real hardware?</ref>
*# Write to either $2000 or the mirror of [[PPUCTRL]] at $2100, depending on the desired value of the least significant bit that will be written to [[PPUCTRL]].<ref>[https://forums.nesdev.org/viewtopic.php?p=230434#p230434 Forum]: 2nd2006_next_level test rom and extensions</ref>
* After reset ends (by CIC or reset button), the [[PPU power up state|PPU refuses to accept data]] written to the registers at [[PPUCTRL]], [[PPUMASK]], [[PPUSCROLL]] ($2005), and [[PPUADDR]] ($2006) for about the length of one frame. NES startup code should wait at least this long after reset before using the PPU (see: [[Init code]]).
* After reset ends (by CIC or reset button), the [[PPU power up state|PPU refuses to accept data]] written to the registers at [[PPUCTRL]], [[PPUMASK]], [[PPUSCROLL]] ($2005), and [[PPUADDR]] ($2006) for about the length of one frame. NES startup code should wait at least this long after reset before using the PPU (see: [[Init code]]).
 
* The VBlank flag (bit 7) of [[PPUSTATUS]] is not cleared on reset, only power-up.
* The VBlank flag in $2002.D7 is not cleared on reset, only power-up.
 
* Some 6502 write instructions produce an extra read or write as part of their operation, which produces unexpected results when used with PPU registers [[OAMDATA]] ($2004) and [[PPUDATA]] ($2007). This includes all read-modify-write instructions (ASL, LSR, ROL, ROR, DEC, INC) as well as indexed addressing instructions (e.g. STA $2000, X). Spurious reads or writes that operate on these PPU registers are a problem, because they have the side effect of incrementing its internal write address.
* Some 6502 write instructions produce an extra read or write as part of their operation, which produces unexpected results when used with PPU registers [[OAMDATA]] ($2004) and [[PPUDATA]] ($2007). This includes all read-modify-write instructions (ASL, LSR, ROL, ROR, DEC, INC) as well as indexed addressing instructions (e.g. STA $2000, X). Spurious reads or writes that operate on these PPU registers are a problem, because they have the side effect of incrementing its internal write address.
 
* [2C07 and Dendy only] The PAL red and green emphasis bits are swapped with respect to the 2C02 (NTSC).
* [2C07 and Dendy only] The red and green emphasis bits are opposite in meaning from the 2C02.
 
* [2A03] DMC DMA during reads from $2007 can cause an extra read signal, causing a lost byte. This issue can also affect $2002 vblank polling (though this is already unreliable, see above). Workaround: disable DMC while reading from PPU.
* [2A03] DMC DMA during reads from $2007 can cause an extra read signal, causing a lost byte. This issue can also affect $2002 vblank polling (though this is already unreliable, see above). Workaround: disable DMC while reading from PPU.
* Setting bit 6 of PPUCTRL (EXTBG direction) on a stock NES to 1 (output) causes a bus conflict that can potentially damage the PPU. See ''[[PPU_registers#Master/slave_mode_and_the_EXT_pins|master/slave_mode_and_the_EXT_pins]]''.
* Setting bit 6 of PPUCTRL (EXTBG direction) on a stock NES to 1 (output) causes a bus conflict that can potentially damage the PPU. See ''[[PPU_registers#Master/slave_mode_and_the_EXT_pins|master/slave_mode_and_the_EXT_pins]]''.
* A Y scroll position in 240-255 is [[PPU scrolling#Y increment|treated as "negative"]], rendering the attribute table as two rows of garbage tiles.
* Setting PPUADDR to a palette address while rendering is disabled will cause the PPU to render the color at that address. This means updating palettes outside of vblank can cause a flash even if rendering is disabled.
* Changing PPUADDR from $3Fxy (y≠0) to any non-palette address may, in unknown circumstances, corrupt some colors in the palette. See ''[[PPU_registers#Palette_corruption|PPUADDR § Palette corruption]]
* Color $0D in the palette is "blacker than black" and causes image stability problems on some TVs.  Do not use this color.
* In a mid-scanline first write to [[PPUSCROLL]], the PPU starts reading the value a bit too early, catching a CPU open bus value that usually comes from the high bit of the address and setting the fine X scroll to open bus D2-D0 for about a pixel. Workaround: If writing fine X early, write to a PPUSCROLL mirror at $2005, $2105, ..., or $2705 to match either the old or new fine X value.


=== OAM and Sprites ===
=== OAM and Sprites ===


* Sprite 0 hit does not trigger at x=255.
* Sprite 0 hit does not trigger at x=255.
* Sprite overflow is unreliable due to errors in its implementation. The internal copy to secondary OAM causes a [[PPU sprite evaluation|diagonal fetch pattern]], causing both false positives and false negatives in the sprite overflow bit. (Workaround: Make sure the ninth sprite immediately follows the eighth, and use sprite overflow only to time the top of the screen, not the bottom.) See: [[PPU sprite evaluation#Sprite overflow bug|Sprite overflow bug]].
* Sprite overflow is unreliable due to errors in its implementation. The internal copy to secondary OAM causes a [[PPU sprite evaluation|diagonal fetch pattern]], causing both false positives and false negatives in the sprite overflow bit. (Workaround: Make sure the ninth sprite immediately follows the eighth, and use sprite overflow only to time the top of the screen, not the bottom.) See: [[PPU sprite evaluation#Sprite overflow bug|Sprite overflow bug]].
* Because OAM is DRAM, which needs to be refreshed frequently, the contents of OAM begin to decay quickly when rendering is turned off via [[PPUMASK]] ($2001). During rendering, sprite evaluation will continually refresh the OAM data. Caveats and workarounds:
* Because OAM is DRAM, which needs to be refreshed frequently, the contents of OAM begin to decay quickly when rendering is turned off via [[PPUMASK]] ($2001). During rendering, sprite evaluation will continually refresh the OAM data. Caveats and workarounds:
** The data remains reliably intact for about the length of NTSC's vblank, but longer than this and it will begin to corrupt itself.
** The data remains reliably intact for about the length of NTSC's vblank, but longer than this and it will begin to corrupt itself.<ref>[http://forums.nesdev.org/viewtopic.php?f=9&t=9912 Forum]: Just how cranky is the PPU OAM?</ref>
** Using OAM DMA ($4014) during vblank is the only reliable way to fill the entire OAM buffer during vblank.
** Using OAM DMA ($4014) during vblank is usually the best way to fill the entire OAM buffer during vblank.
** Writes to OAMDATA ($2004) are usually too slow to fill the entire OAM buffer before it begins to decay. It can be used during vblank to update a few bytes of OAM.
** Writes to OAMDATA ($2004) are usually too slow to fill the entire OAM buffer before it begins to decay. It can be used during vblank to update a few bytes of OAM.
** PAL systems must execute OAM DMA early within vblank, because sprite evaluation begins partway through vblank to keep it refreshed during its extended duration.<ref>[http://forums.nesdev.org/viewtopic.php?f=9&t=11041 Forum thread]: PAL OAM reliability during vblank.</ref>
** PAL systems must execute OAM DMA early within vblank, because sprite evaluation begins partway through vblank to keep it refreshed during its extended duration.<ref>[http://forums.nesdev.org/viewtopic.php?f=9&t=11041 Forum]: PAL OAM reliability during vblank.</ref>
 
* Reading from OAM is inconsistent or unusable depending on the hardware revision. This is due to differences in its DRAM controller, and its lack of reliability in edge cases:
* Reading from OAM is inconsistent or unusable depending on the hardware revision. This is due to differences in its DRAM controller, and its lack of reliability in edge cases:
** [2C02 through 2C02E (early Famicoms and very early NESes)] OAM is simply never readable.
** [2C02 through 2C02E (early Famicoms and very early NESes), 2C03B, 2C03C (Vs. System PPUs)] OAM is simply never readable.
** [2C02 only] Writes to [[OAMADDR]] ($2003) corrupt OAM. (Workaround: Rewrite entire OAM before rendering starts, possibly using DMA initiated by writes to $4014, or rely on [[OAMADDR]] being 0 at end of rendering.)
** [2C02G, 2C03G, and 2C02H] Writes to [[OAMADDR]] ($2003) corrupt OAM. (Workaround: Rewrite entire OAM before rendering starts, possibly using DMA initiated by writes to $4014, or rely on [[OAMADDR]] being 0 at end of rendering.)
** [2C07 only] OAM can only be accessed for the 20 scanlines after the NMI would have happened. (To compensate for PAL's longer vblank period, the 2C07 always enables the OAM refresh logic, regardless of whether rendering is enabled.)
** [2C07 and 2C07A] OAM cannot be accessed from scanlines 21 through 70, after the NMI would have happened. (To compensate for PAL's longer vblank period, the 2C07 always enables the OAM refresh logic for part of the blanking period.)
** Leaving the value in [[OAMADDR]] (either written or by autoincrement) at a value of eight or greater before rendering starts causes minor OAM corruption, copying the eight bytes at ''OAMADDR&~7'' to the beginning of OAM.
* [2C02G and 2C02H, others?] Leaving the value in [[OAMADDR]] (either written or by autoincrement) at a value of eight or greater before rendering starts causes minor OAM corruption, copying the eight bytes at ''OAMADDR&~7'' to the beginning of OAM.<ref>[http://forums.nesdev.org/viewtopic.php?p=110019#p110019 Forum]: Re: Just how cranky is the PPU OAM? (test notes by quietust)</ref>
** Turning rendering off in [[PPUMASK]] ($2001) before the PPU has finished [[PPU sprite evaluation|evaluating sprites for that line]] (x=192 for lines with no sprites, x=240 for lines with at least one sprite) can corrupt OAM, leading to sprite flicker.
* Turning rendering off or on mid-frame via [[PPUMASK]] ($2001) has multiple issues:
** See: [[PPU sprite evaluation#Rendering disable or enable during active scanline|PPU sprite evaluation: Rendering disable or enable during active scanline]].
** Turning rendering off before the PPU has finished [[PPU sprite evaluation|evaluating sprites for that line]] (x=192 for lines with no sprites, x=240 for lines with at least one sprite) can corrupt OAM, leading to sprite flicker.
** Turning rendering on mid-frame has other problems not fully described yet.


== Input ==
== Input ==
* DMC DMA during a controller read ($4016/$4017) causes double clocking, which causes bits of the report to be skipped. A common symptom is spurious presses of Right. Workarounds:
* [2A03] DMC DMA during a controller read ($4016/$4017) causes double clocking, which causes bits of the report to be skipped. A common symptom is spurious presses of Right. Workarounds:
** [[Standard controller]] / [[Power Pad]]: read the controller multiple times to make sure valid input is read.
** [[Standard controller]] / [[Power Pad]]: read the controller multiple times to make sure valid input is read.
** [[Four Score]]: check for signature.
** [[Four Score]]: check for signature.
** [[Arkanoid controller]]: prevent a sudden single frame acceleration.
** [[Arkanoid controller]]: prevent a sudden single frame acceleration.
** If already using DMC IRQ: read the controller in the DMC IRQ handler.
** If already using DMC IRQ: read the controller in the DMC IRQ handler.
** Run an [[Controller reading code#DPCM Safety using OAM DMA|OAM DMA before reading the controller]] to align the CPU clock to the even/odd phase of the APU clock.


== Audio ==
== Audio ==


=== [[APU Pulse]] ===
=== [[APU Pulse]] ===
* In sweep decrease mode, the carry input differs between the two channels, causing a slightly different sweep rate.
* In sweep decrease mode, the carry input differs between the two channels, causing a slightly different sweep rate.
* Channels set to low frequencies can get silenced by the sweep unit if it is left in increase mode, even if the sweep is otherwise disabled. (Workaround: Write $08 to $4001 and $4005 to use decrease mode while disabling sweep.)
* Channels set to low frequencies can get silenced by the sweep unit if it is left in increase mode, even if the sweep is otherwise disabled. Workaround:
* Writing to $4003 or $4007 to change the high byte of the period while a note is playing causes a click as the phase resets. (Workaround: Write $4003 and $4007 only when they have changed. If using software sweeps, use hardware sweep and $4017 writes to change the high bit.)
** Write $08 to $4001 and $4005 to use decrease mode while disabling sweep.
* Writing to $4003 or $4007 to change the high byte of the period while a note is playing causes a click as the phase resets. Workarounds:
*# Write $4003 and $4007 only when they have changed.
*# The hardware sweep can be used to change the high period bits by clocking it immediately with a write to $4017.<ref>[http://forums.nesdev.org/viewtopic.php?t=231 Forum]: Vibrato on square without phase reset.</ref> (Advanced technique.)


=== [[APU DMC]] ===
=== [[APU DMC]] ===


* The length counter for DPCM samples ends up reading 1 byte past the end of an otherwise 16 byte aligned sample. This creates a need for 15 bytes of padding between samples.
* The length counter for DPCM samples ends up reading 1 byte past the end of an otherwise 16 byte aligned sample. This creates a need for 15 bytes of padding between samples.
* The sample playback frequency table contains a set of 16 pitches tuned to a standard A-440 scale. These appear to have been designed for a limited wavetable synthesis using looped samples, but because of the +1 modifier on sample length, the wavelength is detuned.
* The sample playback frequency table contains a set of 16 pitches tuned to a standard A-440 scale. These appear to have been designed for a limited wavetable synthesis using looped samples, but because of the +1 modifier on sample length, the wavelength is detuned. (Workaround: Use a 17-byte loop and the key of B major.)
* The frequency table on PAL systems contains 2 slight tuning errors ($4 and $C).<ref>[http://forums.nesdev.org/viewtopic.php?p=94079#p94079 Forum post]: PAL DPCM frequency table contains 2 errors.</ref>
* The frequency table on PAL systems contains 2 slight tuning errors ($4 and $C).<ref>[http://forums.nesdev.org/viewtopic.php?p=94079#p94079 Forum]: PAL DPCM frequency table contains 2 errors.</ref>
* Playback of samples generates occasional conflicts with controller reads through $4016/4017 (see above: [[#Input|Input]]), as well as PPU reads through $2007 (see above: [[#Video|Video]]). When using DPCM samples, the code must work around these conflicts.
* Playback of samples generates occasional conflicts with controller reads through $4016/4017 (see above: [[#Input|Input]]), as well as PPU reads through $2007 (see above: [[#Video|Video]]). When using DPCM samples, the code must work around these conflicts.


== CPU ==
== CPU ==
The 6502 has several hardware gotchas (adapted from [http://forum.6502.org/viewtopic.php?t=770 this 6502 forum discussion] and [[wikipedia:MOS Technology 6502#Bugs and quirks|Wikipedia's article on the 6502]]).
The NES CPU has several hardware gotchas, most of which are inherited from the MOS 6502 it is derived from:<ref>[http://forum.6502.org/viewtopic.php?t=770 6502.org forum]: 6502 hardware gotchas.</ref><ref>[[wikipedia:MOS Technology 6502#Bugs and quirks|Wikipedia's article on the 6502]].</ref>
However, it should be safer to rely on the page wrapping than on the NES-specific gotchas above because 6502 variants with different wrapping behavior also have the [[CPU unofficial opcodes]] removed, and a few later licensed games rely on unofficial opcodes.


* ''JMP ($xxyy)'', or JMP indirect, does not advance pages if the lower eight bits of the specified address is $FF; the upper eight bits are fetched from ''$xx00'', 255 bytes earlier, instead of the expected following byte.
* ''JMP ($xxyy)'', or JMP indirect, does not advance pages if the lower eight bits of the specified address is $FF; the upper eight bits are fetched from ''$xx00'', 255 bytes earlier, instead of the expected following byte.
Line 72: Line 77:
* BRK, IRQ, or NMI can mask each other under certain conditions. (see Visual6502 wiki [http://visual6502.org/wiki/index.php?title=6502_BRK_and_B_bit] and [http://visual6502.org/wiki/index.php?title=6502_Timing_of_Interrupt_Handling] ) Not all can happen on the NES.
* BRK, IRQ, or NMI can mask each other under certain conditions. (see Visual6502 wiki [http://visual6502.org/wiki/index.php?title=6502_BRK_and_B_bit] and [http://visual6502.org/wiki/index.php?title=6502_Timing_of_Interrupt_Handling] ) Not all can happen on the NES.
* Decimal mode was disconnected from the ALU in the NES's second-source 6502 to save on patent royalties. Some famiclones, however, use an authentic 6502 with a working decimal mode. (Workaround: Don't SED, and convert binary numbers to decimal when displaying them.)
* Decimal mode was disconnected from the ALU in the NES's second-source 6502 to save on patent royalties. Some famiclones, however, use an authentic 6502 with a working decimal mode. (Workaround: Don't SED, and convert binary numbers to decimal when displaying them.)
* The 6502 has several [[CPU unofficial opcodes|unofficial opcodes]] that were not part of its specification, but do reliably exist on the Famicom and NES. Code using these opcodes may not be portable to other variants of the 6502 CPU (e.g. SNES). Some games rely on the operation of these unofficial opcodes.
* Page wrapping behavior is reliable on the Famicom/NES but may not be portable to other variants of the 6502 CPU.


== References ==
== References ==
<references>
<references />

Latest revision as of 05:16, 29 March 2023

This page describes aspects of the NES hardware that may be considered an error in the implementation (errata), or just otherwise unintuitive or unexpected behaviour.

Many of these issues are poorly emulated, and can cause program compatibility problems, especially for homebrew games not tested on hardware.

Video

  • Setting the VRAM address using PPUADDR ($2006) corrupts the scroll position. (Workaround: Reset the scroll position using PPUSCROLL ($2005) and PPUCTRL ($2000) after finishing all background updates.)
  • Reading PPUSTATUS ($2002) at the exact same time that PPUSTATUS bit 7 goes high at the start of vertical blanking keeps $2002.D7 from going high at all that frame. (Workaround: Use NMI to wait for vertical blanking.)
  • If the program changes the vblank NMI from disabled to enabled through PPUCTRL bit 7 while the vblank flag (PPUSTATUS bit 7) is set, an NMI will trigger immediately. This can cause NMI to occur other than at the start of vblank, or cause more than one NMI in a single vblank, as long as it is still during vertical blanking and the program has not yet read PPUSTATUS. (Workaround: Read PPUSTATUS shortly before enabling NMIs.)
  • Writing to PPUCTRL ($2000) at the exact start of horizontal blanking may cause the PPU to start reading from the left name table instead of the right. Workarounds:
    1. Use horizontal or one-screen mirroring.
    2. Don't write to PPUCTRL outside vertical or forced blanking except as part of a properly timed raster effect. If you are writing to PPUCTRL as a way of temporarily preventing the NMI handler from being called while it is already running, don't disable NMI through PPUCTRL. Instead, use a variable to lock out reentrant NMI, and check this variable at the beginning of your NMI handler.[1]
    3. Write to either $2000 or the mirror of PPUCTRL at $2100, depending on the desired value of the least significant bit that will be written to PPUCTRL.[2]
  • After reset ends (by CIC or reset button), the PPU refuses to accept data written to the registers at PPUCTRL, PPUMASK, PPUSCROLL ($2005), and PPUADDR ($2006) for about the length of one frame. NES startup code should wait at least this long after reset before using the PPU (see: Init code).
  • The VBlank flag (bit 7) of PPUSTATUS is not cleared on reset, only power-up.
  • Some 6502 write instructions produce an extra read or write as part of their operation, which produces unexpected results when used with PPU registers OAMDATA ($2004) and PPUDATA ($2007). This includes all read-modify-write instructions (ASL, LSR, ROL, ROR, DEC, INC) as well as indexed addressing instructions (e.g. STA $2000, X). Spurious reads or writes that operate on these PPU registers are a problem, because they have the side effect of incrementing its internal write address.
  • [2C07 and Dendy only] The PAL red and green emphasis bits are swapped with respect to the 2C02 (NTSC).
  • [2A03] DMC DMA during reads from $2007 can cause an extra read signal, causing a lost byte. This issue can also affect $2002 vblank polling (though this is already unreliable, see above). Workaround: disable DMC while reading from PPU.
  • Setting bit 6 of PPUCTRL (EXTBG direction) on a stock NES to 1 (output) causes a bus conflict that can potentially damage the PPU. See master/slave_mode_and_the_EXT_pins.
  • A Y scroll position in 240-255 is treated as "negative", rendering the attribute table as two rows of garbage tiles.
  • Setting PPUADDR to a palette address while rendering is disabled will cause the PPU to render the color at that address. This means updating palettes outside of vblank can cause a flash even if rendering is disabled.
  • Changing PPUADDR from $3Fxy (y≠0) to any non-palette address may, in unknown circumstances, corrupt some colors in the palette. See PPUADDR § Palette corruption
  • Color $0D in the palette is "blacker than black" and causes image stability problems on some TVs. Do not use this color.
  • In a mid-scanline first write to PPUSCROLL, the PPU starts reading the value a bit too early, catching a CPU open bus value that usually comes from the high bit of the address and setting the fine X scroll to open bus D2-D0 for about a pixel. Workaround: If writing fine X early, write to a PPUSCROLL mirror at $2005, $2105, ..., or $2705 to match either the old or new fine X value.

OAM and Sprites

  • Sprite 0 hit does not trigger at x=255.
  • Sprite overflow is unreliable due to errors in its implementation. The internal copy to secondary OAM causes a diagonal fetch pattern, causing both false positives and false negatives in the sprite overflow bit. (Workaround: Make sure the ninth sprite immediately follows the eighth, and use sprite overflow only to time the top of the screen, not the bottom.) See: Sprite overflow bug.
  • Because OAM is DRAM, which needs to be refreshed frequently, the contents of OAM begin to decay quickly when rendering is turned off via PPUMASK ($2001). During rendering, sprite evaluation will continually refresh the OAM data. Caveats and workarounds:
    • The data remains reliably intact for about the length of NTSC's vblank, but longer than this and it will begin to corrupt itself.[3]
    • Using OAM DMA ($4014) during vblank is usually the best way to fill the entire OAM buffer during vblank.
    • Writes to OAMDATA ($2004) are usually too slow to fill the entire OAM buffer before it begins to decay. It can be used during vblank to update a few bytes of OAM.
    • PAL systems must execute OAM DMA early within vblank, because sprite evaluation begins partway through vblank to keep it refreshed during its extended duration.[4]
  • Reading from OAM is inconsistent or unusable depending on the hardware revision. This is due to differences in its DRAM controller, and its lack of reliability in edge cases:
    • [2C02 through 2C02E (early Famicoms and very early NESes), 2C03B, 2C03C (Vs. System PPUs)] OAM is simply never readable.
    • [2C02G, 2C03G, and 2C02H] Writes to OAMADDR ($2003) corrupt OAM. (Workaround: Rewrite entire OAM before rendering starts, possibly using DMA initiated by writes to $4014, or rely on OAMADDR being 0 at end of rendering.)
    • [2C07 and 2C07A] OAM cannot be accessed from scanlines 21 through 70, after the NMI would have happened. (To compensate for PAL's longer vblank period, the 2C07 always enables the OAM refresh logic for part of the blanking period.)
  • [2C02G and 2C02H, others?] Leaving the value in OAMADDR (either written or by autoincrement) at a value of eight or greater before rendering starts causes minor OAM corruption, copying the eight bytes at OAMADDR&~7 to the beginning of OAM.[5]
  • Turning rendering off or on mid-frame via PPUMASK ($2001) has multiple issues:

Input

  • [2A03] DMC DMA during a controller read ($4016/$4017) causes double clocking, which causes bits of the report to be skipped. A common symptom is spurious presses of Right. Workarounds:

Audio

APU Pulse

  • In sweep decrease mode, the carry input differs between the two channels, causing a slightly different sweep rate.
  • Channels set to low frequencies can get silenced by the sweep unit if it is left in increase mode, even if the sweep is otherwise disabled. Workaround:
    • Write $08 to $4001 and $4005 to use decrease mode while disabling sweep.
  • Writing to $4003 or $4007 to change the high byte of the period while a note is playing causes a click as the phase resets. Workarounds:
    1. Write $4003 and $4007 only when they have changed.
    2. The hardware sweep can be used to change the high period bits by clocking it immediately with a write to $4017.[6] (Advanced technique.)

APU DMC

  • The length counter for DPCM samples ends up reading 1 byte past the end of an otherwise 16 byte aligned sample. This creates a need for 15 bytes of padding between samples.
  • The sample playback frequency table contains a set of 16 pitches tuned to a standard A-440 scale. These appear to have been designed for a limited wavetable synthesis using looped samples, but because of the +1 modifier on sample length, the wavelength is detuned. (Workaround: Use a 17-byte loop and the key of B major.)
  • The frequency table on PAL systems contains 2 slight tuning errors ($4 and $C).[7]
  • Playback of samples generates occasional conflicts with controller reads through $4016/4017 (see above: Input), as well as PPU reads through $2007 (see above: Video). When using DPCM samples, the code must work around these conflicts.

CPU

The NES CPU has several hardware gotchas, most of which are inherited from the MOS 6502 it is derived from:[8][9]

  • JMP ($xxyy), or JMP indirect, does not advance pages if the lower eight bits of the specified address is $FF; the upper eight bits are fetched from $xx00, 255 bytes earlier, instead of the expected following byte.
  • All of the zero page addressing modes wrap within the zero page. The $xx,x , $xx,y, and ($xx,x) addressing modes all count 254,255,0,1…; none advance 254,255,256,257…
  • The ($xx),y addressing mode wraps when fetching the indirect address if the lower eight bits are stored at $FF (the upper eight bits are fetched from $0000, not $0100).
  • BRK, IRQ, or NMI can mask each other under certain conditions. (see Visual6502 wiki [1] and [2] ) Not all can happen on the NES.
  • Decimal mode was disconnected from the ALU in the NES's second-source 6502 to save on patent royalties. Some famiclones, however, use an authentic 6502 with a working decimal mode. (Workaround: Don't SED, and convert binary numbers to decimal when displaying them.)
  • The 6502 has several unofficial opcodes that were not part of its specification, but do reliably exist on the Famicom and NES. Code using these opcodes may not be portable to other variants of the 6502 CPU (e.g. SNES). Some games rely on the operation of these unofficial opcodes.
  • Page wrapping behavior is reliable on the Famicom/NES but may not be portable to other variants of the 6502 CPU.

References

  1. Forum: Random glitchy line in Super Mario Bros. on real hardware?
  2. Forum: 2nd2006_next_level test rom and extensions
  3. Forum: Just how cranky is the PPU OAM?
  4. Forum: PAL OAM reliability during vblank.
  5. Forum: Re: Just how cranky is the PPU OAM? (test notes by quietust)
  6. Forum: Vibrato on square without phase reset.
  7. Forum: PAL DPCM frequency table contains 2 errors.
  8. 6502.org forum: 6502 hardware gotchas.
  9. Wikipedia's article on the 6502.