Invalid Sprite Transfer Corruption

From Glitch City Wiki
Revision as of 15:22, 4 June 2024 by TimoVM (talk | contribs) (Created page with "Invalid Sprite Transfer Corruption is a glitch that occurs in pokémon Gold & Silver. It is not present in pokémon Crystal. When a pokémon with an invalid front sprite is encountered in battle, the area between $D000 and $D0FF is erroneously filled with repeating $D1 values. Among other things, this alters the value of wLinkMode, causing the game to believe it is currently in a link battle, locking up the game after selecting any action in battle. ==Explanation== Wh...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Invalid Sprite Transfer Corruption is a glitch that occurs in pokémon Gold & Silver. It is not present in pokémon Crystal.

When a pokémon with an invalid front sprite is encountered in battle, the area between $D000 and $D0FF is erroneously filled with repeating $D1 values. Among other things, this alters the value of wLinkMode, causing the game to believe it is currently in a link battle, locking up the game after selecting any action in battle.

Explanation

Whenever an enemy pokémon's front picture needs to be loaded in battle, the game calls GetMonFrontPic. This function works in two stages:

  1. Find the front pic's pointer in ROM, decompress and load it into the scratch buffer, located in sram
  2. Transfer the front pic's data from sram to vram, so that it can be displayed as screen tiles

The root cause of the issue is that, for invalid species, the game refuses to copy any sprite data to sram, but still attempts to transfer data to vram.

The order of events is as follows:

  1. Prior to loading the front sprite through GetMonFrontPic, the game calls GetUnownLetter to determine which species of Unown should be loaded based on DVs. This leaves a value of $D0F6 in hl, the second byte of wEnemyMonDVs.
  2. GetFrontPic is called. This function is responsible for opening sram, determining the ROM pointer for the enemy pokémon's sprite data and loading this sprite data into the scratch buffer. This function implicitely requires a vram pointer to be present in register de, which determines where the decompressed data will later be copied to.
    • For valid species, this pointer is then pushed and popped to register hl near the end of the function.
    • Invalid species will cause an immediate return within GetFrontPic. This means that sram is never opened and, critically, the vram pointer is never moved to hl, leaving a junk value of $D0F6 in hl instead.
  3. Next, the game jumps to Load2bppToSRAM and gets everything ready to transfer data from sram to vram, copying from A000 (kept in de) to the vram pointer (kept in hl and unaltered by this function).
    • For invalid spedies, due to the junk value of $D0F6 remaining in hl, this will cause the game to attempt to transfer data from A000 to D0F6. Sram is still closed at this point in time due to GetFrontPic terminating early and never getting the opportunity to open sram.
    • Load2bppToSRAM then calls Get2bpp. Since the lcd is on (meaning vblank will occur and restrict vram access at certain intervals), the game jumps to Request2bpp so that the sprite data transfer can be handled during the normal vblank routine.
  4. During the vblank interrupt, Serve2bppRequest then proceeds to transfer data from A000 to D0F6.
    • Serve2bppRequest retrieves data from sram by altering the stack pointer and popping values to de using instruction $D1 (pop de). Since sram is closed, attempting to pop values to de results in an open bus issue where the value retrieved is determined by the last valid instruction byte. Since the last used instruction was $D1, the value $D1D1 is popped to de.
    • The game then transfers this data to D0F6. As a precaution, for every 16 bytes, the game will use 15 inc l instructions and finish off with a single inc hl instruction. Since the starting value of hl is $D0F6, the inc hl can never coincide with a value in register l of $FF, which means the h register can never increment. This leads the game to overwrite the entire area between $D000 and $D0FF with $D1 values.

This glitch does not occur in Crystal. In Crystal, GetMonFrontPic was altered so that invalid species will terminate the entire process early, meaning that the data transfer from sram to vram will not be executed.