From SNESdev Wiki
Jump to navigationJump to search

This page describes quirks in the SNES hardware that programmers need to be aware of. They could be mistakes in the hardware's implementation, or just unintuitive behavior.


  • Offset-per-tile never affects the first (leftmost) tile. This is to compensate for a horizontal scroll with a partial column on each end, allowing all 33 visible tiles to have a unique offset.
  • When color math is set to affect sprites, it will only affect sprites using the last four palettes.
  • If the program changes the vblank NMI from disabled to enabled through NMITIMEN bit 7 while the vblank flag (RDNMI 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 RDNMI. (Workaround: Read RDNMI shortly before enabling NMIs.)
  • When there are too many sprite slivers on a scanline, the SNES will drop the highest priority slivers instead of the lowest priority ones.
  • The SNES programming manual describes a situation where the Time Over flag is erroneously set when the first hardware sprite is 16x16, 32x32, or 64x64, has a horizontal position of 0-255, and other hardware sprites have negative horizontal positions.
  • The SNES programming manual says that a hardware sprite should not have its horizontal position set to $100.
    • This seems to be because that causes a sprite to contribute to the scanline limits when it should not [1]
  • INIDISP (register $2100) problems
    • Changing the brightness is not instant. On a 3-chip SNES, it may only take a few pixels to change the brightness, but on a 1-chip SNES it may be a gradual fade that takes 72 pixels or more.
      • This can be a problem for games that extend vblank by disabling rendering and enabling it several scanlines into the frame. For this use-case, it's recommended to disable rendering by writing $8F (or $80 ORed with whatever the desired brightness is) to INIDISP instead of $80, so that the brightness is not changed as rendering is enabled.
    • INIDISP early read bug: When INIDISP is written to, the PPU doesn't wait for the value to be put on the bus before attempting to read it. This means that the SNES will end up rendering about one pixel where INIDISP has been set to whatever was on the data bus before the correct value. For instructions that don't use indirect addressing, this will likely be the last byte of the instruction.
      • Workaround: Use long addressing to write to INIDISP during rendering, and take advantage of how PPU registers are available in many different banks. STA $8F2100 will put $8f on the bus before the written value, and STA $0F2100 will put $0f on the bus before the written value, and so on.
  • 16x32 sprites do not work correctly with OBJ interlacing[2]
    • When OBJ interlacing is on, 16x32 sprites are treated as if they are 16x16 - the bottom 16x16 is ignored, and the top 16x16 is squished into 16x8. 32x64 sprites behave as expected.
  • 16x32 and 32x64 sprites do not handle being vertically flipped correctly.


  • The gauss interpolation table has some mistakes in it [3]
  • The SNES programming manual warns that writing to the first two SPC700 communication registers ($2140 and $2141) with a 16-bit write can also write to $2143 [4][5]
    • This may be difficult to trigger or perhaps not actually exist[6]
  • Writing to SPC700 communication registers ($2140, $2141, $2142, $2143) at the same time the other processor reads it can result in incorrect data being read.
    • A SPC700 program may want to read twice and only proceed when two subsequent reads have the same value.
  • The hardware noise unit uses the contents of an LFSR to generate a signed noise sample. Because the LFSR only shifts 1 bit per sample, the correlated lower bits end up producing a strong highpass filter effect on the noise.[7] Especially in the high and low ranges, this will differ from the more typical 1-bit LFSR noise sound seen in other sound chips.


  • Starting a multiplication ($4203 WRMPYB) or division ($4206 WRDIVB) while the 5A22 is still processing a previous multiplication or division can cause the 5A22 to output erroneous values to RDDIV and/or RDMPY.[8]


  • Some A-Bus addresses are invalid: [9]
    • The A-Bus address cannot access a B-Bus address ($21xx)
    • The A-Bus address cannot access the MMIO or DMA registers ($4000-$41ff, $4200-$421f, $4300-$437f)
    • The A-Bus address cannot be a Work-RAM address if the B-Bus address is WMDATA ($2180). This means DMA cannot be used to copy from one section of the SNES's RAM to another.
  • On version 1 of the 5A22 chip ("S-CPU"), the chip can crash if DMA finishes right before HDMA happens. This is generally only a problem for games that want to use DMA to clear WRAM or copy data from a coprocessor to WRAM, as that's the main reason to use DMA during rendering.
  • On version 2 of the 5A22 chip ("S-CPU-A"), a recent HDMA transfer to/from INIDISP (Meaning that BBADn is set to zero $00) can make a DMA transfer fail. Nothing will happen and the DMA size registers (DASnL, DASnH) will be unchanged, instead of zero like they normally are after a DMA has been completed.
    • Workaround: Set BBADn to $ff instead, and set the transfer pattern to 1. This will cause HDMA to write to $21ff (nothing) and then $2100 (INIDISP). Both bytes should be set to the same value to prevent the INIDISP early read bug.
    • S-CPU (the first version), S-CPU-B and the 1-CHIP SNES are not affected by this bug.


  • Automatic controller reading begins between H=32.5 and H=95.5 of the first vblank scanline[10]. This means that checking HVBJOY ($4212) is not quite sufficient to avoid an in-progress auto-read if used immediately after the start of vblank.


  1. Super Famicom Development Wiki: Sprites
  2. Forum thread: 16x32 sprites in interlaced mode?
  3. Fullsnes: SNES APU DSP BRR Pitch
  4. SNES Development Manual Book 1, section 3-9-6: Sound Programming Cautions
  5. Super Famicom Development Wiki: SPC700 Reference
  6. Forum post: APU crosstalk 16-bit bug
  7. Form post: Re: Was the SPC700's noise channel based on the 2a03's noise channel?
  8. Forum thread - Writing $4203 twice too fast gives erroneous result (not emulated)
  9. higan source code, sfc/cpu/dma.cpp, by Near
  10. Form post: Stupid problems with autoread on hardware