• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Help Thread: ASM & Disassembly

Status
Not open for further replies.

Blah

Free supporter
  • 1,924
    Posts
    11
    Years
    Thank you very much! :)
    I'd just need some clarifications: everytime I call a function, I have to load it into rX register, then use "bl *label*" and put "bx rX" in that label? The function is executed after that last line, correct? And does it automatically put the value stored in LR into PC at the end of the function?
    About flags, they all can be accessed by 0xXXX then?
    Sorry for bothering.

    EDIT
    I found these lines in a code


    In this case, after executing the function it all ends because I haven't stored PC (or LR?) with bl?

    ugh, I hate that way of doing it. You have the bx command taking up 2 bytes every time you needed to jump if you do the way which you've quoted. It's not anymore efficient, and not any more readable either, it just wastes space. Only hipsters use it.


    Did you read my explanation for how the bl and bx work together to create the longer jump range while allowing the return? The answer to your question was there in that explanation. Anyways,


    everytime I call a function, I have to load it into rX register, then use "bl *label*" and put "bx rX" in that label?
    Yeah that's the best way when you can't just use a bl.

    The function is executed after that last line, correct? And does it automatically put the value stored in LR into PC at the end of the function?
    I don't understand the question, but basically LR = PC when bl is used. So actually when the called function returns via pop pc or bx lr or something, then you go to the line after that "bl".
     

    Touched

    Resident ASMAGICIAN
  • 625
    Posts
    9
    Years
    • Age 122
    • Seen Feb 1, 2018
    I don't understand the question, but basically LR = PC when bl is used. So actually when the called function returns via pop pc or bx lr or something, then you go to the line after that "bl".

    LR = PC + 4.

    Thank you very much! :)
    I'd just need some clarifications: everytime I call a function, I have to load it into rX register, then use "bl *label*" and put "bx rX" in that label? The function is executed after that last line, correct? And does it automatically put the value stored in LR into PC at the end of the function?

    It doesn't automatically do it. You have to do it. If you do BX LR, that sets PC = LR. pop {..., pc} also can return, assuming you've done a corresponding push {..., lr}. That uses the stack to transfer the value of LR to PC. There are a number of ways of doing it. See my tutorial for a more thorough explanation on this.

    About flags, they all can be accessed by 0xXXX then?
    Sorry for bothering.

    If you call that checkflag function, then yes. It's basically exactly the same as the script function except it can be called from ASM code.

    EDIT
    I found these lines in a code

    In this case, after executing the function it all ends because I haven't stored PC (or LR?) with bl?

    Not quite sure what you mean by "it all ends". This does exit the routine because it is branching away, but execution of engine code still continues.
    This is part of what we call a hook. It is a way of modifying an existing routine without being encumbered by space limitations. Basically we find a function and hijack execution so that the function branches to free space. We then use a special routine that performs the action we want and branch back. The part you linked looks like the part that branches back to the original function.
     
  • 87
    Posts
    8
    Years
    • Seen Feb 5, 2021
    Allright, thanks to you both! The first time I read your tutorials there were many things I couldn't understand but now I think they're clear.
    So then, if I use bl+bx, that's what happens: bl allows me to return to the next instruction of my code (after executing the function), while bx just branches to the function.
    If I use bl only, the function should not be "too far" or it may not work.
    If I use bx only, it exits my routine, executes the function, then keeps on executing the code just after the function (not the one I wrote I mean).
    In short, bl sets LR = PC + 4 but hasn't got unlimited range while bx has unlimited range but it doesn't set LR?
     

    Telinc1

    Weirdo Extraordinaire
  • 168
    Posts
    10
    Years
    I have here this routine for cmda6 which locks up the game when it runs. Yes, I've incremented the offset by 1. And yes, it's at an aligned location.
    Code:
    .text
    .align 2
    .thumb
    .global walkingscript
    
    main:
    push {r4,r5,lr}
    add sp, #-0x4
    lsl r0, r0, #0x18 @ getQueueItemByIndex
    lsr r0, r0, #0x18
    lsl r1, r0, #0x2
    add r1, r1, r0
    lsl r1, r1, #0x3
    ldr r0, exec_queue
    add r5, r1, r0 @ /getQueueItemByIndex
    mov r4, sp
    add r4, #0x2
    mov r0, sp
    add r1, r4, #0x0
    ldr r2, get_player_coords
    bl linker @ Store player coordinates at [r0] and [r1], respectively.
    mov r0, sp @ Start checking if player coordinates have changed.
    ldrh r2, [r0, #0x0]
    mov r3, #0x0
    ldsh r1, [r0, r3]
    mov r3, #0x2
    ldsh r0, [r5, r3]
    cmp r1, r0
    bne process
    mov r0, #0x0
    ldsh r1, [r4, r0]
    mov r3, #0x4
    ldsh r0, [r5, r3]
    cmp r1, r0
    beq return @ Fall through to process
    
    process:
    strh r2, [r5, #0x2]
    ldrh r0, [r4, #0x0]
    strh r0, [r5, #0x4]
    mov r0, sp
    mov r1, #0x0
    ldsh r0, [r0, r1]
    mov r2, #0x0
    ldsh r1, [r4, r2]
    ldr r2, get_background_byte
    bl linker @ r0: BG byte of tile the player's standing on; r4: sp + 2, pointer
    lsl r0, r0, #0x18
    lsr r0, r0, #0x18
    @ Skip bl to return; that's what cripples the routine
    lsl r0, r0, #0x18
    cmp r0, #0x0
    beq return @ Return if no background byte
    mov r0, sp
    mov r3, #0x0
    ldsh r0, [r0, r3] @ r0 = player x
    mov r2, #0x0
    ldsh r1, [r4, r2] @ r1 = player y
    ldr r2, get_tile_number
    bl linker @ r0 = tile number the player's on
    @ REGISTERS: see note 1
    ldr r1, tile_table
    mov r2, #0x0
    ldr r4, table_delimiter
    ldrh r4, [r4, #0x0]
    
    loop:
    ldrh r3, [r1, r2] @ Load the current value
    cmp r3, r4 @ Compare the current value to our delimiter.
    beq return @ And return if we finished reading the table.
    cmp r3, r0 @ Compare the current value to the tile number the player's on.
    bne next_entry @ Read the next entry if the two aren't equal.
    ldr r0, script
    ldr r2, script_executor
    bl linker
    
    return:
    add sp, #0x4
    pop {r4, r5}
    pop {r0}
    bx r0
    
    next_entry:
    add r2, r2, #0x2 @ Increase our table offset by 0x2.
    b loop @ Read the next entry.
    
    linker:
    bx r2
    
    .align 2
    exec_queue: .word 0x03005098
    get_player_coords: .word 0x0805C538
    get_background_byte: .word 0x08058F78
    get_tile_number: .word 0x08058E48
    script_executor: .word 0x08069AE4
    table_delimiter: .word 0x0000
    tile_table: .word 0x08AAAAAA
    script: .word 0x8BBBBBB
    
    @ Tile number table: [TILE NUMBER - 2 bytes] [END - 0000]
    @ note 1:
    @  - r0: tile number
    @  - r1: table location
    @  - r2: table offset
    @  - r3: table value
    @  - r4: delimiter
     

    Blah

    Free supporter
  • 1,924
    Posts
    11
    Years
    I have here this routine for cmda6 which locks up the game when it runs. Yes, I've incremented the offset by 1. And yes, it's at an aligned location.
    Code:
    .text
    .align 2
    .thumb
    .global walkingscript
    
    main:
    push {r4,r5,lr}
    add sp, #-0x4
    lsl r0, r0, #0x18 @ getQueueItemByIndex
    lsr r0, r0, #0x18
    lsl r1, r0, #0x2
    add r1, r1, r0
    lsl r1, r1, #0x3
    ldr r0, exec_queue
    add r5, r1, r0 @ /getQueueItemByIndex
    mov r4, sp
    add r4, #0x2
    mov r0, sp
    add r1, r4, #0x0
    ldr r2, get_player_coords
    bl linker @ Store player coordinates at [r0] and [r1], respectively.
    mov r0, sp @ Start checking if player coordinates have changed.
    ldrh r2, [r0, #0x0]
    mov r3, #0x0
    ldsh r1, [r0, r3]
    mov r3, #0x2
    ldsh r0, [r5, r3]
    cmp r1, r0
    bne process
    mov r0, #0x0
    ldsh r1, [r4, r0]
    mov r3, #0x4
    ldsh r0, [r5, r3]
    cmp r1, r0
    beq return @ Fall through to process
    
    process:
    strh r2, [r5, #0x2]
    ldrh r0, [r4, #0x0]
    strh r0, [r5, #0x4]
    mov r0, sp
    mov r1, #0x0
    ldsh r0, [r0, r1]
    mov r2, #0x0
    ldsh r1, [r4, r2]
    ldr r2, get_background_byte
    bl linker @ r0: BG byte of tile the player's standing on; r4: sp + 2, pointer
    lsl r0, r0, #0x18
    lsr r0, r0, #0x18
    @ Skip bl to return; that's what cripples the routine
    lsl r0, r0, #0x18
    cmp r0, #0x0
    beq return @ Return if no background byte
    mov r0, sp
    mov r3, #0x0
    ldsh r0, [r0, r3] @ r0 = player x
    mov r2, #0x0
    ldsh r1, [r4, r2] @ r1 = player y
    ldr r2, get_tile_number
    bl linker @ r0 = tile number the player's on
    @ REGISTERS: see note 1
    ldr r1, tile_table
    mov r2, #0x0
    ldr r4, table_delimiter
    ldrh r4, [r4, #0x0]
    
    loop:
    ldrh r3, [r1, r2] @ Load the current value
    cmp r3, r4 @ Compare the current value to our delimiter.
    beq return @ And return if we finished reading the table.
    cmp r3, r0 @ Compare the current value to the tile number the player's on.
    bne next_entry @ Read the next entry if the two aren't equal.
    ldr r0, script
    ldr r2, script_executor
    bl linker
    
    return:
    add sp, #0x4
    pop {r4, r5}
    pop {r0}
    bx r0
    
    next_entry:
    add r2, r2, #0x2 @ Increase our table offset by 0x2.
    b loop @ Read the next entry.
    
    linker:
    bx r2
    
    .align 2
    exec_queue: .word 0x03005098
    get_player_coords: .word 0x0805C538
    get_background_byte: .word 0x08058F78
    get_tile_number: .word 0x08058E48
    script_executor: .word 0x08069AE4
    table_delimiter: .word 0x0000
    tile_table: .word 0x08AAAAAA
    script: .word 0x8BBBBBB
    
    @ Tile number table: [TILE NUMBER - 2 bytes] [END - 0000]
    @ note 1:
    @  - r0: tile number
    @  - r1: table location
    @  - r2: table offset
    @  - r3: table value
    @  - r4: delimiter

    It would be nice if you could tell us what this routine is supposed to be doing, that way I don't have to decipher.
    Immediately, the first issue I see are these function pointers:

    Code:
    get_player_coords: .word 0x0805C538
    get_background_byte: .word 0x08058F78
    get_tile_number: .word 0x08058E48
    script_executor: .word 0x08069AE4

    You need to add +1 to the addresses listed here (obviously you don't need to for the table/script ones). There are a few efficiency issues here and there, but nothing which would impact the routine to cause a freeze/crash. I suggest adding +1 to those, and then stepping through a debugger if the issue persists.
     

    Telinc1

    Weirdo Extraordinaire
  • 168
    Posts
    10
    Years
    It would be nice if you could tell us what this routine is supposed to be doing, that way I don't have to decipher.
    Immediately, the first issue I see are these function pointers:

    Code:
    get_player_coords: .word 0x0805C538
    get_background_byte: .word 0x08058F78
    get_tile_number: .word 0x08058E48
    script_executor: .word 0x08069AE4
    You need to add +1 to the addresses listed here (obviously you don't need to for the table/script ones). There are a few efficiency issues here and there, but nothing which would impact the routine to cause a freeze/crash. I suggest adding +1 to those, and then stepping through a debugger if the issue persists.
    First of all, I'm a total idiot for those pointers. I'll just check them out and see if that works. As for efficiency, this is practically a carbon copy of one of the routines from the original game. The purpose is basically to check if you've moved since last frame and execute a script if you have and are standing on a tile, the number of which is present in a table.
    Edit: That totally worked and made me feel super dumb, especially considering I mentioned adding +1 in my original post. Now to figure out why searching through a 6-byte table takes 10 seconds, literally.
     
    Last edited:

    Blah

    Free supporter
  • 1,924
    Posts
    11
    Years
    First of all, I'm a total idiot for those pointers. I'll just check them out and see if that works. As for efficiency, this is practically a carbon copy of one of the routines from the original game. The purpose is basically to check if you've moved since last frame and execute a script if you have and are standing on a tile, the number of which is present in a table.
    Edit: That totally worked and made me feel super dumb, especially considering I mentioned adding +1 in my original post. Now to figure out why searching through a 6-byte table takes 10 seconds, literally.

    Oh, I see. This is not the way you should go about doing this. Make a hook for something that gets executed on a per step basis (increasing execution efficiency), either that or just make a task.
     
  • 87
    Posts
    8
    Years
    • Seen Feb 5, 2021
    GBATEK reports these informations:
    General Internal Memory
    00000000-00003FFF BIOS - System ROM (16 KBytes)
    00004000-01FFFFFF Not used
    02000000-0203FFFF WRAM - On-board Work RAM (256 KBytes) 2 Wait
    02040000-02FFFFFF Not used
    03000000-03007FFF WRAM - On-chip Work RAM (32 KBytes)
    03008000-03FFFFFF Not used
    04000000-040003FE I/O Registers
    04000400-04FFFFFF Not used
    Internal Display Memory
    05000000-050003FF BG/OBJ Palette RAM (1 Kbyte)
    05000400-05FFFFFF Not used
    06000000-06017FFF VRAM - Video RAM (96 KBytes)
    06018000-06FFFFFF Not used
    07000000-070003FF OAM - OBJ Attributes (1 Kbyte)
    07000400-07FFFFFF Not used
    External Memory (Game Pak)
    08000000-09FFFFFF Game Pak ROM/FlashROM (max 32MB) - Wait State 0
    0A000000-0BFFFFFF Game Pak ROM/FlashROM (max 32MB) - Wait State 1
    0C000000-0DFFFFFF Game Pak ROM/FlashROM (max 32MB) - Wait State 2
    0E000000-0E00FFFF Game Pak SRAM (max 64 KBytes) - 8bit Bus width
    0E010000-0FFFFFFF Not used
    Unused Memory Area
    10000000-FFFFFFFF Not used (upper 4bits of address bus unused)

    However, when I use an hex editor (such as HxD), which part of memory does it display?
    The ROM usually is 16 MB and all that memory is just contained in 08000000-08FFFFFF?
    If so, how can I hack Palette/Images or RAM?
     

    Telinc1

    Weirdo Extraordinaire
  • 168
    Posts
    10
    Years
    GBATEK reports these informations:
    -snip-
    However, when I use an hex editor (such as HxD), which part of memory does it display?
    The ROM usually is 16 MB and all that memory is just contained in 08000000-08FFFFFF?
    If so, how can I hack Palette/Images or RAM?
    The hex editor displays the 08000000-08FFFFFF and (if the game is 32 MB) the 09000000-09FFFFFF ranges (that is, the contents of the ROM - read only memory. It's called that because you can't change it at run time). The palettes and the images are contained in that memory and just get copied to the VRAM or wherever else during run time, so it's a matter of finding them in the hex editor and changing them. The RAM is well, random access memory. It gets wiped out when you turn off the emulator (actually, your PC has RAM too if you didn't know) because it's not persistent memory. RAM can only be "hacked" using ASM, or for that matter, cheat codes.
     
  • 87
    Posts
    8
    Years
    • Seen Feb 5, 2021
    The hex editor displays the 08000000-08FFFFFF and (if the game is 32 MB) the 09000000-09FFFFFF ranges (that is, the contents of the ROM - read only memory. It's called that because you can't change it at run time). The palettes and the images are contained in that memory and just get copied to the VRAM or wherever else during run time, so it's a matter of finding them in the hex editor and changing them. The RAM is well, random access memory. It gets wiped out when you turn off the emulator (actually, your PC has RAM too if you didn't know) because it's not persistent memory. RAM can only be "hacked" using ASM, or for that matter, cheat codes.
    Thank you very much! The image/palette's offset is just the one Unlz displays, right?
    And in this case, what will I find in the hex editor at the image's offset? How are those hexadecimals linked to the image?
     

    PurpleOrange

    still don't know what I'm doing
  • 367
    Posts
    10
    Years
    so i have this code made by touched in THIS thread
    Code:
    .text
    .align 2
    .thumb
    .thumb_func
    
    main:
        push {lr}
    
        ldr r0, =(0x02024284)
        mov r1, #0x27
        mov r2, #0x1F
        push {r2}
        mov r2, sp
        ldr r3, =(0x0804037C + 1)
        bl call_via_r3
        pop {r2}
        pop {pc}
    
    call_via_r3:
        bx r3

    this code changes the HP IV of a pokemon, however it only does it to the first pokemon in the player's party, how would i modify it for use in a givepokemon script so the given pokemon has the changed HP IV?
     
    Last edited:

    Blah

    Free supporter
  • 1,924
    Posts
    11
    Years
    so i have this code made by touched in THIS thread
    Code:
    .text
    .align 2
    .thumb
    .thumb_func
    
    main:
        push {lr}
    
        ldr r0, =(0x02024284)
        mov r1, #0x27
        mov r2, #0x1F
        push {r2}
        mov r2, sp
        ldr r3, =(0x0804037C + 1)
        bl call_via_r3
        pop {r2}
        pop {pc}
    
    call_via_r3:
        bx r3

    this code changes the HP IV of a pokemon, however it only does it to the first pokemon in the player's party, how would i modify it for use in a givepokemon script so the given pokemon has the changed HP IV?
    You should use a hook for givepokemon. This is designed for cases where the Pokemon is in your party. If you did givepokemon, it's possible the Pokemon would just go straight to the PC. So this part here:

    Code:
    ldr r0, =(0x02024284)
    Which is the address of the first Pokemon on your party, would be needing changes (but if it's sent to the PC, you can't do the traditional way of countpokemon * 64 + address).

    So to reiterate, you should place a hook at the createpokemon routine, and there you can make a routine that works like this, but with your own switch (var or flag) so it would only happen when you wanted it to happen.

    If the Pokemon is 100% going to be in the Player's party (first Pokemon or something), you can use the routine you've quoted and just adjust

    Code:
    ldr r0, =(0x02024284)

    like I've stated earlier in the post~
     

    Lance32497

    LanceKoijer of Pokemon_Addicts
  • 792
    Posts
    9
    Years
    Hello, I'm trying to make a routine that adds a value on a certain stat but I couldn't make it work, it doesn't freeze the game but it doesn nothing, I know it has errors but I don't know what to edit, here's what I made.
    Spoiler:
     

    PurpleOrange

    still don't know what I'm doing
  • 367
    Posts
    10
    Years
    many stuffs

    that's simpler than i thought, thank you

    okay, so i should make 5 routines (one for each extra slot), and change
    Code:
    ldr r0, =(0x02024284)
    accordingly , then use countpokemon in xse to work out which of the 5 routines it needs to use, is that right?

    also on a side note, this part here
    Code:
    mov r2, #0x1F
    how would i make the value bigger than 0xFF, for things like trainer ID and nickname etc. i do not know the ways of the asm
     

    Blah

    Free supporter
  • 1,924
    Posts
    11
    Years
    Hello, I'm trying to make a routine that adds a value on a certain stat but I couldn't make it work, it doesn't freeze the game but it doesn nothing, I know it has errors but I don't know what to edit, here's what I made.
    Spoiler:

    What? I don't understand what the distinction between var 0x8004 and 0x8005 are in your routine. For current HP, the routine is simply:

    Code:
    	ldr r0, location_of_stat_for_first_pokemon
    	ldr r1, =(0x20370B8)
    	ldrh r1, [r1]
    	mov r2, #0x64
    	mul r1, r1, r2
    	add r0, r0, r1 @addr of the stat
    
    	ldr r1, =(0x20370C0)
    	ldrh r1, [r1]
    	strh r1, [r0]
     

    Blah

    Free supporter
  • 1,924
    Posts
    11
    Years
    that's simpler than i thought, thank you

    okay, so i should make 5 routines (one for each extra slot), and change
    Code:
    ldr r0, =(0x02024284)
    accordingly , then use countpokemon in xse to work out which of the 5 routines it needs to use, is that right?

    No, you should just use some math. Recall, countpokemon stores the amount of Pokemon in the lastresult (I forget if it's 0-5 or 1-6). From there, you just add to 0x2024284, 0x64 * amount of Pokemon. Lastresult is 0x20370D0 btw.

    also on a side note, this part here
    Code:
    mov r2, #0x1F
    how would i make the value bigger than 0xFF, for things like trainer ID and nickname etc. i do not know the ways of the asm

    You can't using mov. What you can do is use ldr instead of move for things that are 32 bits (so a nickname won't fit). But set_attr (the function Touched calls in that routine) takes a pointer to the value to set, rather than an actual value (so you don't need to make r2 something other than a pointer. Touched uses the stack, which is probably what confused you. You could use a ROM address. Like this:

    Code:
        ldr r0, =(0x02024284)
        mov r1, #0x2
       ldr r2, =(address to nick name)
        ldr r3, =(0x0804037C + 1)
        bl linker
     

    Lance32497

    LanceKoijer of Pokemon_Addicts
  • 792
    Posts
    9
    Years
    What? I don't understand what the distinction between var 0x8004 and 0x8005 are in your routine. For current HP, the routine is simply:

    Code:
    	ldr r0, location_of_stat_for_first_pokemon
    	ldr r1, =(0x20370B8)
    	ldrh r1, [r1]
    	mov r2, #0x64
    	mul r1, r1, r2
    	add r0, r0, r1 @addr of the stat
    
    	ldr r1, =(0x20370C0)
    	ldrh r1, [r1]
    	strh r1, [r0]

    Var 8004 dictates the slot number of a pokemon in your party, var 8005 dictates what Stat to modify, 0 being the current HP, 1 being the Total HP, 2 being the ATK , 3 being the DEF and so on, while var 8006 dictates what value to add, that's what I'm trying to do. Sorry for my mistakes hehe I'm so new in ASM, all I can do is compile and insert it.
     

    Blah

    Free supporter
  • 1,924
    Posts
    11
    Years
    Var 8004 dictates the slot number of a pokemon in your party, var 8005 dictates what Stat to modify, 0 being the current HP, 1 being the Total HP, 2 being the ATK , 3 being the DEF and so on, while var 8006 dictates what value to add, that's what I'm trying to do. Sorry for my mistakes hehe I'm so new in ASM, all I can do is compile and insert it.

    That's fine, do you know how to modify the routine I posted to incorporate var 0x8005?
     
  • 417
    Posts
    9
    Years
    • Seen Nov 20, 2016
    Var 8004 dictates the slot number of a pokemon in your party, var 8005 dictates what Stat to modify, 0 being the current HP, 1 being the Total HP, 2 being the ATK , 3 being the DEF and so on, while var 8006 dictates what value to add, that's what I'm trying to do. Sorry for my mistakes hehe I'm so new in ASM, all I can do is compile and insert it.
    Stats don't work that way. The only stats outside of the encrypted data section are within the 20 bytes specific only to pokemon in parties. They are calculated based the species' base stat, iv, ev, nature, etc. You cannot simply write a change to them because that change is reversed anytime the stat needs to be recalculated.
     

    Lance32497

    LanceKoijer of Pokemon_Addicts
  • 792
    Posts
    9
    Years
    That's fine, do you know how to modify the routine I posted to incorporate var 0x8005?

    Actually I have an idea, I'll try to insert var 8005 there, and if I really give up, I willask for a help. Thanks for the response

    Stats don't work that way. The only stats outside of the encrypted data section are within the 20 bytes specific only to pokemon in parties. They are calculated based the species' base stat, iv, ev, nature, etc. You cannot simply write a change to them because that change is reversed anytime the stat needs to be recalculated.

    I don't get it. I can manually edit the stats of a pokemon in VBA
     
    Status
    Not open for further replies.
    Back
    Top