Pokémon Crystal any% speedrun route: Difference between revisions

From Glitch City Wiki
Jump to navigation Jump to search
Content added Content deleted
>Bbbbbbbbba
m (→‎Arbitrary code execution: Change "jp (hl)" to "jp hl")
>Bbbbbbbbba
m (typo)
Line 6: Line 6:


==Preparations==
==Preparations==

* Clear saves between resets.
*Clear saves between resets.
** This is required any time you start a New Game, since your lucky ID persists regardless of having a save file.
**This is required any time you start a New Game, since your lucky ID persists regardless of having a save file.
* [https://youtu.be/LgQiDpcPpDQ '''Manipulate for a Trainer ID of 0x26FB (09979) and Lucky ID of 0x186F (06255).''']
* [https://youtu.be/zqTlMDVPg0o '''Manipulate for a Cyndaquil with DVs E9xx'''] (see the original route guide for detailed inputs).
*[https://youtu.be/LgQiDpcPpDQ '''Manipulate for a Trainer ID of 0x26FB (09979) and Lucky ID of 0x186F (06255).''']
*[https://youtu.be/zqTlMDVPg0o '''Manipulate for a Cyndaquil with DVs E9xx'''] (see the original route guide for detailed inputs).
** This manipulation needs the player name to be 3 characters (the preset name "MAT" will do).
**This manipulation needs the player name to be 3 characters (the preset name "MAT" will do).
* Swap Leer and Tackle during the Rival fight to set up the move order.
* Win the rival fight to level up and learn Smokescreen.
*Swap Leer and Tackle during the Rival fight to set up the move order.
*Win the rival fight to level up and learn Smokescreen.
** '''The moves of Cyndaquil should be Leer, Tackle, Smokescreen in this order''' (Tackle, Smokescreen, Leer also works, but takes more time).
**'''The moves of Cyndaquil should be Leer, Tackle, Smokescreen in this order''' (Tackle, Smokescreen, Leer also works, but takes more time).
* Catch any encounter (2 Pokémon are required for cloning).
*Catch any encounter (2 Pokémon are required for cloning).
* Run from all other encounters (guarantees safe experience values).
*Run from all other encounters (guarantees safe experience values).


==Bad clone glitch==
==Bad clone glitch==

* Go to the PC on the 2nd floor of Cherrygrove Pokémon Center.
*Go to the PC on the 2nd floor of Cherrygrove Pokémon Center.
* '''Rename boxes:'''
*'''Rename boxes:'''
** BOX 1: T 0 'v é N 5 'v 's
** BOX 2: é n 6 'v f é R 5
**BOX 1: T 0 'v é N 5 'v 's
** BOX 3: f é o 6 p é ''é'' 6
**BOX 2: é n 6 'v f é R 5
** BOX 4: é ''5'' 5 <PK> <PK> 'd
**BOX 3: f é o 6 p é ''é'' 6
**BOX 4: é ''5'' 5 <PK> <PK> 'd
** Note: Characters in italic doesn't matter (they are overwritten by the ACE).
**Note: Characters in italic don't matter (they are overwritten by the ACE).
* '''Move Cyndaquil to the second slot in party''', and save the game ("move PkMn w/o mail" is the fastest way to achieve both).
*'''Move Cyndaquil to the second slot in party''', and save the game ("move PkMn w/o mail" is the fastest way to achieve both).
* '''Do the [[bad clone glitch]] by depositing one of the Pokémon, switching boxes and hard resetting during the save.'''
*'''Do the [[bad clone glitch]] by depositing one of the Pokémon, switching boxes and hard resetting during the save.'''
** Either a "real" bad clone (level 0) or a "pseudo" bad clone (correct level, but unterminated nickname; referred to as "friendly clone" in the original route guide) will work.
**Either a "real" bad clone (level 0) or a "pseudo" bad clone (correct level, but unterminated nickname; referred to as "friendly clone" in the original route guide) will work.


==Arbitrary code execution==
==Arbitrary code execution==

* After reloading the save, go down 1 tile, then right 4 tiles.
*After reloading the save, go down 1 tile, then right 4 tiles.
* Open Pokédex and scroll to the position of Spearow (0x15).
*Open Pokédex and scroll to the position of Spearow (0x15).
** '''This sets the temporary variable $D265 to 0x15. $D266 (wFailedToFlee) should remain 0x00 after reset.'''
**'''This sets the temporary variable $D265 to 0x15. $D266 (wFailedToFlee) should remain 0x00 after reset.'''
** Notice that where the accessible Pokédex ends will depend on what Pokémon you have seen. In case the only wild Pokémon you have seen is Pidgey, Spearow would be out of reach in new Pokédex mode, so the player will need to go to old Pokédex mode (where Cyndaquil comes after Spearow).
**Notice that where the accessible Pokédex ends will depend on what Pokémon you have seen. In case the only wild Pokémon you have seen is Pidgey, Spearow would be out of reach in new Pokédex mode, so the player will need to go to old Pokédex mode (where Cyndaquil comes after Spearow).
* Close out of Pokédex, go left 4 tiles then up 1 tile to get back to PC.
*Close out of Pokédex, go left 4 tiles then up 1 tile to get back to PC.
** '''This movement sets up an array at $CD70 that stores some pointers for the background map to begin with "D8 9B DA 9B DC 9B ...".''' <!-- TODO: Elaborate. -->
**'''This movement sets up an array at $CD70 that stores some pointers for the background map to begin with "D8 9B DA 9B DC 9B ...".'''<!-- TODO: Elaborate. -->
* Open PC and go to the withdraw screen.
*Open PC and go to the withdraw screen.
** '''The game will try to show the [[unterminated name Pokémon (Generation II)|unterminated nickname]] starting from $D073.''' There are a lot of data between there and $D265, but after a reset they are predictable, and won't contain any terminators or other problematic control characters. Finally, after a few B presses for control characters, the text engine reaches $D265 and triggers [[0x1500 control code arbitrary code execution]], making the program counter jump to $CD52.
**'''The game will try to show the [[unterminated name Pokémon (Generation II)|unterminated nickname]] starting from $D073.''' There are a lot of data between there and $D265, but after a reset they are predictable, and won't contain any terminators or other problematic control characters. Finally, after a few B presses for control characters, the text engine reaches $D265 and triggers [[0x1500 control code arbitrary code execution]], making the program counter jump to $CD52.
** Relevant register states:
**Relevant register states:
*** a = 0x52 (temporary variable for reading out the jump destination $CD52)
***a = 0x52 (temporary variable for reading out the jump destination $CD52)
*** e = 0xFF (jumptable index minus one)
*** carry flag unset (calculating the address of jumptable entry 255 didn't overflow)
***e = 0xFF (jumptable index minus one)
***carry flag unset (calculating the address of jumptable entry 255 didn't overflow)


{|
{|
|-
|-
|
|
{| border=1 width="100%" align=left style="background:#f0f0f0; border:1px solid #000000; border-collapse:collapse;"
{| border="1" width="100%" align="left" style="background:#f0f0f0; border:1px solid #000000; border-collapse:collapse;"
|-style="background: silver;"
|- style="background: silver;"
! Program counter !! Hex !! ASM !! In-game meaning !! In-game value !! Comments
!Program counter!!Hex!!ASM!!In-game meaning!!In-game value!!Comments
|-
|-
| $CD52 ~ $CD6F || 00 (*30) || nop || <!-- TODO --> || ||
|$CD52 ~ $CD6F||00 (*30)||nop||<!-- TODO -->|| ||
|-
|-
| $CD70 || D8 || ret c || rowspan=3 | Pointers to background map tiles || rowspan=3 | $9BD8, $9BDA, $9BDC... || Jump not taken
|$CD70||D8||ret c|| rowspan="3" |Pointers to background map tiles|| rowspan="3" |$9BD8, $9BDA, $9BDC...||Jump not taken
|-
|-
| $CD71 || 9B || '''sbc e''' || Sets the carry flag (0x52 - 0xFF)
|$CD71||9B||'''sbc e'''||Sets the carry flag (0x52 - 0xFF)
|-
|-
| $CD72 || DA 9B DC || '''jp c, $DC9B''' || Jump taken
|$CD72||DA 9B DC||'''jp c, $DC9B'''||Jump taken
|-
|-
| $DC9B ~ $DC9E || 00 (*4) || nop || Unused || ||
|$DC9B ~ $DC9E||00 (*4)||nop||Unused|| ||
|-
|-
| $DC9F || 18 6F || '''jr 0x6F''' || Lucky ID || 06255 (0x186F) || Jumps to $DD10
|$DC9F||18 6F||'''jr 0x6F'''||Lucky ID||06255 (0x186F)||Jumps to $DD10
|-
|-
| $DD10 || AD / 00 || xor l / nop || Held item of second party Pokémon (Cyndaquil) || Berry / nothing ||
|$DD10||AD / 00||xor l / nop||Held item of second party Pokémon (Cyndaquil)||Berry / nothing||
|-
|-
| $DD11 || 2B || dec hl || First move of Cyndaquil || Leer ||
|$DD11||2B||dec hl||First move of Cyndaquil||Leer||
|-
|-
| $DD12 || 21 6C 00 || '''ld hl, 0x006C''' || Second to fourth move of Cyndaquil || Tackle, Smokescreen, empty slot || hl = 0x006C
|$DD12||21 6C 00||'''ld hl, 0x006C'''||Second to fourth move of Cyndaquil||Tackle, Smokescreen, empty slot||hl = 0x006C
|-
|-
| $DD15 || 26 FB || '''ld h, 0xFB''' || Original Trainer ID of Cyndaquil || 09979 (0x26FB) || hl = 0xFB6C
|$DD15||26 FB||'''ld h, 0xFB'''||Original Trainer ID of Cyndaquil||09979 (0x26FB)||hl = 0xFB6C
|-
|-
| $DD17 ~ $DD18 || 00 00 || nop || rowspan=2 | Experience of Cyndaquil<br/><br/> || rowspan=2 | 205 (0x0000CD) ||
|$DD17 ~ $DD18||00 00||nop|| rowspan="2" |Experience of Cyndaquil<br /><br />|| rowspan="2" |205 (0x0000CD)||
|-
|-
| rowspan=2 | $DD19 || rowspan=2 | CD 00 32 || rowspan=2 | '''call $3200''' || rowspan=2 | Calls WaitBGMap2, which calls DelayFrames;<br/>a = 0x00, c = 0x00
| rowspan="2" |$DD19|| rowspan="2" |CD 00 32|| rowspan="2" |'''call $3200'''|| rowspan="2" |Calls WaitBGMap2, which calls DelayFrames;<br />a = 0x00, c = 0x00
|-
|-
| Stat experience (HP) of Cyndaquil || 50 (0x0032)
|Stat experience (HP) of Cyndaquil||50 (0x0032)
|-
|-
| $DD1C || 00 || nop || rowspan=2 | Stat experience (Attack) of Cyndaquil || rowspan=2 | 65 (0x0041) ||
|$DD1C||00||nop|| rowspan="2" |Stat experience (Attack) of Cyndaquil|| rowspan="2" |65 (0x0041)||
|-
|-
| $DD1D || 41 || ''ld b, c'' || b = 0x00
|$DD1D||41||''ld b, c''||b = 0x00
|-
|-
| $DD1E || 00 || nop || rowspan=2 | Stat experience (Defense) of Cyndaquil || rowspan=2 | 64 (0x0040) ||
|$DD1E||00||nop|| rowspan="2" |Stat experience (Defense) of Cyndaquil|| rowspan="2" |64 (0x0040)||
|-
|-
| $DD1F || 40 || ld b, b ||
|$DD1F||40||ld b, b||
|-
|-
| $DD20 || 00 || nop || rowspan=2 | Stat experience (Speed) of Cyndaquil || rowspan=2 | 43 (0x002B) ||
|$DD20||00||nop|| rowspan="2" |Stat experience (Speed) of Cyndaquil|| rowspan="2" |43 (0x002B)||
|-
|-
| $DD21 || 2B || '''dec hl''' || hl = 0xFB6B
|$DD21||2B||'''dec hl'''||hl = 0xFB6B
|-
|-
| $DD22 || 00 || nop || rowspan=2 | Stat experience (Special) of Cyndaquil || rowspan=2 | 44 (0x002C) ||
|$DD22||00||nop|| rowspan="2" |Stat experience (Special) of Cyndaquil|| rowspan="2" |44 (0x002C)||
|-
|-
| $DD23 || 2C || '''inc l''' || hl = 0xFB6C
|$DD23||2C||'''inc l'''||hl = 0xFB6C
|-
|-
| $DD24 || E9 || '''jp hl''' || First byte of DVs of Cyndaquil || 0xE9 || Jumps to 0xFB6C (Echo RAM, equivalent to $DB6C)
|$DD24||E9||'''jp hl'''||First byte of DVs of Cyndaquil||0xE9||Jumps to 0xFB6C (Echo RAM, equivalent to $DB6C)
|-
|-
| $FB6C ~ $FB71 || 00 (*6) || nop || Unused || ||
|$FB6C ~ $FB71||00 (*6)||nop||Unused|| ||
|-
|-
| $FB72 || 00 || nop || Index of current box (0-based) || Box 1 ||
|$FB72||00||nop||Index of current box (0-based)||Box 1||
|-
|-
| $FB73 ~ $FB74 || 00 00 || nop || Unused || ||
|$FB73 ~ $FB74||00 00||nop||Unused|| ||
|-
|-
| $FB75 || 93 || '''sub e''' || rowspan=5 | Box 1 name || T || a = 0x01
|$FB75||93||'''sub e'''|| rowspan="5" |Box 1 name||T||a = 0x01
|-
|-
| $FB76 || F6 D6 || '''or a, 0xD6''' || 0 'v || a = 0xD7
|$FB76||F6 D6||'''or a, 0xD6'''||0 'v||a = 0xD7
|-
|-
| $FB78 || EA 8D FB || '''ld ($FB8D), a''' || é N 5 || ($FB8D) = 0xD7
|$FB78||EA 8D FB||'''ld ($FB8D), a'''||é N 5||($FB8D) = 0xD7
|-
|-
| $FB7B || D6 D4 || '''sub a, 0xD4''' || 'v 's || a = 0x03
|$FB7B||D6 D4||'''sub a, 0xD4'''||'v 's||a = 0x03
|-
|-
| $FB7D || 50 || ld d, b || (terminator) ||
|$FB7D||50||ld d, b||(terminator)||
|-
|-
| $FB7E || EA AD FC || '''ld ($FCAD), a''' || rowspan=4 | Box 2 name || é n 6 || [wBackupMapGroup] = 0x03
|$FB7E||EA AD FC||'''ld ($FCAD), a'''|| rowspan="4" |Box 2 name||é n 6||[wBackupMapGroup] = 0x03
|-
|-
| $FB81 || D6 A5 || '''sub a, 0xA5''' || 'v f || a = 0x5E
|$FB81||D6 A5||'''sub a, 0xA5'''||'v f||a = 0x5E
|-
|-
| $FB83 || EA B1 FB || '''ld ($FB91), a''' || é R 5 || ($FB91) = 0x5E
|$FB83||EA B1 FB||'''ld ($FB91), a'''||é R 5||($FB91) = 0x5E
|-
|-
| $FB86 || 50 || ld d, b || (terminator) ||
|$FB86||50||ld d, b||(terminator)||
|-
|-
| $FB87 || A5 || '''and l''' || rowspan=5 | Box 3 name ($FB8D overwritten by previous code) || f || a = 0x4C
|$FB87||A5||'''and l'''|| rowspan="5" |Box 3 name ($FB8D overwritten by previous code)||f||a = 0x4C
|-
|-
| $FB88 || EA AE FC || '''ld ($FCAE), a''' || é o 6 || [wBackupMapNumber] = 0x4C
|$FB88||EA AE FC||'''ld ($FCAE), a'''||é o 6||[wBackupMapNumber] = 0x4C
|-
|-
| $FB8B || AF || '''xor a''' || p || a = 0x00; clears the carry flag
|$FB8B||AF||'''xor a'''||p||a = 0x00; clears the carry flag
|-
|-
| $FB8C || EA D6 FC || '''ld ($FCD7), a''' || é (0xD7) 6 || [wPartyCount] = 0x00
|$FB8C||EA D6 FC||'''ld ($FCD7), a'''||é (0xD7) 6||[wPartyCount] = 0x00
|-
|-
| $FB8F || 50 || ''ld d, b'' || (terminator) || d = 0x00
|$FB8F||50||''ld d, b''||(terminator)||d = 0x00
|-
|-
| $FB90 || EA 5D FB || '''ld ($FB5E), a''' || rowspan=5 | Box 4 name ($FB91 overwritten by previous code) || é (0x5E) 5 || [wEventFlags + 236] = 0x00
|$FB90||EA 5D FB||'''ld ($FB5E), a'''|| rowspan="5" |Box 4 name ($FB91 overwritten by previous code)||é (0x5E) 5||[wEventFlags + 236] = 0x00
|-
|-
| $FB93 || E1 || '''pop hl''' || <PK> || Pops text pointer
|$FB93||E1||'''pop hl'''||<PK>||Pops text pointer
|-
|-
| $FB94 || E1 || '''pop hl''' || <PK> || Pops return pointer in RunMobileScript
|$FB94||E1||'''pop hl'''||<PK>||Pops return pointer in RunMobileScript
|-
|-
| $FB94 || D0 || '''ret nc''' || 'd || Returns to MobileScriptChar
|$FB94||D0||'''ret nc'''||'d||Returns to MobileScriptChar
|}
|}
|}
|}

* The text engine continues to try to display text from de = 0x00FF, prints some more garbage, but eventually encounters a 0x50 terminator.
*The text engine continues to try to display text from de = 0x00FF, prints some more garbage, but eventually encounters a 0x50 terminator.
* Close the PC, and go downstairs.
*Close the PC, and go downstairs.
** Since the 2nd floor of Pokémon centers are in fact a shared map, the downstairs warp uses wBackupMapGroup and wBackupMapNumber to determine which map to return to. Map 0x4C in map group 0x03 is Red's room in Mt. Silver. Before the ACE, the game is supposed to return the player to warp 0x03 in the 1st floor of Cherrygrove Pokémon Center, so now the game will try to return the player to warp 0x03 in Red's room. Warp 0x03 is invalid, but it turns out to be at coordinate (13, 7), which is conveniently close to Red at (9, 10).
**Since the 2nd floor of Pokémon centers are in fact a shared map, the downstairs warp uses wBackupMapGroup and wBackupMapNumber to determine which map to return to. Map 0x4C in map group 0x03 is Red's room in Mt. Silver. Before the ACE, the game is supposed to return the player to warp 0x03 in the 1st floor of Cherrygrove Pokémon Center, so now the game will try to return the player to warp 0x03 in Red's room. Warp 0x03 is invalid, but it turns out to be at coordinate (13, 7), which is conveniently close to Red at (9, 10).
* Talk to Red.
*Talk to Red.
** Normally, Red only appears in Mt. Silver after the Hall of Fame sequence, but setting [wEventFlags + 236] = 0x00 resets the appropriate event flag and lets him appear.
**Normally, Red only appears in Mt. Silver after the Hall of Fame sequence, but setting [wEventFlags + 236] = 0x00 resets the appropriate event flag and lets him appear.
** At this point, we have no Pokémon in our party (more precisely, our party count is 0), so the [[instant victory glitch (Generation II)|"instant victory effect"]] is triggered. Since the player never entered a battle since loading the save file, the overworld script acts as if the player won. In the case of Red, this means going to the credit roll.
**At this point, we have no Pokémon in our party (more precisely, our party count is 0), so the [[instant victory glitch (Generation II)|"instant victory effect"]] is triggered. Since the player never entered a battle since loading the save file, the overworld script acts as if the player won. In the case of Red, this means going to the credit roll.


{{Youtube|qaVUIAgsLpY|Pokeguy84}}
{{Youtube|qaVUIAgsLpY|Pokeguy84}}


==See also==
==See also==

* [http://wiki.pokemonspeedruns.com/index.php?title=Pok%C3%A9mon_Crystal/Any%25_Guide The full current Crystal any% route]
*[http://wiki.pokemonspeedruns.com/index.php?title=Pok%C3%A9mon_Crystal/Any%25_Guide The full current Crystal any% route]


[[Category:Speedrun routes]]
[[Category:Speedrun routes]]

Revision as of 06:52, 20 July 2019

The current Pokémon Crystal any% glitched speedrun route is based on 0x1500 control code arbitrary code execution. It triggers the glitch by using the bad clone glitch to get a Pokémon with an unterminated nickname, and viewing its name in the PC withdraw screen. It also makes use of two luck manipulations to get desirable values for the Trainer ID, the Lucky ID, and DVs of the starter. This page documents the route and analyzes the glitches used in detail.

The content of this page is based on the advanced route guide by entrpntr. Details unrelated to glitches/manipulations (i.e. for time saving only) are omitted.

Preparations

  • Clear saves between resets.
    • This is required any time you start a New Game, since your lucky ID persists regardless of having a save file.
  • Manipulate for a Trainer ID of 0x26FB (09979) and Lucky ID of 0x186F (06255).
  • Manipulate for a Cyndaquil with DVs E9xx (see the original route guide for detailed inputs).
    • This manipulation needs the player name to be 3 characters (the preset name "MAT" will do).
  • Swap Leer and Tackle during the Rival fight to set up the move order.
  • Win the rival fight to level up and learn Smokescreen.
    • The moves of Cyndaquil should be Leer, Tackle, Smokescreen in this order (Tackle, Smokescreen, Leer also works, but takes more time).
  • Catch any encounter (2 Pokémon are required for cloning).
  • Run from all other encounters (guarantees safe experience values).

Bad clone glitch

  • Go to the PC on the 2nd floor of Cherrygrove Pokémon Center.
  • Rename boxes:
    • BOX 1: T 0 'v é N 5 'v 's
    • BOX 2: é n 6 'v f é R 5
    • BOX 3: f é o 6 p é é 6
    • BOX 4: é 5 5 <PK> <PK> 'd
    • Note: Characters in italic don't matter (they are overwritten by the ACE).
  • Move Cyndaquil to the second slot in party, and save the game ("move PkMn w/o mail" is the fastest way to achieve both).
  • Do the bad clone glitch by depositing one of the Pokémon, switching boxes and hard resetting during the save.
    • Either a "real" bad clone (level 0) or a "pseudo" bad clone (correct level, but unterminated nickname; referred to as "friendly clone" in the original route guide) will work.

Arbitrary code execution

  • After reloading the save, go down 1 tile, then right 4 tiles.
  • Open Pokédex and scroll to the position of Spearow (0x15).
    • This sets the temporary variable $D265 to 0x15. $D266 (wFailedToFlee) should remain 0x00 after reset.
    • Notice that where the accessible Pokédex ends will depend on what Pokémon you have seen. In case the only wild Pokémon you have seen is Pidgey, Spearow would be out of reach in new Pokédex mode, so the player will need to go to old Pokédex mode (where Cyndaquil comes after Spearow).
  • Close out of Pokédex, go left 4 tiles then up 1 tile to get back to PC.
    • This movement sets up an array at $CD70 that stores some pointers for the background map to begin with "D8 9B DA 9B DC 9B ...".
  • Open PC and go to the withdraw screen.
    • The game will try to show the unterminated nickname starting from $D073. There are a lot of data between there and $D265, but after a reset they are predictable, and won't contain any terminators or other problematic control characters. Finally, after a few B presses for control characters, the text engine reaches $D265 and triggers 0x1500 control code arbitrary code execution, making the program counter jump to $CD52.
    • Relevant register states:
      • a = 0x52 (temporary variable for reading out the jump destination $CD52)
      • e = 0xFF (jumptable index minus one)
      • carry flag unset (calculating the address of jumptable entry 255 didn't overflow)
Program counter Hex ASM In-game meaning In-game value Comments
$CD52 ~ $CD6F 00 (*30) nop
$CD70 D8 ret c Pointers to background map tiles $9BD8, $9BDA, $9BDC... Jump not taken
$CD71 9B sbc e Sets the carry flag (0x52 - 0xFF)
$CD72 DA 9B DC jp c, $DC9B Jump taken
$DC9B ~ $DC9E 00 (*4) nop Unused
$DC9F 18 6F jr 0x6F Lucky ID 06255 (0x186F) Jumps to $DD10
$DD10 AD / 00 xor l / nop Held item of second party Pokémon (Cyndaquil) Berry / nothing
$DD11 2B dec hl First move of Cyndaquil Leer
$DD12 21 6C 00 ld hl, 0x006C Second to fourth move of Cyndaquil Tackle, Smokescreen, empty slot hl = 0x006C
$DD15 26 FB ld h, 0xFB Original Trainer ID of Cyndaquil 09979 (0x26FB) hl = 0xFB6C
$DD17 ~ $DD18 00 00 nop Experience of Cyndaquil

205 (0x0000CD)
$DD19 CD 00 32 call $3200 Calls WaitBGMap2, which calls DelayFrames;
a = 0x00, c = 0x00
Stat experience (HP) of Cyndaquil 50 (0x0032)
$DD1C 00 nop Stat experience (Attack) of Cyndaquil 65 (0x0041)
$DD1D 41 ld b, c b = 0x00
$DD1E 00 nop Stat experience (Defense) of Cyndaquil 64 (0x0040)
$DD1F 40 ld b, b
$DD20 00 nop Stat experience (Speed) of Cyndaquil 43 (0x002B)
$DD21 2B dec hl hl = 0xFB6B
$DD22 00 nop Stat experience (Special) of Cyndaquil 44 (0x002C)
$DD23 2C inc l hl = 0xFB6C
$DD24 E9 jp hl First byte of DVs of Cyndaquil 0xE9 Jumps to 0xFB6C (Echo RAM, equivalent to $DB6C)
$FB6C ~ $FB71 00 (*6) nop Unused
$FB72 00 nop Index of current box (0-based) Box 1
$FB73 ~ $FB74 00 00 nop Unused
$FB75 93 sub e Box 1 name T a = 0x01
$FB76 F6 D6 or a, 0xD6 0 'v a = 0xD7
$FB78 EA 8D FB ld ($FB8D), a é N 5 ($FB8D) = 0xD7
$FB7B D6 D4 sub a, 0xD4 'v 's a = 0x03
$FB7D 50 ld d, b (terminator)
$FB7E EA AD FC ld ($FCAD), a Box 2 name é n 6 [wBackupMapGroup] = 0x03
$FB81 D6 A5 sub a, 0xA5 'v f a = 0x5E
$FB83 EA B1 FB ld ($FB91), a é R 5 ($FB91) = 0x5E
$FB86 50 ld d, b (terminator)
$FB87 A5 and l Box 3 name ($FB8D overwritten by previous code) f a = 0x4C
$FB88 EA AE FC ld ($FCAE), a é o 6 [wBackupMapNumber] = 0x4C
$FB8B AF xor a p a = 0x00; clears the carry flag
$FB8C EA D6 FC ld ($FCD7), a é (0xD7) 6 [wPartyCount] = 0x00
$FB8F 50 ld d, b (terminator) d = 0x00
$FB90 EA 5D FB ld ($FB5E), a Box 4 name ($FB91 overwritten by previous code) é (0x5E) 5 [wEventFlags + 236] = 0x00
$FB93 E1 pop hl <PK> Pops text pointer
$FB94 E1 pop hl <PK> Pops return pointer in RunMobileScript
$FB94 D0 ret nc 'd Returns to MobileScriptChar
  • The text engine continues to try to display text from de = 0x00FF, prints some more garbage, but eventually encounters a 0x50 terminator.
  • Close the PC, and go downstairs.
    • Since the 2nd floor of Pokémon centers are in fact a shared map, the downstairs warp uses wBackupMapGroup and wBackupMapNumber to determine which map to return to. Map 0x4C in map group 0x03 is Red's room in Mt. Silver. Before the ACE, the game is supposed to return the player to warp 0x03 in the 1st floor of Cherrygrove Pokémon Center, so now the game will try to return the player to warp 0x03 in Red's room. Warp 0x03 is invalid, but it turns out to be at coordinate (13, 7), which is conveniently close to Red at (9, 10).
  • Talk to Red.
    • Normally, Red only appears in Mt. Silver after the Hall of Fame sequence, but setting [wEventFlags + 236] = 0x00 resets the appropriate event flag and lets him appear.
    • At this point, we have no Pokémon in our party (more precisely, our party count is 0), so the "instant victory effect" is triggered. Since the player never entered a battle since loading the save file, the overworld script acts as if the player won. In the case of Red, this means going to the credit roll.

YouTube video by Pokeguy84


See also