Jump to content

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

m
→‎Preparations: Avoided unnecessary uses of "you".
>Bbbbbbbbba
m (→‎Arbitrary code execution: Change "jp (hl)" to "jp hl")
m (→‎Preparations: Avoided unnecessary uses of "you".)
 
(9 intermediate revisions by 2 users not shown)
Line 6:
 
==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 the runner starts a New Game, since the 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/zqTlMDVPg0oLgQiDpcPpDQ '''Manipulate for a CyndaquilTrainer withID DVsof E9xx''']0x26FB (see09979) theand originalLucky routeID guideof for0x186F detailed inputs(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.
*Swap WinLeer and Tackle during the rivalRival fight to levelset up andthe learnmove Smokescreenorder.
*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).
** '''The experience and stat experience values after defeating the rival do affect the ACE. They are not a crucial part of it (if they were "safe" values, the box names can be changed to account for that), but this particular route needs those values.'''
 
==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 21: éT n 60 'v f é RN 5 'v 's
** BOX 32: f é on 6 p'v éf ''é'' 6R 5
** BOX 43: f é ''5''o 56 <PK>p <PK>é 'd'é'' 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==
* 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 ...".''' <!-- TODO: Elaborate. -->
* 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.
** 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)
 
*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 ...".'''<!-- TODO: Elaborate. -->
*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.
**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)
 
{| class="wikitable"
!Program counter!!Hex!!ASM!!In-game meaning!!In-game value!!Comments
|-
|$CD52 – $CD6F||00 (*30)||nop||<!-- TODO -->|| ||
|
{| border=1 width="100%" align=left style="background:#f0f0f0; border:1px solid #000000; border-collapse:collapse;"
|-style="background: silver;"
! Program counter !! Hex !! ASM !! In-game meaning !! In-game value !! Comments
|-
|$CD70||D8||ret c|| rowspan="3" |Pointers to background map tiles|| rowspan="3" |$9BD8, $9BDA, $9BDC...||Jump not taken
| $CD52 ~ $CD6F || 00 (*30) || nop || <!-- TODO --> || ||
|-
|$CD71||9B||'''sbc e'''||Sets the carry flag (0x52 - 0xFF)
| $CD70 || D8 || ret c || rowspan=3 | Pointers to background map tiles || rowspan=3 | $9BD8, $9BDA, $9BDC... || Jump not taken
|-
| $CD71 CD72||DA 9B DC|| '''sbcjp ec, $DC9B''' ||Jump Sets the carry flag (0x52 - 0xFF)taken
|-
|$DC9B – $DC9E||00 (*4)||nop||Unused|| ||
| $CD72 || DA 9B DC || '''jp c, $DC9B''' || Jump taken
|-
|$DC9F||18 6F||'''jr 0x6F'''||Lucky ID||06255 (0x186F)||Jumps to $DD10
| $DC9B ~ $DC9E || 00 (*4) || nop || Unused || ||
|-
| $DC9F DD10||AD 18 6F/ 00||xor '''jrl 0x6F'''/ nop||Held Luckyitem IDof ||second 06255party Pokémon (0x186FCyndaquil) ||Berry Jumps/ to $DD10nothing||
|-
| $DD10 DD11|| AD / 00 2B||dec xor l / nop hl||First Held itemmove of second party Pokémon (Cyndaquil) || Berry / nothing Leer||
|-
| $DD11 DD12||21 2B6C 00|| dec'''ld hl, 0x006C'''||Second Firstto fourth move of Cyndaquil ||Tackle, LeerSmokescreen, empty slot||hl = 0x006C
|-
| $DD12 DD15||26 21 6C 00 FB|| '''ld hlh, 0x006C0xFB''' ||Original SecondTrainer to fourth moveID of Cyndaquil ||09979 Tackle, Smokescreen, empty slot (0x26FB)|| hl = 0x006C0xFB6C
|-
|$DD17 – $DD15 DD18||00 26 FB 00|| '''ld h, 0xFB''' nop|| Originalrowspan="2" Trainer ID|Experience of Cyndaquil<br /><br />|| 09979rowspan="2" |205 (0x26FB0x0000CD) || hl = 0xFB6C
|-
| $DD17 ~rowspan="2" |$DD18 DD19|| 00rowspan="2" |CD 00 32|| noprowspan="2" |'''call $3200'''|| rowspan="2" |Calls ExperienceWaitBGMap2, ofwhich Cyndaquil<br/>calls DelayFrames;<br />a || rowspan=2 |0x00, 205c (0x0000CD)= ||0x00
|-
|Stat experience (HP) of Cyndaquil||50 (0x0032)
| rowspan=2 | $DD19 || rowspan=2 | CD 00 32 || rowspan=2 | '''call $3200''' || rowspan=2 | Calls WaitBGMap2, which calls DelayFrames;<br/>a = 0x00, c = 0x00
|-
|$DD1C||00||nop|| rowspan="2" |Stat experience (HPAttack) of Cyndaquil || 50rowspan="2" |65 (0x00320x0041)||
|-
|$DD1D||41||''ld b, c''||b = 0x00
| $DD1C || 00 || nop || rowspan=2 | Stat experience (Attack) of Cyndaquil || rowspan=2 | 65 (0x0041) ||
|-
|$DD1E||00||nop|| rowspan="2" |Stat experience (Defense) of Cyndaquil|| rowspan="2" |64 (0x0040)||
| $DD1D || 41 || ''ld b, c'' || b = 0x00
|-
|$DD1F||40||ld b, b||
| $DD1E || 00 || nop || rowspan=2 | Stat experience (Defense) of Cyndaquil || rowspan=2 | 64 (0x0040) ||
|-
|$DD20||00||nop|| rowspan="2" |Stat experience (Speed) of Cyndaquil|| rowspan="2" |43 (0x002B)||
| $DD1F || 40 || ld b, b ||
|-
|$DD21||2B||'''dec hl'''||hl = 0xFB6B
| $DD20 || 00 || nop || rowspan=2 | Stat experience (Speed) of Cyndaquil || rowspan=2 | 43 (0x002B) ||
|-
|$DD22||00||nop|| rowspan="2" |Stat experience (Special) of Cyndaquil|| rowspan="2" |44 (0x002C)||
| $DD21 || 2B || '''dec hl''' || hl = 0xFB6B
|-
|$DD23||2C||'''inc l'''||hl = 0xFB6C
| $DD22 || 00 || nop || rowspan=2 | Stat experience (Special) of Cyndaquil || rowspan=2 | 44 (0x002C) ||
|-
| $DD23 DD24|| 2C E9|| '''incjp lhl''' ||First hlbyte =of DVs of Cyndaquil||0xE9||Jumps to 0xFB6C (Echo RAM, equivalent to $DB6C)
|-
|$FB6C – $FB71||00 (*6)||nop||Unused|| ||
| $DD24 || E9 || '''jp hl''' || First byte of DVs of Cyndaquil || 0xE9 || Jumps to 0xFB6C (Echo RAM, equivalent to $DB6C)
|-
| $FB6C ~ $FB71 FB72|| 00 (*6) || nop ||Index Unusedof current box (0-based)||Box 1||
|-
|$FB73 – $FB72 FB74||00 00 || nop || Index of current box (0-based) Unused|| Box 1 ||
|-
| $FB73 ~ $FB74 FB75||93||'''sub 00 00 e'''|| noprowspan="5" ||Box Unused1 name|| T||a = 0x01
|-
| $FB75 FB76||F6 93 D6|| '''subor ea, 0xD6''' ||0 rowspan=5 'v| Box 1 name || T || a = 0x010xD7
|-
| $FB76 FB78||EA F6 D68D FB|| '''orld a[$FB8D], 0xD6a''' ||é 0 'vN 5|| a($FB8D) = 0xD7
|-
| $FB78 FB7B||D6 EA 8D FB D4|| '''ldsub ($FB8D)a, a0xD4''' ||'v é N 5 's|| ($FB8D)a = 0xD70x03
|-
|$FB7D||50||ld d, b||(terminator)||
| $FB7B || D6 D4 || '''sub a, 0xD4''' || 'v 's || a = 0x03
|-
|$FB7E||EA AD FC||'''ld [$FCAD], a'''|| rowspan="4" |Box 2 name||é n 6||[wBackupMapGroup] = 0x03
| $FB7D || 50 || ld d, b || (terminator) ||
|-
| $FB7E FB81||D6 EA AD FC A5|| '''ldsub ($FCAD)a, a0xA5''' ||'v rowspan=4 f| Box 2 name || é n 6 || [wBackupMapGroup]a = 0x030x5E
|-
| $FB81 FB83||EA D6 A5B1 FB|| '''subld a[$FB91], 0xA5a''' ||é 'v fR 5|| a($FB91) = 0x5E
|-
| $FB83 FB86|| EA B1 FB 50|| '''ld ($FB91)d, a''' b|| é R 5 (terminator)|| ($FB91) = 0x5E
|-
|$FB87||A5||'''and l'''|| rowspan="5" |Box 3 name ($FB8D overwritten by previous code)||f||a = 0x4C
| $FB86 || 50 || ld d, b || (terminator) ||
|-
| $FB87 FB88||EA A5AE FC|| '''andld l[$FCAE], a''' ||é rowspan=5o 6| Box 3 name ($FB8D overwritten by previous code) || f || a[wBackupMapNumber] = 0x4C
|-
| $FB88 FB8B|| EA AE FC AF|| '''ld ($FCAE),xor a''' ||p||a é= o0x00; 6clears ||the [wBackupMapNumber] =carry 0x4Cflag
|-
| $FB8B FB8C||EA AFD7 FC|| '''xorld [$FCD7], a''' ||é p(0xD7) 6|| a[wPartyCount] = 0x00; clears the carry flag
|-
| $FB8C FB8F|| EA D6 FC 50|| '''ld ($FCD7)d, ab''' || é (0xD7terminator) 6 || [wPartyCount]d = 0x00
|-
| $FB8F FB90||EA 505E FB|| '''ld d[$FB5E], ba'''|| rowspan="5" |Box 4 name ($FB91 overwritten by previous code)| (terminator0x5E) 5||[wEventFlags + d236] = 0x00
|-
|$FB93||E1||'''pop hl'''||{{PK}}||Pops text pointer
| $FB90 || EA 5D FB || '''ld ($FB5E), a''' || rowspan=5 | Box 4 name ($FB91 overwritten by previous code) || é (0x5E) 5 || [wEventFlags + 236] = 0x00
|-
| $FB93 FB94|| E1 || '''pop hl''' || <{{PK> }}|| Pops textreturn pointer in RunMobileScript
|-
| $FB94 || E1 D0|| '''popret hlnc''' || <PK> 'd||Returns Pops return pointer into RunMobileScriptMobileScriptChar
|-
| $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 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.
 
*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 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 video==
{{Youtube|qaVUIAgsLpY|Pokeguy84}}
 
==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]]
Cookies help us deliver our services. By using our services, you agree to our use of cookies.