[GBA] - I need help to understand how to use branch with link

Started by Whipon, January 28, 2021, 08:14:21 PM

Previous topic - Next topic



I'm trying to do a simple ASM hack to the GBA game Castlevania - Harmony of Dissonance.

I want to be able to change weapons each time I use a potion, high potion or elixir.

My first approach was to just replace some of the code involved in using these items:

(NOTE: Opcodes are reversed, as they are stored that way in the ROM).


8242 2ED2 0125 D888 1018 0880 => A07B 0130 0728 00D1 0120 A073

7BA0 LDRB R0, [R4 + #0e]
3001 ADD R0, 01
2807 CMP R0, 07
D100 BNE 000001a6 (00)
2001 MOV R0, 01
73A0 STRB R0, [R4 + #0e]

I did the above hack several years ago and worked very well. This time I wanted to keep the original code and jump to a sub routine instead.

I found lots of free space at the offset 080DADA4.

So I wanted to insert a BL at offset 0802974E

I had to modify the opcode since it made No$GBA to stop because the one I used with VBA was read as invalid. I don't understand why.

0A88 E08A => B1F0 29FB (F0B1 FB29 BL 080DADA4) (No$GBA) - B1F0 29DB (F0B1 DB29) (VBA)


7BA0 LDRB R0, [R4 + #0e]
3001 ADD R0, 01
2807 CMP R0, 07
D100 BNE 000001a6 (00)
2001 MOV R0, 01
73A0 STRB R0, [R4 + #0e]
0A88    LDRH R2, [R1, #000] (Replaced instruction at 0802974E)
E08A    LDRH R0, [R4, #016] (Replaced instruction at 08029750)
7047    BX R14 (Return to 8029752)

The problem I'm facing is that the jump never happens. It just continues execution at next instruction (8029752).

I tried looking for help in several documents I found in the web, with no luck. I can't figure what I'm doing wrong.

Also I searched in several trace text files, in hope to find if I'm missing some opcodes, but it seems that's not the issue.

I wanted to learn how to jump and return from subroutines to improve my GBA hacking skills.

I'll really appreciate if somebody can lend me a hand.

Thanks in advance ;).



Interesting hack. Assuming it is not a lack of controls forcing you to double up then gun game/weapon master a like for Castlevania could be amusing.

Anyway so you found no fluff in the potion subroutine you can optimise to give space to also tickle the equipped weapon value while it is doing its thing. You also reckon you don't want to do something like hook a potion count/health value to change at the same time (or within a vblank/hblank's gap). Instead hook the potion routine to jump (branch tending to be a preferred term in GBA world), fix whatever you overwrote for the jump, do your bit, and return just after where it left off to continue on with life.

For the sake of others playing along at home branch with link is what C coding would put in place before calling a function and returning later, why also calling a function within a function is not a great look as you then rely on the hardware (good luck there) or compiler to make sure everything gets back to where it needs to be. You would also want to make sure you are in the right mode (the GBA's ARM7 has two instruction sets it flicks between to either optimise for speed, size or difficulty of operation
"080DADA4" is a location in RAM/memory bus, but as the whole cart is visible in said memory at once if you subtract 08000000 from that value you get the location in the cart (some will tell you to ignore the 08 at the start, and that works for the vast majority of games which are under 16 megabytes and thus don't use the higher location. Technically there are further locations where the cart is visible and you might have a pointer/memory location listed somewhere, these are rare though).
Though scrolling down a bit on http://problemkaputt.de/gbatek.htm#arminstructionsummary to get to the jumps section might be clearer

  Instruction                  Cycles  Flags  Expl.
  B{cond}   label              2S+1N    ----  PC=$+8+/-32M
  BL{cond}  label              2S+1N    ----  PC=$+8+/-32M, LR=$+4

That is to say the B(ranch) instruction takes 2S+1N cycles ( http://problemkaputt.de/gbatek.htm#armcpuinstructioncycletimes ) to execute and causes the program counter (the register R15) to change to current location (what $ stands for)plus 8 +/- the value held (it is a maths based one with a signed value rather than absolute location for this instruction, for ).
BL (branch with link) does the same but also sets the link register (the register R14) to current location plus 4, ostensibly so you can say go to the location the link register has later on and be at the next instruction when you land (as opposed to landing back on the jump in a nice potential infinite loop which is what would have happened if the +4 had not been there)

Anyway does the unmodified game execute something at 0802974E during normal operations? As you presumably picked it for what looked like a good reason then should be easy enough to set a break on execute (or maybe just read) for that location and using a potion to have it hopefully pop up and say this location saw something was executed (and in what mode in case you need to change that). No point in pondering the implications of code, possibly even chasing down really esoteric potential flaws, if it turns out by some quirk that was not an actively used instruction; the very thing you are trying to do with a jump and a return could be happening here but from the original devs.
Hopefully you are not already within a subroutine (using a potion is usually a good candidate as you are not always going to be using them enough to want to keep things active here) and possibly then nailed the return location value of the game's original return (the stack not really having anything special for return values) when the BL stuffed its own value in R14 as part of its operation. If it is within a subroutine already then you get to restore its return location or use blind jumps and take care of the locations yourself (easy enough to do if you are playing hacker and presumably not remaking the game from scratch, or have a spare register to function as an alternative to R14 for these purposes and use BX instead, or maybe are willing to trust push and pop with R14).

no$gba vs vba. Not entirely sure what goes there. no$gba is generally seen as more accurate so if you messed up some kind of timing, location or read ahead then that could have been it. Will have to drill into that one and possibly some ARM docs.
Only got http://shell-storm.org/online/Online-Assembler-and-Disassembler/ to play with right now for a disassembler and am too lazy to play hand encoding (the no$gba specs have the option for it though). There is also the question of does the no$gba command work in vba?

0A88 E08A =>
B1F0 29FB (F0B1 FB29 BL 080DADA4) (No$GBA)
B1F0 29DB (F0B1 DB29) (VBA)

according to said link.

(No$GBA):  B1 F0 29 FB    bl #0xb1656
(VBA)   :  B1 F0 29 DB    bl #0x8b165a


Thank you very much, FAST6191, that was a wonderful explanation ;).

It turns out that the opcode I got through trial and error in VBA was indeed invalid.
The correct one was the one I found through NO$GBA.

Correct Opcode:

0802974E 0A88 E08A => B1F0 29FB (F0B1 FB29 BL 080DADA4)

I tested the above opcode in VBA and NO$GBA and it works perfectly now, HP restore also works.

I don't know why it didn't work before.

I wonder if there's an easier method to get the proper opcodes for the BL Thumb instruction.
I have a list of Thumb hex opcodes, but I had to figure out the last two bytes of the instruction.
(Edited those bytes until I got the desired address).

Have a nice weekend  :).


Woah, have you been entering hex codes by hand to get the asm?

You can use an assembler like armips or xkas-plus to convert your asm into hex. Makes it a lot easier and they take care of addresses for you.


Thanks a lot, Phonymike.

I'll give it a try in the next hack ;).