• 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
  • I think the simplest way would be like this:
    Code:
    mov r0, numerator
    mov r1, denominator
    mov r2, #100
    mul r0, r2
    lsr r2, r1, #1
    add r0, r2
    swi #6
    What it does is it multiplies the numerator by 100 so the result is a percentage, then adds half of the denominator to make sure the result is correctly rounded (no need to process the remainder), and then divides. I haven't tested this code, but I'm pretty sure it works.

    In a case like:
    Numerator = 35
    Denomenator = 102
    -
    you'd do, swi(3551, 102)
    which is 34, and the modulo return is 83.

    The expected return would be 35. I don't think you can get away with not checking the remainder. You could multiply by 1000 at the start, then you can shed the last digit if it's less than 5, but that's not faster, since swi does the modulo itself.
     
    76
    Posts
    9
    Years
  • In a case like:
    Numerator = 35
    Denomenator = 102
    -
    you'd do, swi(3551, 102)
    which is 34, and the modulo return is 83.

    The expected return would be 35. I don't think you can get away with not checking the remainder. You could multiply by 1000 at the start, then you can shed the last digit if it's less than 5, but that's not faster, since swi does the modulo itself.
    When I divide 35 by 102, the result is roughly 0.343137, so I'd say 34 would be right here. The idea behind adding half of the denominator before dividing is that you essentially increase the quotient by 0.5, and then the decimal places get cut off. So if the digit after the point is 5 or higher, you'll reach a new integer, thus it rounds correctly.

    You can also do it the way you suggested, but I'd multiply by 100 at the start and add 1 in case you need to round up. Also, you need to compare the full remainder with half of the denominator, not half of the remainder.
     

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • When I divide 35 by 102, the result is roughly 0.343137, so I'd say 34 would be right here. The idea behind adding half of the denominator before dividing is that you essentially increase the quotient by 0.5, and then the decimal places get cut off. So if the digit after the point is 5 or higher, you'll reach a new integer, thus it rounds correctly.

    You can also do it the way you suggested, but I'd multiply by 100 at the start and add 1 in case you need to round up. Also, you need to compare the full remainder with half of the denominator, not half of the remainder.
    Oh, that's quite nice. I hadn't thought of doing things like that, very nicely done. My only suggestion is that you use div instead of SWI if you're not using the modulo div component (pretty sure it's faster).
     
    417
    Posts
    9
    Years
    • Seen Nov 20, 2016
    Thanks for the help everyone! I think the lesson of today is that I have poor intuition when it comes to simple math V_V I thought the computation would have been significantly more complicated than either of the provided routines...
     

    Joexv

    ManMadeOfGouda joexv.github.io
    1,037
    Posts
    11
    Years
  • Yeah, you don't need to do all of that. Just uncompress the Pokemon, and copy it into the opponent's party space. What I've been trying to say is that the opponent's party is purged before battling, so inorder for your storage pokemon which you've copied over to not be purged, you'd need to hook to turn off the purging mechanic. :)

    OK I sorta kinda feel stupid, I was miss understanding why you were telling me to hook. Anyways I hooked rewrote the routine to copy the data from the players current party, and simply calling the swap party storage routine at the beginning and end of the routine(to avoid having to recalc stats) which ended up working, BUT the enemy's HP, level and such were seemingly cleared no matter what I did. So, I rewrote it again, this time just straight up taking the 0x50 bytes that the pokemon are stored as, recalced the stats,(while they're in the opponents party, which is probably why it doesnt work). Now this still "worked" in a sense, but the game started endlessly recreating the trainers party, which ended up a black screen.

    Heres the current form of the routine:
    (Fbi I hooked into the same place that you did for your frontier party gen routine)
    Spoiler:
     

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • Heres the current form of the routine:
    (Fbi I hooked into the same place that you did for your frontier party gen routine)
    Spoiler:

    Do you happen to have the hook location's address? The code doesn't make it very obvious, unfortunately.


    Code:
    .text
    .align 2
    .thumb
    .thumb_func
    
    Main:
    	push {r0-r6}
    	[B]ldr r0, =(0x205) @doing this is perfectly fine too[/B]
    	ldr r3, =(0x806E6D0 +1)
    	bl linker
    	cmp r0, #0x0
    	beq end
    	ldr r5, =(0x2024284) @change this to storage location if u wish
    	mov r6, #0x0
    
    Loop:
    	cmp r6, #0x6
    	beq end
    	mov r0, r5
    	mov r1, #0x64 @size, compressed should be 0x50. Party-swap I made will uncompress them when swapping though.
    	mul r1, r1, r2
    	add r0, r0, r1 @pokemon to copy
    	mov r4, r0
    	ldr r3, =(0x803E47C +1)
    	bl linker
    	ldr r4, =(0x202402C) 
    	add r1, r1, r4 @place to put copied mon
    	@ here you should call memcpy, check IDA for offsets
    	add r6, r6, #0x1
    	b loop
    
    end:
    	@end, return to hooked section, and restore overwritten instructions
    
    linker:
    	bx r3
    .align 2

    A few big errors, I saw were "bl loop". You should just use "b". Another thing was loading byte by byte, that's no good. Consider if the opponent had 6 Pokemon, that's 600 iterations in your loop, slow stuff.
     

    Joexv

    ManMadeOfGouda joexv.github.io
    1,037
    Posts
    11
    Years
  • Do you happen to have the hook location's address? The code doesn't make it very obvious, unfortunately.


    Spoiler:

    A few big errors, I saw were "bl loop". You should just use "b". Another thing was loading byte by byte, that's no good. Consider if the opponent had 6 Pokemon, that's 600 iterations in your loop, slow stuff.
    Hook location is 0x112F8,
    Heres the changed routine via your suggestions:
    Code:
    .text
    .align 2
    .thumb
    .thumb_func
    
    Main:
    	push {r0-r6}
    ldr r0, =(0x205) @doing this is perfectly fine too
    	ldr r3, =(0x806E6D0 +1)
    	bl linker
    	cmp r0, #0x0
    	beq end
    	ldr r5, =(0x203C000) @changed to storage
    	mov r6, #0x0
    
    Loop:
    	cmp r6, #0x6
    	beq end
    	mov r0, r5
    	mov r1, #0x50 @changed to compensate for the storage
    	mul r1, r1, r2
    	add r0, r0, r1 @pokemon to copy
    	mov r4, r0
    	ldr r3, =(0x803E47C +1)
    	bl linker
    	ldr r4, =(0x202402C) 
    	add r1, r1, r4 @place to put copied mon
    	ldr r3, =(0x81e5e78 +1) @memcpy as you requested
    	bl linker
    	add r6, r6, #0x1
    	b loop
    
    end: @ending stuff from hook
    	pop {r0-r6}
    	ldr r0, =(0x400)
    	cmp r1, r0
    	bne place
    	mov r0, #0x0
    	ldr r3, =(0x80116AC +1)
    	bx r3
    	
    place:
    	ldr r3, =(0x8011304 +1)
    	bx r3
    linker:
    	bx r3
    .align 2

    And still black screen
     
    417
    Posts
    9
    Years
    • Seen Nov 20, 2016
    Commented.

    Code:
    .text
    .align 2
    .thumb
    .thumb_func
    
    Main:
    	push {r0-r6}
    ldr r0, =(0x205) @doing this is perfectly fine too
    	ldr r3, =(0x806E6D0 +1) @flag check
    	bl linker
    	cmp r0, #0x0
    	beq end
    	ldr r5, =(0x203C000) @changed to storage
    	mov r6, #0x0
    
    Loop:
    	cmp r6, #0x6
    	beq end
    	mov r0, r5 @storage offset
    	mov r1, #0x50 @[b]changed to compensate for the storage[/b]
    	mul r1, r1, r2 @[b]what is r2? is this not meant to be mul r1, r1, r6?[/b]
    	add r0, r0, r1 @pokemon to copy
    	mov r4, r0
    	ldr r3, =(0x803E47C +1) @calc effective stats
    @[b]won't this overwrite the first 20 bytes of the next pokemon?[/b]
    	bl linker
    	ldr r4, =(0x202402C) 
    	add r1, r1, r4 @place to put copied mon
    @[b]r1 will NOT be the same as it was before bl linker[/b]
    	ldr r3, =(0x81e5e78 +1) @memcpy as you requested
    	bl linker
    	add r6, r6, #0x1
    	b loop
    
    end: @ending stuff from hook
    	pop {r0-r6}
    	ldr r0, =(0x400)
    	cmp r1, r0
    	bne place
    	mov r0, #0x0
    	ldr r3, =(0x80116AC +1)
    	bx r3
    	
    place:
    	ldr r3, =(0x8011304 +1)
    	bx r3 @[b]this instruction is unnecessary[/b]
    linker:
    	bx r3
    .align 2
     

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • Aw, some of this was my fault. I copied his code, and changed to a higher register to save the data, but things like mul I forgot to change back to the high register. Also if you did Party swap Joexv, you don't actually need to recalculate the stats, since I'm 95% sure it is recalculated there. Sorry for the confusion folks :D

    Something to add to what Azurile has said, is "b loop" and "Loop:" depending on the compiler this is case sensitive.
     

    Blah

    Free supporter
    1,924
    Posts
    11
    Years
  • Aw, some of this was my fault. I copied his code, and changed to a higher register to save the data, but things like mul I forgot to change back to the high register. Also if you did Party swap Joexv, you don't actually need to recalculate the stats, since I'm 95% sure it is recalculated there. Sorry for the confusion folks :D

    Something to add to what Azurile has said, is "b loop" and "Loop:" depending on the compiler this is case sensitive.
     

    Joexv

    ManMadeOfGouda joexv.github.io
    1,037
    Posts
    11
    Years
  • Aw, some of this was my fault. I copied his code, and changed to a higher register to save the data, but things like mul I forgot to change back to the high register. Also if you did Party swap Joexv, you don't actually need to recalculate the stats, since I'm 95% sure it is recalculated there. Sorry for the confusion folks :D

    Something to add to what Azurile has said, is "b loop" and "Loop:" depending on the compiler this is case sensitive.

    Yea no worries I fixed them, but still nothing. One thing though, is that even is I just take a party pokemon(all stats and everything) it will zero it out, calling the stat recalc, does indeed bring new stats to the pokemon, but it causes a black screen. Which I feel like the original method I was using to try and create the party, by decrypting and all, is by far the easiest way to do this without massive amounts of changes to the existing trainer battle routines.(or just lots of hooks). So I'm gonna rewrite the initial code(again), and see what happens this time.

    EDIT: Ok I rewrote the original routine, but it fails to store any pokemon past the first slot of the enemy trainer, it doesnt store moves. Seriously wtf am I doing wrong????
    Spoiler:
     
    Last edited:
    417
    Posts
    9
    Years
    • Seen Nov 20, 2016
    Yea no worries I fixed them, but still nothing. One thing though, is that even is I just take a party pokemon(all stats and everything) it will zero it out, calling the stat recalc, does indeed bring new stats to the pokemon, but it causes a black screen. Which I feel like the original method I was using to try and create the party, by decrypting and all, is by far the easiest way to do this without massive amounts of changes to the existing trainer battle routines.(or just lots of hooks). So I'm gonna rewrite the initial code(again), and see what happens this time.

    EDIT: Ok I rewrote the original routine, but it fails to store any pokemon past the first slot of the enemy trainer, it doesnt store moves. Seriously wtf am I doing wrong????
    Code:
    Push {r0-r7, lr}
    .....
    pop {r0-r7}
    Also I'm not sure whether or not you've completely scrapped it, but in your previous code where I mentioned that r1 will not be the same as it was prior to the bl? I should have also said the same for r0, Furthermore, I believe memcpy requires an argument in r2 of the size you're copying.

    It may be a good idea to test using vba-sdl-h. I use it when I can't get routines to work. It can get difficult to stare at your routines for hours and the answer is often as facepalm-y as a misaligned stack.
     

    Joexv

    ManMadeOfGouda joexv.github.io
    1,037
    Posts
    11
    Years
  • Code:
    Push {r0-r7, lr}
    .....
    pop {r0-r7}
    Also I'm not sure whether or not you've completely scrapped it, but in your previous code where I mentioned that r1 will not be the same as it was prior to the bl? I should have also said the same for r0, Furthermore, I believe memcpy requires an argument in r2 of the size you're copying.

    It may be a good idea to test using vba-sdl-h. I use it when I can't get routines to work. It can get difficult to stare at your routines for hours and the answer is often as facepalm-y as a misaligned stack.
    Well while not as simple as a misaligned stack, a lot of the issues I was having was caused by the RAM offset I was using. Swapped to 0x0203E000 and now the only issue I have is routine based and probably wont be too hard to fix.
    Thanks for the help!
     
    77
    Posts
    9
    Years
    • Seen Dec 5, 2015
    Quick question.
    Touched's ASM Tutorial said:
    If we have not pushed LR, we can return with the following code,

    bx lr

    I'm having problems finding the definition of return as used in this context. Anyone able to clarify?
     

    Touched

    Resident ASMAGICIAN
    625
    Posts
    9
    Years
    • Age 122
    • Seen Feb 1, 2018
    Quick question.
    I'm having problems finding the definition of return as used in this context. Anyone able to clarify?

    It's the same as a return statement. When you call a subroutine, the value of PC gets set to the location of called subroutine. This gets executed, but when it reaches the end, it needs to start executing the calling subroutine where it left off. This place would be known as the return location. The verb "return" means handing control back to the calling subroutine. When using the BL instruction, LR (the return address) is set, allowing the subroutine to return by setting PC (the address to execute) to that value somehow. This can take the form of BX LR (if LR was unchanged in the course of the routine), MOV PC, LR (if the calling routine uses the same instruction set as the caller), POP {PC} (if the return address was pushed to the stack), etc.
     

    destinedjagold

    You can contact me in PC's discord server...
    8,593
    Posts
    16
    Years
    • Seen Dec 23, 2023
    This is for a Ruby ROM... And this is part of the ASM code...
    Code:
    LDR R0, = 0x0202E8C4
    LDRH R0, [R0]
    CMP R0, #1
    BNE FINE
    I wanna ask a question about it. Where in this code is it using variable 0x8000 and how can I change it to use variable 0x4015 instead? Thanks~

    Inside the spoiler is the entire ASM code, created by Andrea~

    Spoiler:
     
    417
    Posts
    9
    Years
    • Seen Nov 20, 2016
    This is for a Ruby ROM... And this is part of the ASM code...
    Code:
    LDR R0, = 0x0202E8C4
    LDRH R0, [R0]
    CMP R0, #1
    BNE FINE
    I wanna ask a question about it. Where in this code is it using variable 0x8000 and how can I change it to use variable 0x4015 instead? Thanks~
    No clue on the ram map of ruby (not even the rom map, tbh). But assuming that code is correct, the only logical place would be the 0x0202E8C4. I think that because ruby doesn't use DMA***, you can simply access the variable address directly. Do you have a ram map for ruby? You literally just replace that address with the offset for var 0x4015.

    ***citation needed
     
    87
    Posts
    8
    Years
    • Seen Feb 5, 2021
    Hi, I recently started learning ASM and I've got several questions on the basis and on some already written codes. Thank you in advance for your answers.
    1) Push and Pop are used not to overwrite the current value of registers, right? So when I'm pushing a register it's like I'm doing a backup of its content while when I'm popping I'm taking back that content from the stack. Since the stack is a LIFO structure, why do we still write this?
    push {r0-r2, lr}
    [...]
    pop{r0-r2, pc}
    Does the machine automatically read pop's bracket in reverse order? (So it first pops PC, whose content becomes that of LR, then r2, r1, r0?)

    2) I read register r0-r3 don't need to be pushed, is it correct? And why? Is it because they don't contain previous data?

    3) I don't understand what "bl" and "bx" commands actually do. "Bl" is a branch with link, so it jumps to a label but first saves in LR the current address? And what about bx? What does the register after bx mean?
    I found this code in FBI's tutorial:
    main:
    push {r0-r2, lr}
    @derive 0x828 in register r0
    mov r0, #0xFF
    lsl r0, r0, #0x3
    add r0, r0, #0x30
    @this is how we call the function
    ldr r1, =(0x806E6D0 +1)
    bl linker
    @check if set or not
    cmp r0, #0x1
    beq set
    pop {r0-r2, pc}

    set:
    @if we get here it's set

    linker:
    bx r1

    It loads in r1 the offset of a function, then it uses bl/bx commands. What is it doing here and why?

    4)Variables are stored in certain offsets but what about flags? In FBI's tutorial this code is written:
    main:
    push {r0-r1, lr}

    @set r0 and r1 to some values
    mov r0, #0xFF
    mov r1, #0x8

    @multiply r0, and r1 (0xFF * 0x8) = 0x7F8
    mul r0, r0, r1

    @add 0x30 to 0x7F8 to get 0x828
    add r0, r0, #0x30
    pop {r0-r1, pc}

    So differently from variables, we just use the number of the flag? Is it because flag 0x828 actually is at 0x08000828 offset?

    I'm still learning but these are my questions this far (well I guess there are already too many of them)

    Again, thank you!
     

    Touched

    Resident ASMAGICIAN
    625
    Posts
    9
    Years
    • Age 122
    • Seen Feb 1, 2018
    Hi, I recently started learning ASM and I've got several questions on the basis and on some already written codes. Thank you in advance for your answers.
    1) Push and Pop are used not to overwrite the current value of registers, right? So when I'm pushing a register it's like I'm doing a backup of its content while when I'm popping I'm taking back that content from the stack. Since the stack is a LIFO structure, why do we still write this?

    Does the machine automatically read pop's bracket in reverse order? (So it first pops PC, whose content becomes that of LR, then r2, r1, r0?)

    The syntax is for convenience. Push places stuff on the stack in descending order (ordered by register number). pop takes it off in ascending order. This has the effect of making the registers stored in numerical order with the smallest register occupying the lowest address (remember the stack is descending, with smaller addresses indicating a larger stack). [Source]

    2) I read register r0-r3 don't need to be pushed, is it correct? And why? Is it because they don't contain previous data?

    r0-r3 are scratch registers. This is defined in the ARM calling convention. Essentially when calling a function, you just assume they're going to be messed up. Since all the code in a given program makes the same assumption, it is safe to edit them without restoring them to their previous values when your function returns.

    3) I don't understand what "bl" and "bx" commands actually do. "Bl" is a branch with link, so it jumps to a label but first saves in LR the current address? And what about bx? What does the register after bx mean?
    I found this code in FBI's tutorial:

    It loads in r1 the offset of a function, then it uses bl/bx commands. What is it doing here and why?

    I explained BL to someone else recently . This might help you since they had similar questions. TL;DR BL sets the Link Register and is used to call functions. BX branches to the address held in the register operand. It is used to switch between ARM and THUMB modes, but it has other uses such as calling functions that you don't know the address of (from a table of functions or something) and bypassing branch distance restrictions in BL.

    4)Variables are stored in certain offsets but what about flags? In FBI's tutorial this code is written:

    So differently from variables, we just use the number of the flag? Is it because flag 0x828 actually is at 0x08000828 offset?

    I'm still learning but these are my questions this far (well I guess there are already too many of them)

    Again, thank you!

    Some variables (0x800X) are stored at specific RAM offsets. The others are stored in a block of memory that overlaps with flags. If you wish to get the address of a variable such as 0x4000, or access a flag, you should use the engine functions to do this. They are the same functions used by setvar, addvar, checkflag, setflag, etc.

    The code that FBI provides there is something that calls a function that checks whether a flag is set. It is the same function that is used by the checkflag script command. This function takes a flag index in r0 and puts (returns) a 1 in r0 if the flag is set, otherwise it returns 0. This function will determine the address of the flag for you and perform the relevant calculations.

    The flag is not at address 0x08000828. This would be a ROM address so that wouldn't make sense anyway as flags can change (they'd need to be in the RAM).
     
    87
    Posts
    8
    Years
    • Seen Feb 5, 2021
    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
    ldr r1, =(0x806D600 +1)
    bx r1

    In this case, after executing the function it all ends because I haven't stored PC (or LR?) with bl?
     
    Status
    Not open for further replies.
    Back
    Top