pokemon rom researcher

Seen July 2nd, 2016
Posted August 31st, 2015
104 posts
10.8 Years
I found something that bugs a lot of people, the fact HM moves cannot be deleted, can be easily erased on Fire Red.
There are two main routines that check if an attack is an HM or not:
one for the battle routine at 0x80441B8;
one for the in-screen move learning at 0x08125A90.
Both routines check for HMs in different locations.
The first checks if the attack given is part of a non-deletion list at 0x0825e014, ended in FFFF, and searches through it until it reaches the ending value, or finding a valid attack. Here's the code:
ROM:080441B8 ; =============== S U B R O U T I N E =======================================
ROM:080441B8 Battle_HM_set                           ; CODE XREF: sub_80CE8DC+A80p
ROM:080441B8                 PUSH    {R4,LR}
ROM:080441BA                 LSLS    R0, R0, #0x10
ROM:080441BC                 LSRS    R3, R0, #0x10        ;given attack stored
ROM:080441BE                 LDR     R2, =unk_825E014        ;list location
ROM:080441C0                 LDRH    R0, [R2]
ROM:080441C2                 LDR     R1, =0xFFFF
ROM:080441C4                 CMP     R0, R1
ROM:080441C6                 BEQ     loc_80441EA          ;is end of list
ROM:080441C8                 MOVS    R4, R1
ROM:080441CA                 ADDS    R1, R2, #0
ROM:080441CC loc_80441CC                             ; CODE XREF: Battle_HM_set+30j
ROM:080441CC                 LDRH    R0, [R2]
ROM:080441CE                 ADDS    R1, #2
ROM:080441D0                 ADDS    R2, #2
ROM:080441D2                 CMP     R0, R3
ROM:080441D4                 BNE     loc_80441E4
ROM:080441D6                 MOVS    R0, #1        ;is same attack, undeletable
ROM:080441D8                 B       loc_80441EC
ROM:080441D8 ; ---------------------------------------------------------------------------
ROM:080441DA                 DCB    0
ROM:080441DB                 DCB    0
ROM:080441DC off_80441DC     DCD unk_825E014         ; DATA XREF: Battle_HM_set+6r
ROM:080441E0 dword_80441E0   DCD 0xFFFF              ; DATA XREF: Battle_HM_set+Ar
ROM:080441E4 ; ---------------------------------------------------------------------------
ROM:080441E4 loc_80441E4                             ; CODE XREF: Battle_HM_set+1Cj
ROM:080441E4                 LDRH    R0, [R1]
ROM:080441E6                 CMP     R0, R4
ROM:080441E8                 BNE     loc_80441CC    ;new end_of_list check
ROM:080441EA loc_80441EA                             ; CODE XREF: Battle_HM_set+Ej
ROM:080441EA                 MOVS    R0, #0      ;ended list, attack deletable
ROM:080441EC loc_80441EC                             ; CODE XREF: Battle_HM_set+20j
ROM:080441EC                 POP     {R4}
ROM:080441EE                 POP     {R1}
ROM:080441F0                 BX      R1
The other searches for them at 0x0845A80C, the TM attack list. It does so by looking over the TMs at position 50+
ROM:08125A90 ; =============== S U B R O U T I N E =======================================
ROM:08125A90 Check_for_HM                            ; CODE XREF: ROM:0813939Ep
ROM:08125A90                 PUSH    {LR}
ROM:08125A92                 LSLS    R0, R0, #0x10
ROM:08125A94                 LSRS    R2, R0, #0x10
ROM:08125A96                 MOVS    R1, #0
ROM:08125A98                 LDR     R3, =TM_List        ;Location for all TM Attacks
ROM:08125A9A loc_8125A9A                             ; CODE XREF: Check_for_HM+28j
ROM:08125A9A                 MOVS    R0, R1
ROM:08125A9C                 ADDS    R0, #0x32
ROM:08125A9E                 LSLS    R0, R0, #1
ROM:08125AA0                 ADDS    R0, R0, R3
ROM:08125AA2                 LDRH    R0, [R0]             ;loads HM required by R1
ROM:08125AA4                 CMP     R0, R2
ROM:08125AA6                 BNE     loc_8125AB0
ROM:08125AA8                 MOVS    R0, #1            ;if equal, undeletable, return 1
ROM:08125AAA                 B       loc_8125ABC
ROM:08125AAA ; ---------------------------------------------------------------------------
ROM:08125AAC off_8125AAC     DCD TM_List             ; DATA XREF: Check_for_HM+8r
ROM:08125AB0 ; ---------------------------------------------------------------------------
ROM:08125AB0 loc_8125AB0                             ; CODE XREF: Check_for_HM+16j
ROM:08125AB0                 ADDS    R0, R1, #1
ROM:08125AB2                 LSLS    R0, R0, #0x18
ROM:08125AB4                 LSRS    R1, R0, #0x18
ROM:08125AB6                 CMP     R1, #6         ;maximum TM checking
ROM:08125AB8                 BLS     loc_8125A9A
ROM:08125ABA                 MOVS    R0, #0       ;not any TM, deletable
ROM:08125ABC loc_8125ABC                             ; CODE XREF: Check_for_HM+1Aj
ROM:08125ABC                 POP     {R1}
ROM:08125ABE                 BX      R1
So, how to "fix" it? Well, change 080441D6 to 00 and 08125AA8 to 00 to make no attack undeletable.
If, on the other hand, you have a wish to prevent the player from deleting random attacks, simply repoint the list to a location where your attacks fit (plus the 0xffff part), and change the following addresses:
0x08125A9C to 00 (one byte only)
0x08125AAC to your list pointer reversed
0x08125AB6 to the number of attacks you placed -2 (to a max of 101 undeletable attacks)

If, for some reason, you wish to make all attacks undeletable, change
080441EA to 00
08125ABA to 00

Hope this helps those hacks who want to get rid of HMs.
Here are the links for my work

Currently working on:
Battle Script Documentation
Another large project