Guides:Fast 0x1500 ACE: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
Line 306: | Line 306: | ||
* If the code executes succesfully without crashing the game, the bootstrap has now been repaired. |
* If the code executes succesfully without crashing the game, the bootstrap has now been repaired. |
||
=Appendix |
=Appendix= |
||
== |
==Plain text transcripts of codes== |
||
===Test box name=== |
|||
Defeating the provided list of pokémon and viewing Onix's summary will result in the following values starting from the buffered species ID at $D10E, assuming the pokémon is in party slot #2: |
|||
{| class="wikitable" style="margin-left: auto; margin-right: auto; border: none;" |
|||
! Language !! Box name content !! Language !! Box name content !! Language !! Box name content |
|||
|- |
|||
! scope="row"| English |
|||
|| <pre>Box 14: Pk Pk h 'd</pre> |
|||
! scope="row" | Italian |
|||
|| <pre>Box 14: Pk Pk Í</pre> |
|||
! scope="row"| Spanish |
|||
|| <pre>Box 14: Pk Pk Í</pre> |
|||
|} |
|||
Note: "..." refers to one ellipsis character, “pk” refers to the one pk character, "$" refers to the pokédollar sign, "*" refers to the multiplication symbol. Make sure to pay careful attention to upper/lowercase letters. |
|||
===Setup box name=== |
|||
{| class="wikitable" style="margin-left: auto; margin-right: auto; border: none;" |
|||
! Language !! Box name content !! Language !! Box name content !! Language !! Box name content |
|||
|- |
|||
! scope="row"| English |
|||
|| <pre>Box 1: 0 ♀ ♀ Pk ? ♂ E ♀ |
|||
Box 2: Pk 5 'd 'v z é B 5 |
|||
Box 3: 'v 9 é [SPACE] 5 'v , F |
|||
Box 4: 's [SPACE] 5 ? E 's [SPACE] 5 |
|||
Box 5: 'v 's é J 5 'v 8 5 |
|||
Box 6: é F 5 'v * é I 5 |
|||
Box 7: 0 C 's [SPACE] 5 é H 5 |
|||
Box 8: p é G 5 ♀ 'v h h |
|||
Box 9: 's [SPACE] 5 é C 5 'v B |
|||
Box 10: h 's [SPACE] 5 é D 5 * |
|||
Box 11: 'v 9 é E 5 Pk 0 9 |
|||
Box 12: 's B 5 é C 5 Pk 'v |
|||
Box 13: 'v Pk é T 2 h 'd 5 |
|||
Box 14: p 0 5 ♀ Pk 'v G 1</pre> |
|||
! scope="row" | Italian |
|||
|| <pre>Box 1: 0 ♀ ♀ Pk ? ¡ E ♀ |
|||
Box 2: Pk 5 ì ° z é B 5 |
|||
Box 3: ° 9 é [SPACE] 5 ° , F |
|||
Box 4: ó [SPACE] 5 ? E ó [SPACE] 5 |
|||
Box 5: ° ú é I 5 ° 9 5 |
|||
Box 6: é J 5 ° 8 é F 5 |
|||
Box 7: È R ó [SPACE] 5 é H 5 |
|||
Box 8: p é G 5 ♀ ° h h |
|||
Box 9: ó [SPACE] 5 é C 5 ° B |
|||
Box 10: h ó [SPACE] 5 é D 5 * |
|||
Box 11: ° 9 é E 5 Pk 0 9 |
|||
Box 12: ó B 5 é C 5 Pk ° |
|||
Box 13: ° Pk é T 2 h ì 5 |
|||
Box 14: p 0 5 ♀ Pk ° G 1</pre> |
|||
! scope="row"| Spanish |
|||
|| <pre>BBox 1: 0 ♀ ♀ Pk ? ¡ E ♀ |
|||
Box 2: Pk 5 ì ° z é B 5 |
|||
Box 3: ° 9 é [SPACE] 5 ° , F |
|||
Box 4: ó [SPACE] 5 ? E ó [SPACE] 5 |
|||
Box 5: ° ó é J 5 È 5 5 |
|||
Box 6: é I 5 ° 3 é F 5 |
|||
Box 7: È R ó [SPACE] 5 é H 5 |
|||
Box 8: p é G 5 ♀ ° h h |
|||
Box 9: ó [SPACE] 5 é C 5 ° B |
|||
Box 10: h ó [SPACE] 5 é D 5 * |
|||
Box 11: ° 9 é E 5 Pk 0 9 |
|||
Box 12: ó B 5 é C 5 Pk ° |
|||
Box 13: ° Pk é T 2 h ì 5 |
|||
Box 14: p 0 5 ♀ Pk ° G 1</pre> |
|||
|} |
|||
Note: "..." refers to one ellipsis character, “pk” refers to the one pk character, "$" refers to the pokédollar sign, "*" refers to the multiplication symbol. Make sure to pay careful attention to upper/lowercase letters. |
|||
===Reset box name=== |
|||
{| class="wikitable" style="margin-left: auto; margin-right: auto; border: none;" |
|||
! Language !! Box name content !! Language !! Box name content !! Language !! Box name content |
|||
|- |
|||
! scope="row"| English |
|||
|| <pre>Box 1: p 0 Mn ♀ Pk p 0 2 |
|||
Box 2: E ♀ Pk 'v y é h 5 |
|||
Box 3: F 'v , h 's h 5 p |
|||
Box 4: 'v 6 h 's h 5 p p |
|||
Box 5: 0 'm 's h 5 'v M F |
|||
Box 6: 's h 5 'v B A 'd Pk |
|||
Box 7: Pk h 'd |
|||
Box 14: p 0 5 ♀ Pk 'v G 1</pre> |
|||
! scope="row" | Italian |
|||
|| <pre>Box 1: p 0 Mn È 2 ♀ Pk 5 |
|||
Box 2: ° y é f 5 F ° , |
|||
Box 3: Ù f 5 È È Ù f 5 |
|||
Box 4: È Á Ù f 5 5 È W |
|||
Box 5: Ù f 5 È [SPACE] Ù f 5 |
|||
Box 6: Pk Pk Í Í Í |
|||
Box 14: p 0 5 ♀ Pk ° G 1 </pre> |
|||
! scope="row"| Spanish |
|||
|| <pre>Box 1: p 0 Mn È 2 ♀ Pk 5 |
|||
Box 2: ° y é f 5 F ° , |
|||
Box 3: Ù f 5 È È Ù f 5 |
|||
Box 4: È Á Ù f 5 5 È W |
|||
Box 5: Ù f 5 È [SPACE] Ù f 5 |
|||
Box 6: Pk Pk Í Í Í |
|||
Box 14: p 0 5 ♀ Pk ° G 1 </pre> |
|||
|} |
|||
Note: "..." refers to one ellipsis character, “pk” refers to the one pk character, "$" refers to the pokédollar sign, "*" refers to the multiplication symbol. Make sure to pay careful attention to upper/lowercase letters. |
|||
==In-depth explanation of the setup== |
|||
===Explanation on the 0x1500 ACE setup=== |
|||
This setup uses [[0x1500 control code arbitrary code execution|0x1500 control code ACE]]. Since the page already contains an explanation on how it works, this page will focus on what the setup does to achieve its effect. |
|||
Relevant addresses for this explanation: |
|||
{| class="wikitable" style="margin-left: auto; margin-right: auto; border: none;" |
|||
! Address !! Function |
|||
|- |
|||
| $D002 || Address where the last read mail is stored. |
|||
|- |
|||
| $D073 || Address where the names of pokémon are buffered. |
|||
|- |
|||
| $D086 || Address where the names of used items are buffered. |
|||
|- |
|||
| $D108 || Address where the current selected party pokémon’s species is buffered. |
|||
|- |
|||
| $D109 || Address where the current selected party pokémon’s party slot is buffered. |
|||
|- |
|||
| $D10C || Address where the amount of items tossed is buffered. |
|||
|- |
|||
| $D10D || Address where the amount of the last selected item is buffered. |
|||
|- |
|||
| $D10E || Address where the data of the last viewed pokémon is buffered. |
|||
|} |
|||
* Attempting to toss 21 items will buffer $15 at $D10C. |
|||
* For a list at the highest size it has ever been, the "Quantity" of the CANCEL button will be $00, which will be buffered at $D10D when the CANCEL button is used with A. This forms the required $1500 to start executing ACE. |
|||
* By opening the start menu and moving one step up at the specified location, the game will load in a $C0 (ret nz) at $CD70, allowing safe return from the effects of $1500. The game will resume executing from $D10E onward. |
|||
* The last pokémon viewed is buffered from $D10E onward. Setting Tackle ($21) as its first move allows safe passage over Screech ($67). |
|||
* Rocky’s trainer ID is fixed and will be $BF (cp a, which resets the carry flag) and $1E. Both values are safe to pass. |
|||
* Rocky’s XP total will end up between 326 and 350. This is always interpreted as $01 and $XX. since the high HP stat exp byte is always $00, the total is interpreted as ld bc, $00XX. This means that exp is always safe to pass. |
|||
* Due to the specific pokémon defeated, the data in the stat experience fields will be read as $D2 $00 $F0. This is interpreted as jp nc $F000, due to echo ram this will redirect execution to $D000, which is where the last read mail was buffered. The nc condition is always fulfilled thanks to the previous $BF (cp a) in Onix’s Trainer ID. |
|||
* At this point, the mail will redirect execution to $FB75. Due to echo ram, this will effectively redirect execution to $DB75, the start of box name 1. Please note that $D003 and $D004 are overwritten by opening the withdraw screen to the values of the current box and slot of the currently selected stored pokémon. These values are taken care of by setting the first character of the mail to $FA, which is interpreted along with the next two values as ld a, (YYXX). |
|||
* Afterwards, the game will simply execute the box name code. |
|||
* For the alternative method, selecting Spearow to read its mail will buffer both its species and party slot to $D108 and $D109. By putting a Spearow in the topmost party slot, this will buffer a $15 and $00 respectively. One drawback of this approach is that these RAM values are more volatile and are immediately overwritten after executing ACE. Luckily, Onix’s species ID ($5F) acts as a text terminator, preventing users from accidentally crashing the game if they forget to reset these values. |
|||
Additional note: $D086 isn’t actually relevant to the setup. The reason it’s mentioned here is because using an item buffers the item’s name to this location, placing a $50 terminator just a bit after where the pokémon’s name is normally buffered. Having this terminator in place will allow you to safely view an unterminated pokémon’s name. |
|||
===Effect of the test box name=== |
|||
Box name data starts from $D8BF onward. Converting the provided mail code to assembly results in the following: |
|||
====English==== |
|||
<pre> |
<pre> |
||
F6 FF or $FF ; a = $FF, reset carry flag |
|||
5F ld e, a |
|||
D0 ret nc ; Safely return to normal game operation |
|||
00 nop |
|||
21 67 00 ld hl, $0067 ; Determined by Onix's moves, Tackle and Screech |
|||
00 nop |
|||
BF cp a, a |
|||
1E 00 ld e, $00 ; $BF1E corresponds to Onix's OTID of 48926 |
|||
01 XX 00 ld bc, $00XX ; $XX is determined by the exact level of the opponents fought |
|||
D2 00 F0 jp nc, $F000 |
|||
</pre> |
</pre> |
||
====French==== |
|||
After the effect of 0x1500 ACE is triggered and execution is redirected to $D10E, execution slides harmlessly through move, OTID and experience data before jumping to $F000, which is echo RAM for $D000 and is located two bytes before the last buffered mail. |
|||
<pre> |
|||
===Effect of Sandshrew=== |
|||
21 80 80 ld hl, $8080 |
|||
C9 ret ; Safely return to normal game operation |
|||
</pre> |
|||
====German==== |
|||
Defeating the provided list of pokémon and viewing Sandshrew's summary will result in the following values starting from the buffered species ID at $D10E, assuming the pokémon is in party slot #2: |
|||
<pre> |
<pre> |
||
21 80 80 ld hl, $8080 |
|||
1B dec de |
|||
F6 FF or $FF ; a = $FF, reset carry & zero flag |
|||
30 0A jr .jump |
|||
C0 ret nz ; Safely return to normal game operation |
|||
XX XX XX XX XX XX XX XX XX 73 ; Move, OTID and experience is skipped over by the jr instruction |
|||
</pre> |
|||
00 nop ; .jump |
|||
C3 00 F0 jp $F000 |
|||
====Italian & Spanish==== |
|||
<pre> |
|||
C9 ret ; Safely return to normal game operation |
|||
</pre> |
</pre> |
||
===Effect of the setup box name code=== |
|||
After the effect of 0x1500 ACE is triggered and execution is redirected to $D10E, execution jumps over move, OTID and experience data, followed by another jump to $F000, which is echo RAM for $D000 and is located two bytes before the last buffered mail. |
|||
Converting the characters from box names to assembly results in the following code. Please note that the box name code overwrites part of itself, the translated assembly assumes the code was already used once. |
|||
===Effect of the mail=== |
|||
The code overwrites part of itself to call the byteFill function. This will fill the area between $D859 and $D88A with $FF values, setting all 50 TM quantities to 255. Separately, this code writes five bytes from $DA10 onward, the effect pointer of TM15, installing a TM15 bootstrap that redirects execution to the Mail Writer. Lastly, when combined with the mail code, it will convert the first item in the main item pocket to a TM15. |
|||
The last read mail is buffered from $D002 onward. Please note that viewing the bad clone in box #1 will write the value $01 to $D003 and $D004. Converting the characters from the mail to assembly results in the following, ordered by language: |
|||
====English==== |
====English==== |
||
<pre> |
<pre> |
||
Box 1: $DB75 |
|||
FA 01 01 ld a, ($0101) ; a = $C3 |
|||
AF xor a ; a = $00 |
|||
D6 FF sub $FF ; a = $01 |
|||
D4 75 FB call nc, wBoxNames |
|||
EA D0 FB ld ($FBD0), a |
|||
A7 and a, a ; Reset carry flag |
|||
D6 F1 sub $F1 ; a = $10 |
|||
E1 pop hl ; After returning from 0x1500 ACE, this prevents the print function from printing text in WRAM. |
|||
50 ld d; b |
|||
</pre> |
|||
Box 2: $DB7E |
|||
====French==== |
|||
EA 87 FB ld ($FB87), a |
|||
D6 EF sub $EF ; a = $21 |
|||
EA 86 FB ld ($FB87), a |
|||
21 |
|||
Box 3: $DB87 |
|||
<pre> |
|||
10 FA ld hl, $FA10 ; Execution pointer of wrong pocket TM15 |
|||
EA AC FB ld ($F8AC), a |
|||
D6 F5 sub $F5 ; a = $CE, TM15's item ID |
|||
EA 93 F8 ld (wItems), a ; Main item pocket, first item ID |
|||
D6 F3 sub $F3 ; a = $DB |
|||
EA 86 FB ld ($FB86), a |
|||
EA E9 FB ld ($FBE9), a |
|||
4E ld a, a |
|||
D6 BA sub $BA ; a = $21 |
|||
EA 75 FB ld (wBoxNames), a |
|||
D6 FF sub $FF ; a = $22 |
D6 FF sub $FF ; a = $22 |
||
FB ei |
|||
EA B7 FB ld ($FBB7), a |
|||
50 ld d, b |
|||
AF xor a ; a = $00, set zero flag |
|||
CC 75 DB call z, wBoxNames |
|||
Box 4: $DB90 |
|||
E1 pop hl ; After returning from 0x1500 ACE, this prevents the print function from printing text in WRAM. |
|||
EA AA FB ld ($FBAA), a |
|||
85 add a, l ; a = $32 |
|||
EA D1 FB ld ($FBD1), a |
|||
85 add a, l ; a = $42 |
|||
50 ld d, b |
|||
Box 5: $DB99 |
|||
F6 81 or $81 ; a = $C3, reset carry flag |
|||
D4 D6 FB call nc, .setupBootStrap |
|||
B4 or a, h ; a = $FB |
|||
D6 A2 sub $A2 ; a = $59, reset carry flag |
|||
50 ld d, b |
|||
Box 6: $DBA2 |
|||
EA AD FB ld ($FBAD), a |
|||
D4 AA FB call nc, .writeBootstrap |
|||
D6 81 sub $81 ; a = $D8, set carry flag |
|||
22 ldi (hl), a ; .writeBootstrap |
|||
Box 7: $DBAB |
|||
D0 ret nc |
D0 ret nc |
||
21 59 F8 ld hl, $F859 ; $59 will later be overwritten to $41 to prevent crashing when viewing the newly written name |
|||
AF xor a ; a = $00 |
|||
EA D2 FB ld ($FBD2), a |
|||
50 ld d, b |
|||
Box 8: $DBB4 |
|||
D6 D0 sub $D0 ; a = $30 |
|||
EA D5 FB ld ($FBD5), a |
|||
D6 EF sub $EF ; a = $41 |
|||
FB ei |
|||
50 ld d, b |
|||
Box 9: $DBBD |
|||
EA D4 FB ld ($FBD4), a |
|||
EA AD FB ld ($FBAD), a ; Overwrites part of box #7 to prevent a crash |
|||
B5 or a, l ; a = $59 |
|||
FB ei |
|||
50 ld d, b |
|||
Box 10: $DBC6 |
|||
D6 8B sub $8B ; a = $CE |
|||
EA 93 F8 ld (wItems), a ; Main item pocket, item #1's ID |
|||
F6 FF or $FF ; a = $FF, reset carry flag |
|||
FB ei |
|||
50 ld d, b |
|||
Box 11: $DBCF |
|||
FB ei |
|||
01 32 00 ld bc, 0032 |
|||
D2 41 30 jp nc, byteFill |
|||
F5 push af ; .SetupBootStrap |
|||
50 ld d, b |
|||
Box 12: $DBD8 |
|||
D6 85 sub $85 ; a = 3E, reset carry flag |
|||
D4 AA FB call nc, .writeBootstrap |
|||
85 add a, l ; a = $4F |
|||
E6 84 and $84 ; a = $04, reset carry flag |
|||
50 ld d, b |
|||
Box 13: $DBE1 |
|||
D4 AA FB call nc, .writeBootstrap |
|||
F1 pop af ; a = $C3, reset carry flag |
|||
D2 AA FB jp nc, .writeBootstrap |
|||
FB |
|||
50 |
|||
Bootstrap: $DA10 |
|||
3E 04 ld a, $04 |
|||
C3 59 D8 jp wTMsHMs |
|||
</pre> |
</pre> |
||
==== |
====Italian==== |
||
<pre> |
<pre> |
||
Box 1: $DB75 |
|||
FA 01 01 ld a, ($0101) ; a = $C3 |
|||
EA A3 FB ld ($FBA3), a ; Due to mail, a = $00 |
|||
D6 FF sub $FF ; a = $01 |
|||
EA |
EA A1 FB ld ($FBA1), a |
||
50 ld d, b |
|||
C6 FF add $FF ; a = $21 |
|||
EA 75 FB ld (wBoxNames), a |
|||
Box 2: $DB7E |
|||
D6 CF sub $CF ; a = $32 |
|||
EA A2 FB ld ($FBA2), a |
|||
C6 EF add $EF ; a = $21 |
|||
FB ei |
|||
50 ld d, b |
|||
Box 3: $DB87 |
|||
EA 9E FB ld ($FB9E), a |
|||
D6 C8 sub $C8 ; a = $59 |
|||
EA 9F FB ld ($FB9F), a |
|||
50 ld d, b |
|||
Box 4: $DB90 |
|||
C6 D6 add $D6 ; a = $2F |
|||
EA A5 FB ld ($FBA5), a |
|||
D6 FF sub $FF ; a = $30 |
|||
FB ei |
|||
50 ld d, b |
|||
Box 5: $DB99 |
|||
EA A6 FB ld ($FBA6), a |
|||
F6 FF or $FF |
|||
21 59 F8 ld hl, wTMsHMs ; $59 gets overwritten to $D8 to prevent a crash upon viewing the newly written box name |
|||
01 |
|||
Box 6: $DBA2 |
|||
32 00 ld bc, $0032 |
|||
CD 2F 30 call byteFill |
|||
AF xor a ; a = $00 |
AF xor a ; a = $00 |
||
F6 |
F6 E2 or $E2 ; a = $E2 |
||
50 ld d, b |
|||
F6 80 or $80 ; a : $CE |
|||
EA 93 F8 ld (wItems), a ; Main item pocket, first item ID |
|||
Box 7: $DBAB |
|||
F0 F0 ld a, ($FFF0) ; a = 0, zero flag is NOT reset |
|||
84 add a, h ; af = $DA10 |
|||
C4 75 FB call z, wBoxNames |
|||
F5 push af |
|||
E1 pop hl ; After returning from 0x1500 ACE, this prevents the print function from printing text in WRAM. |
|||
E1 pop hl |
|||
D6 9C sub $9C ; a = $3E |
|||
CD C8 FB call .write |
|||
50 ld d, b |
|||
Box 8: $DBB4 |
|||
E6 84 and $84 ; a = $04 |
|||
CD C8 FB call .write |
|||
C6 BF add $BF ; a = $C3 |
|||
BF cp a |
|||
50 ld d, b |
|||
Box 9: $DBBD |
|||
CD C8 FB call .write |
|||
C6 96 add $96 ; a = $59 |
|||
CD C8 FB call .write |
|||
50 ld d, b |
|||
Box 10: $DBC6 |
|||
D6 81 sub $81 ; a = $D8 |
|||
22 ldi (hl), a ; .write |
|||
C9 ret |
|||
Bootstrap: $DA10 |
|||
3E 04 ld a, $04 |
|||
C3 59 D8 jp wTMsHMs |
|||
</pre> |
</pre> |
||
==== |
====Spanish==== |
||
<pre> |
<pre> |
||
Box 1: $DB75 |
|||
FA 01 01 ld a, ($0101) ; a = $C3 |
|||
EA A3 FB ld ($FBA3), a ; Due to mail, a = $00 |
|||
D6 FF sub $FF ; a = $01 |
|||
EA C8 FB ld ($FBC8), a |
|||
EA A1 FB ld ($FBA1), a |
|||
50 ld d, b |
|||
Box 2: $DB7E |
|||
D6 CF sub $CF ; a = $32 |
|||
EA A2 FB ld ($FBA2), a |
|||
C6 EF add $EF ; a = $21 |
|||
FB ei |
|||
50 ld d, b |
|||
Box 3: $DB87 |
|||
EA 9E FB ld ($FB9E), a |
|||
D6 C8 sub $C8 ; a = $59 |
|||
EA 9F FB ld ($FB9F), a |
|||
50 ld d, b |
|||
Box 4: $DB90 |
|||
C6 D2 add $D2 ; a = $2B |
|||
EA A5 FB ld ($FBA5), a |
|||
D6 FB sub $FF ; a = $30 |
|||
FB ei |
|||
50 ld d, b |
|||
Box 5: $DB99 |
|||
EA A6 FB ld ($FBA6), a |
|||
F6 FF or $FF |
|||
21 59 F8 ld hl, wTMsHMs ; $59 gets overwritten to $D8 to prevent a crash upon viewing the newly written box name |
|||
01 |
|||
Box 6: $DBA2 |
|||
32 00 ld bc, $0032 |
|||
CD 2B 30 call byteFill |
|||
AF xor a ; a = $00 |
AF xor a ; a = $00 |
||
F6 E2 or $E2 ; a = $E2 |
|||
CD 75 FB call wBoxNames |
|||
50 ld d, b |
|||
EA 9F FB ld ($FB9F), a ; Prevent a game crash when viewing the box name |
|||
F6 4E or $4E ; a = $DE |
|||
Box 7: $DBAB |
|||
C6 F0 add $F0 ; a = $CE |
|||
84 add a, h ; af = $DA10 |
|||
EA 93 F8 ld (wItems), a ; Main item pocket, first item ID |
|||
F5 push af |
|||
E1 pop hl ; After returning from 0x1500 ACE, this prevents the print function from printing text in WRAM. |
|||
E1 pop hl |
|||
D6 9C sub $9C ; a = $3E |
|||
CD C8 FB call .write |
|||
50 ld d, b |
|||
Box 8: $DBB4 |
|||
E6 84 and $84 ; a = $04 |
|||
CD C8 FB call .write |
|||
C6 BF add $BF ; a = $C3 |
|||
BF cp a |
|||
50 ld d, b |
|||
Box 9: $DBBD |
|||
CD C8 FB call .write |
|||
C6 96 add $96 ; a = $59 |
|||
CD C8 FB call .write |
|||
50 ld d, b |
|||
Box 10: $DBC6 |
|||
D6 81 sub $81 ; a = $D8 |
|||
22 ldi (hl), a ; .write |
|||
C9 ret |
C9 ret |
||
Bootstrap: $DA10 |
|||
3E 04 ld a, $04 |
|||
C3 59 D8 jp wTMsHMs |
|||
</pre> |
</pre> |
||
===Effect of the |
===Effect of the TM code=== |
||
Converting the TM quantities to assembly results in the following code. Please note that this code requires a value of $04 in register a in order to properly work. |
|||
Box name data starts from $D8BF onward. Converting the provided mail code to assembly results in the following: |
|||
====English==== |
====English==== |
||
<pre> |
<pre> |
||
11 80 D2 ld de, $D280 |
|||
F6 FF or $FF ; a = $FF, reset carry flag |
|||
D5 push de |
|||
D0 ret nc ; Safely return to normal game operation |
|||
D5 push de ; .nextMail |
|||
D5 push de |
|||
21 75 5E ld hl, _ComposeMailMessage |
|||
CF rst08h, FarCall a:hl |
|||
E1 pop hl |
|||
D1 pop de |
|||
2A ldi a, (hl) ; .continue |
|||
FE 50 cp $50 |
|||
38 FB jr c, .continue |
|||
28 0A jr z, .terminator |
|||
87 add a, a |
|||
86 add a, (hl) |
|||
12 ld (de), a |
|||
13 inc de |
|||
23 inc hl |
|||
81 add a, c |
|||
4F ld c, a |
|||
12 ld (de), a |
|||
18 EF jr .continue |
|||
21 01 C5 ld hl, $C501 ; .screenLoop |
|||
4D ld c, l |
|||
CD CC 38 call PrintBCDNumber.loop - 1 |
|||
1B dec de ; .goBack |
|||
CD 4B 35 call JoyTextDelay_ForcehJoyDown |
|||
BD cp a, l ; l = $04 |
|||
28 D9 jr z, .nextMail |
|||
38 F0 jr c, .displayLoop |
|||
FE 08 cp $08 |
|||
C8 ret z |
|||
18 F2 jr .goBack |
|||
</pre> |
</pre> |
||
==== |
====Italian==== |
||
<pre> |
<pre> |
||
11 80 D2 ld de, $D280 |
|||
D5 push de |
|||
C9 ret ; Safely return to normal game operation |
|||
D5 push de ; .nextMail |
|||
D5 push de |
|||
21 C5 5E ld hl, _ComposeMailMessage |
|||
CF rst08h, FarCall a:hl |
|||
E1 pop hl |
|||
D1 pop de |
|||
2A ldi a, (hl) ; .continue |
|||
FE 50 cp $50 |
|||
38 FB jr c, .continue |
|||
28 0A jr z, .terminator |
|||
87 add a, a |
|||
86 add a, (hl) |
|||
12 ld (de), a |
|||
13 inc de |
|||
23 inc hl |
|||
81 add a, c |
|||
4F ld c, a |
|||
12 ld (de), a |
|||
18 EF jr .continue |
|||
21 01 C5 ld hl, $C501 ; .screenLoop |
|||
4D ld c, l |
|||
CD BA 38 call PrintBCDNumber.loop |
|||
1B dec de ; .goBack |
|||
CD 39 35 call JoyTextDelay_ForcehJoyDown |
|||
BD cp a, l ; l = $04 |
|||
28 D9 jr z, .nextMail |
|||
38 F0 jr c, .displayLoop |
|||
FE 08 cp $08 |
|||
C8 ret z |
|||
18 F2 jr .goBack |
|||
</pre> |
</pre> |
||
==== |
====Spanish==== |
||
<pre> |
<pre> |
||
11 80 D2 ld de, $D280 |
|||
D5 push de |
|||
F6 FF or $FF ; a = $FF, reset carry & zero flag |
|||
D5 push de ; .nextMail |
|||
C0 ret nz ; Safely return to normal game operation |
|||
D5 push de |
|||
21 E8 5E ld hl, _ComposeMailMessage |
|||
CF rst08h, FarCall a:hl |
|||
E1 pop hl |
|||
D1 pop de |
|||
2A ldi a, (hl) ; .continue |
|||
FE 50 cp $50 |
|||
38 FB jr c, .continue |
|||
28 0A jr z, .terminator |
|||
87 add a, a |
|||
86 add a, (hl) |
|||
12 ld (de), a |
|||
13 inc de |
|||
23 inc hl |
|||
81 add a, c |
|||
4F ld c, a |
|||
12 ld (de), a |
|||
18 EF jr .continue |
|||
21 01 C5 ld hl, $C501 ; .screenLoop |
|||
4D ld c, l |
|||
CD AC 38 call PrintBCDNumber.loop - 1 |
|||
1B dec de ; .goBack |
|||
CD 35 35 call JoyTextDelay_ForcehJoyDown |
|||
BD cp a, l ; l = $04 |
|||
28 D9 jr z, .nextMail |
|||
38 F0 jr c, .displayLoop |
|||
FE 08 cp $08 |
|||
C8 ret z |
|||
18 F2 jr .goBack |
|||
</pre> |
</pre> |
||
===Effect of the reset box name code=== |
|||
====Italian & Spanish==== |
|||
Converting the characters from box names to assembly results in the following code. This code overwrites the latter half of party pokémon #3's stat experience data, allowing it to function as a TM25 bootstrap that redirects execution to the Mail Writer. |
|||
====English==== |
|||
<pre> |
<pre> |
||
Box 1: $DB75 |
|||
C9 ret ; Safely return to normal game operation |
|||
AF xor a ; a = $00, reset carry flag |
|||
F6 E2 or $E2 ; a = $E2 |
|||
F5 push af |
|||
E1 pop hl |
|||
AF xor a ; a = $00, reset carry flag |
|||
F6 F8 or $F8 ; a = $F8 |
|||
50 ld d, b |
|||
Box 2: $DB7E |
|||
84 add a, h ; af = $DA10 |
|||
F5 push hl |
|||
E1 pop hl ; hl = $DA10 |
|||
D6 B8 sub $B8 ; a = $22 |
|||
EA A8 FB ld (.write), a |
|||
50 ld d, b |
|||
Box 3: $DB87 |
|||
85 add a, l ; a = $33 |
|||
D6 F4 sub $F4 ; a = $3E |
|||
A7 and a ; Reset carry flag |
|||
D4 A8 FB call nc, .write |
|||
AF xor a ; a = $00 |
|||
50 ld d, b |
|||
Box 4: $DB90 |
|||
D6 FC sub $FC ; a = $04 |
|||
A7 and a ; Reset carry flag |
|||
D4 A8 FB call nc, .write |
|||
AF xor a ; a = $00 |
|||
AF xor a |
|||
50 ld d, b |
|||
Box 5: $DB99 |
|||
F6 D2 or $D2 ; a = $D2 |
|||
D4 A8 FB call nc, .write |
|||
D6 8C sub $8C ; a = $46 |
|||
85 add a, l ; a = $59 |
|||
50 ld d, b |
|||
Box 6: $D8A2 |
|||
D4 A8 FB call nc, .write |
|||
D6 81 sub $81 ; a = $D8 |
|||
A7 and a ; Reset carry flag |
|||
22 ldi (hl), a ; .write |
|||
D0 call nc |
|||
Party pokémon #3's stat experience, starting from $DA9A |
|||
3E 04 ld a, $04 |
|||
D2 59 D8 jp nc, wTMsHMs |
|||
</pre> |
</pre> |
||
====Italian & Spanish==== |
|||
===Explanation on the 0x1500 ACE setup=== |
|||
<pre> |
|||
This setup uses [[0x1500 control code arbitrary code execution|0x1500 control code ACE]]. Since the page already contains an explanation on how it works, this page will focus on what the setup does to achieve its effect. |
|||
Box 1: $DB75 |
|||
AF xor a ; a = $00 |
|||
F6 E2 or $E2 ; a = $E2 |
|||
C6 F8 add $F8 ; af = $DA10 |
|||
F5 push af |
|||
E1 pop hl ; hl = $DA10 |
|||
FB ei |
|||
50 ld d, b |
|||
Box 2: $DB7E |
|||
Relevant addresses for this explanation: |
|||
D6 B8 sub $B8 ; a = $22 |
|||
EA 9F FB ld (.write), a |
|||
85 add a, l ; a = $32 |
|||
D6 F4 sub $F4 ; a = $3E |
|||
50 ld d, b |
|||
Box 3: $DB87 |
|||
{| class="wikitable" style="margin-left: auto; margin-right: auto; border: none;" |
|||
CD 9F FB call .write |
|||
! Address !! Function |
|||
C6 C6 add $C6 ; a = $04 |
|||
|- |
|||
CD 9F FB call .write |
|||
| $D002 || Address where the last read mail is stored. |
|||
50 ld d, b |
|||
|- |
|||
| $D073 || Address where the names of pokémon are buffered. |
|||
|- |
|||
| $D086 || Address where the names of used items are buffered. |
|||
|- |
|||
| $D108 || Address where the current selected party pokémon’s species is buffered. |
|||
|- |
|||
| $D109 || Address where the current selected party pokémon’s party slot is buffered. |
|||
|- |
|||
| $D10C || Address where the amount of items tossed is buffered. |
|||
|- |
|||
| $D10D || Address where the amount of the last selected item is buffered. |
|||
|- |
|||
| $D10E || Address where the data of the last viewed pokémon is buffered. |
|||
|} |
|||
Box 4: $DB90 |
|||
* Attempting to toss 21 items will buffer $15 at $D10C. |
|||
C6 BF add $BF ; a = $C3 |
|||
* For a list at the highest size it has ever been, the "Quantity" of the CANCEL button will be $00, which will be buffered at $D10D when the CANCEL button is used with A. This forms the required $1500 to start executing ACE. |
|||
CD 9F FB call .write |
|||
* By opening the start menu and moving one step up at the specified location, the game will load in a $C0 (ret nz) at $CD70, allowing safe return from the effects of $1500. The game will resume executing from $D10E onward. |
|||
FB ei |
|||
* The last pokémon viewed is buffered from $D10E onward. Setting Tackle ($21) as its first move allows safe passage over Screech ($67). |
|||
C6 96 add $96 ; a = $59 |
|||
* Rocky’s trainer ID is fixed and will be $BF (cp a, which resets the carry flag) and $1E. Both values are safe to pass. |
|||
50 ld d, b |
|||
* Rocky’s XP total will end up between 326 and 350. This is always interpreted as $01 and $XX. since the high HP stat exp byte is always $00, the total is interpreted as ld bc, $00XX. This means that exp is always safe to pass. |
|||
* Due to the specific pokémon defeated, the data in the stat experience fields will be read as $D2 $00 $F0. This is interpreted as jp nc $F000, due to echo ram this will redirect execution to $D000, which is where the last read mail was buffered. The nc condition is always fulfilled thanks to the previous $BF (cp a) in Onix’s Trainer ID. |
|||
* At this point, the mail will redirect execution to $FB75. Due to echo ram, this will effectively redirect execution to $DB75, the start of box name 1. Please note that $D003 and $D004 are overwritten by opening the withdraw screen to the values of the current box and slot of the currently selected stored pokémon. These values are taken care of by setting the first character of the mail to $FA, which is interpreted along with the next two values as ld a, (YYXX). |
|||
* Afterwards, the game will simply execute the box name code. |
|||
Box 5: $DB99 |
|||
* For the alternative method, selecting Spearow to read its mail will buffer both its species and party slot to $D108 and $D109. By putting a Spearow in the topmost party slot, this will buffer a $15 and $00 respectively. One drawback of this approach is that these RAM values are more volatile and are immediately overwritten after executing ACE. Luckily, Onix’s species ID ($5F) acts as a text terminator, preventing users from accidentally crashing the game if they forget to reset these values. |
|||
CD 9F FB call .write |
|||
C6 7F add $7F ; a = $D8 |
|||
7F ld a, a |
|||
22 ldi (hl), a ; .write |
|||
C9 ret |
|||
Party pokémon #3's stat experience, starting from $DA9A |
|||
Additional note: $D086 isn’t actually relevant to the setup. The reason it’s mentioned here is because using an item buffers the item’s name to this location, placing a $50 terminator just a bit after where the pokémon’s name is normally buffered. Having this terminator in place will allow you to safely view an unterminated pokémon’s name. |
|||
3E 04 ld a, $04 |
|||
C3 59 D8 jp wTMsHMs |
|||
</pre> |
|||
=Plain text transcript for codes= |
=Plain text transcript for codes= |