by yohann imported to wiki by felixwright Quick Links: [[ https://metroidconstruction.com/resource.php?id=90 | Metroid Advance Game Editor (MAGE) ]]\\ [[ general:guides:hxd_guide | HxD Hex Editing Guide ]]\\ [[ https://code.visualstudio.com/ | Visual Studio Code ]]\\ [[ https://marketplace.visualstudio.com/items?itemName=ajarmar.armips | ARMIPS Extension for VS Code ]]\\ [[ https://problemkaputt.de/no$gba.zip | No$GBA Debugger ]]\\ [[ https://github.com/Kingcom/armips/releases | ARMIPS Releases (GitHub) ]]\\ [[ https://labk.org/maps/ | GBA Metroid Data Maps ]]\\ ---- ====== THUMB Assembly Tutorial ====== \\ \\ ===== Introduction ===== This document aims to teach how THUMB works, how to set up an environment to test your code and ultimately how to read/write THUMB code. It is assumed you have some required knowledge about programming (variable types, hexadecimal, pointers and code fundamentals), if you are not familiar with these concepts I highly suggest you learn them before starting as they are extremely important to properly understand assembly. \\ \\ ---- ===== Setup ===== First things first, we’ll need to setup an environment to test and run our code, for that we will need six things : * An IDE, I’ll use [[ https://code.visualstudio.com/ | VS Code ]] with the [[ https://marketplace.visualstudio.com/items?itemName=ajarmar.armips | ARMIPS Extension, ]] feel free to use whatever you feel the most comfortable with. * [[ https://github.com/Kingcom/armips/releases | ARMIPS, ]] this will handle assembling our ASM code to binary code and inserting it into the ROM. * [[ general:guides:hxd_guide | A Hex Editor, ]] this will be good for finding freespace and it’s a useful thing to have around. * [[ https://problemkaputt.de/no$gba.zip | A Debugger, ]] aka an emulator, I highly recommend using no$gba because it’s a very good debugger. * A vanilla USA ROM of Metroid Zero Mission. * [[https://metroidconstruction.com/resource.php?id=90 | MAGE (Metroid Advance Game Editor), ]] since we’ll use Metroid Zero Mission, it’s going to be useful to have a way to edit the game, spawn wherever we want…etc. I recommend you make a dedicated folder to all those thing and then in it have no$gba, MAGE, and a folder containing armips and the ROM. \\ \\ ---- ===== First Look At Code ===== Here’s the simplest patch I ever wrote in ASM, this probably looks a little scary but don’t worry, everything will soon make sense. Even if you never saw ASM, you can slightly guess what some of those instructions do, like add and sub for example. However you may not be familiar with those r0 and r1 and the other instructions.     push r14     ldr r0,=#0x300006e     ldrh r1,[r0,#0x0]     sub r1,#0x3     strh r1,[r0,#0x0]     ldr r0,=#0x30000E4     ldrh r1,[r0,#0x0]     add r1,#0x3     strh r1,[r0,#0x0]     pop r0     bx r0     .pool \\ \\ ---- ===== The Basics ===== ==== Registers ==== A register is essentially a little memory block in the CPU used to store data, in our case the GBA CPU has 37 registers of 32 bit in size each. I won’t explain in detail what all of them do here, you can check the technical documentation of no$gba or gbatek is you want, I’ll only explain what we need to know to write ASM. We only have 16 registers that interests us, from r0 to r15, I like to divide them in 4 categories : ^ Name ^ Description ^ | Volatile/parameter registers [r0-r3] | Those 4 registers are used to store parameters for functions and they also are considered overwritten when returning from a function, in other words they are sort of like local variables. | | Work registers [r4-r7] | Those 4 registers don’t have any particular function, so they can be used in any way you want, however they are saved even when returning from a function, meaning that you can use them without fear of losing them unlike [r0-r3]. | | Storage registers [r8-r12] | Those are similar to the work registers (saved and don’t have any particular function), however due to how instructions are assembled to machine code, most instructions are incompatible with them since the number of the register concerned by the instructions is written on 3 bit meaning it can’t go higher than 7. | | Special registers [r13-r15] | Those registers hold special values and also have an alternative name, r13 or sp is the stack pointer, r14 or lr is the link register and r15 or pc is the program counter. I’ll explain what those are later. | If you couldn’t guess already, ASM doesn’t have any concept of variables and data type, we just put values on registers or the stack and do stuff with them. Now let’s talk about those special registers : ^ Name ^ Description ^ | r13/sp | This is the stack pointer, it contains a pointer to the top of the stack, I will explain what the stack is in the next chapter but for now, all you need to know is that the stack is a special block of memory that works in a LIFO (Last In First Out) way where we can essentially push and pop data from it. | | r14/lr | This is the link register, when calling a function, r15 will be saved to lr, which will allow the called function to know where it was called so that it can go back to that value when the function ended, basically acting as a return. | | r15/pc | This is the program counter, it holds the ROM address of the current instruction executed. | If all of that sounded complicated to you, just try to see registers as global variables. When writing code I’ll often re-explain what this and that do so if you understand better when you see a concrete example don’t worry. ==== Stack ==== I’ve been talking about the stack multiple times now, but what is it ? The stack is a chunk of RAM that, as explained earlier, works in a LIFO manner (Last In First Out). LIFO specifically refers to the push and pop instructions, when pushing an element to the stack, you will put it on top of every other data that was previously pushed, and when poping a value from the stack, you will retrieve the last value that was pushed, aka Last In First Out. The stack has 3 main purposes for us : * Preserving work and storage registers : When using those registers in a function, it’s important to push them at the beginning and to pop them at the end, this will essentially preserve the value that was in them and then restore it to prevent conflicts, this is the “saving” I was referring to earlier.\\ * Preserving the return value : This is similar to the previous one but kind of a particular case, as mentioned earlier, r14 will contain the value of r15 when doing a function call, this means that it will contain the return value, it’s very important to keep it safe if we start to do nested call, since otherwise it’ll be overwritten and the game will basically jump nowhere and everywhere and crash.\\ * Function parameters : The parameter registers [r0-r3] are used to store parameters for functions, however some functions have more than 4 parameters, in that case the rest of them will be stored in the stack, this is notified as sp[0], sp[4]…\\ The stack may seem complicated for now, but most of the time we will just push and pop what we need and that’s it so it’s pretty simple. ==== Banks ==== Banks refer to how data is structured, this can be split like this : ^ Name ^ Description ^ | 0000000-0003FFF | BIOS, System ROM | | 2000000-203FFFF | WRAM or Work RAM (Fast RAM) | | 3000000-3007FFF | IRAM or Internal RAM (Normal RAM) | | 4000000-40003FE | IO, Input Output (hardware registers) | | 5000000-50003FF | PAL, Palette Memory | | 6000000-6017FFF | VRAM, or Video RAM | | 7000000-70003FF | OAM or Object Attribute Memory (data for sprites) | | 8000000-8FFFFFF | ROM, data and code | We will mainly work with IRAM and ROM, but it’s good to know how it works overall. ==== Flags and Conditions ==== In order to do conditions, we will use the flags of the CPU to determine where to jump and cmp instructions to compare value. Here are all the flags : ^ Flag ^ Name ^ Description ^ | Z | The Zero flag | Z = 1 if equal and 0 if not equal | | C | The Carry flag | C = 1 if unsigned higher or same and 0 if unsigned lower | | N | The Negative flag | N = 1 if signed negative and 0 if signed positive or zero | | V | The oVerflow flag | V = 1 if signed overflow and 0 if signed no overflow | We can also combine the flags together to do some other checks, for example C = 1 and Z = 1 means unsigned lower or same. Strictly speaking, we will never really work directly with those flags, but we will use jump instructions that will execute only if certain flags are set, for example beq (Branch EQual) or bne (Branch Not Equal). \\ \\ ---- ===== The First Program ===== Now that you know all the basics, you are ready to create your first program in ASM, create a new file with the ASM extension (I will name it Test.asm) in the folder with the ROM and armips and open the file in your IDE. ==== ARMIPS Directives ==== We will start by giving ARMIPS some information about our code : .gba .open "ZM_U.gba","TestASM.gba",0x8000000 .close First we tell it we are working with a GBA ROM, then we precise the input ROM (ZM_U.gba), the output ROM (TestASM.gba) and where we are starting (0x8000000 to start in ROM). We also close everything at the end. Now we should decide what we’re going to do, to stay simple we’re going to re-create the code I used as an example earlier, it hijacks the AI of an unused enemy and makes it so the room effect goes up automatically. ==== Labels ==== To make things easier, we will define labels that will hold constants, this is especially useful for IRAM and ROM addresses, here we’ll need 2 IRAM addresses, 2 ROM addresses and one constant, the Effect Y Position Offset (''0x300006e''), the BG0Position (''0x30000e4''), the address to our freespace (''0x8760d50''), the Sprite AI Pointers (''0x875E8C0'') and our constant which is going to be the Sprite ID of the sprite we will hijack, in our case it’s ''0x6c'' : .definelabel SpriteID,0x6c .definelabel SpriteAIPointers,0x875E8C0 .definelabel FreeSpace,0x8760d50 .definelabel EffectYPositionOffset,0x300006e .definelabel BG0YPosition,0x30000E4 Sprite AI Pointers is an array of pointers to all the main AI of the sprites, this is where we’ll put a pointer to our freespace. Freespace is unused data in the ROM where we can safely put our code, open the ROM in HxD and go to to our freespace address (without the ''8'' at the beginning because HxD doesn’t work with banks), you will see a lot of ''FF'', this is what freespace usually looks like, repeated data. ==== Hijack ==== We will start by doing our hijack, for that we will do simple maths : .org SpriteAIPointers + SpriteID * 4     .dw FreeSpace+1 ''.org'' tells ARMIPS to go insert code at the location provided after, in our case it’s going to be ''SpriteAIPointers + SpriteID * 4'' ; we start at ''SpriteAIPointers'' and then we add our ''SpriteID'' multiplied by 4 to offset the position to the pointer to the AI of our sprite. The multiplication by 4 is needed because a pointer is 32 bits long or 4 bytes long, so the first pointer is at offset 0, then the second at offset 4, then 8… The ''.dw'' tells ARMIPS to generate a pointer to the address provided, in our case the freespace + 1. You may be wondering why we added 1, adding one to a function start offset tells the CPU to switch to THUMB mode, since our code will be written in THUMB we need to switch modes otherwise it’ll go back to ARM mode and the game will crash. ==== Actual Code ==== Now we have everything ready to start writing code, start by adding a .org to our freespace and by pushing r14 to the stack .org FreeSpace     push r14 In our case pushing r14 isn’t necessary since we don’t call any other function before returning, but I’m doing it because it needs to be a reflex for you when writing code. Next, we’re going to decrement the Effect Y Position Offset (the origin is at the top-left of the screen, since we want it to go up we are decrementing it). To start, we load the IRAM address to r0 by using ''ldr'' (LoaD Register), the syntax is like this : ldr r0,=EffectYPositionOffset Now ''r0'' contains the value we put in our label, so ''0x300006e'', we then need to actually load the value that the address points to, for that we use ''ldrh'' (LoaD Register with Halfword). Halfword means the data we are loading is 16 bit sized or 2 bytes, if we wanted to load an 8 bit/1 byte value we would use ''ldrb'' and if we wanted to load a 32 bit/4 bytes value we would just use ''ldr''. Syntax goes like this : ldrh r1,[r0,#0x0] Here we are loading into ''r1'', the halfword contained in ''r0'' at offset ''0''. In C this would translate to : r0 = 0x300006e; ushort r1 = *(ushort*)r0[0]; In other words, we are just loading the 2 bytes value that ''r0'' points to in ''r1'', so now ''r1'' contains the actual IRAM value of the Effect Y Position Offset. Let’s decrement our value now : sub r1,#0x3 This is fairly simple, sub just subtracts the given register with the given value so this is the same as doing r1 -= 3. All we need to do is store our new value to IRAM now, so instead of doing a load, we are going to do a ''strh'' (Store Register Halfword) : strh r1,[r0,#0x0] We are storing the halfword value of r1 to r0 at offset 0. As you can see it’s very similar to the ldrh we did earlier. And that’s it, we did EffectYPositionOffset -= 3; in only 4 lines of code ! Now let’s do the same thing but with the BG0 Y Position, the code is exactly the same except for 2 small differences, instead of subtracting we are going to add and instead of loading Effect Y Position Offset, we are going to load BG0 Y Position, try to do it yourself. If your code looks like this : ldr r0,=BG0YPosition ldrh r1,[r0,#0x0] add r1,#0x3 strh r1,[r0,#0x0] You’ve successfully done it, if not try to understand what you did wrong and why. The only thing we need to do now is to return, for that we’re going to pop the value we previously pushed in the stack and jump to that : pop r0 bx r0 Here I’m popping the last value of the stack (remember we previously pushed r14 in it) and putting it in r0, then I’m branching to it with the bx instruction. Last very important thing to do is to add .pool at the very end of our code (right after the bx r0). We need a ''.pool'' because of the way instructions are assembled, in THUMB each instruction (except bl) is 16 bits long, but IRAM addresses are 32 bits long, so how can our ldr works ? Well instead of directly loading the address, we will create a pointer to that said address right after the code, and instead load the value of that pointer in our ldr, meaning that now the address loaded can fit in the 16 bites instruction. All this process is handled by the ''.pool'' directive, it’ll automatically create a pointer and replace our ''ldr''. ==== Assembling & Testing ==== Now that our code is ready, we need to assemble it using ARMIPS, open the command line tool in the folder where everything is and simply type : ''armips.exe Test.asm'' Press enter and if everything works, no message should appear but you should have a new ROM named ''TestASM.gba'' in your folder. If it didn’t work, try and debug it yourself, you very likely did an error in the code or didn’t renamed your vanilla ROM to ''ZM_U.gba''. In case you need it, here’s the entire source code : .gba .open "ZM_U.gba","TestASM.gba",0x8000000 .definelabel SpriteID,0x6c .definelabel SpriteAIPointers,0x875E8C0 .definelabel FreeSpace,0x8760d50 .definelabel EffectYPositionOffset,0x300006e .definelabel BG0YPosition,0x30000E4 .org SpriteAIPointers + SpriteID * 4     .dw FreeSpace+1 .org FreeSpace     push r14     ldr r0,=EffectYPositionOffset     ldrh r1,[r0,#0x0]     sub r1,#0x3     strh r1,[r0,#0x0]     ldr r0,=BG0YPosition     ldrh r1,[r0,#0x0]     add r1,#0x3     strh r1,[r0,#0x0]     pop r0     bx r0     .pool     .close If it still doesn’t work, come ask for help in the MAGConst discord server, here’s an invite link if needed. Now let’s test our code, open the new ROM (''TestASM.gba'') in MAGE and navigate to the fifth room in Kraid. Open the Spriteset Editor (Editor->Spriteset Editor), remove all the sprites using the “Remove slot” button, add a sprite using the “Add slot” button and choose the sprite number 6c in the combo box, it should have graphics that spell “Gekitai machine”. Click on apply to save your changes and close the Spriteset Editor. Enter Object editing mode by clicking on the red ball in the tool bar on the left, and remove all the sprites of the room by right clicking on the green squares and selecting “Remove sprite”, now add a new sprite (position doesn’t matter). Right click and select “Test Room Here”, if everything worked correctly, you should see the lava going up automatically. Congratulations, you just created your first ASM patch ! \\ \\ ---- ===== The Second Program (Ice Bomb) ===== Now that you know the basics on how to write an ASM patch, let’s try something more difficult. First of all, I highly recommend you have the data maps of Zero Mission open (here) as well as my function map (here), they will be very useful. This time we’re going to make something much more complicated, an ice bomb. For that we will need to : * Hijack the bomb exploding on sprite subroutine\\ * Check the weaknesses of the sprite\\ * If it can be freezed, we freeze it\\ * Prevent conflicts with vanilla code\\ ==== Setup ==== Let’s begin by creating the file, I’ll name it “TestIceBomb.asm” and setting up ARMIPS directories. Then we need some labels, first the freespace, I’ll be using the same as last time so 0x8760d50. This time we won’t be needing any IRAM addresses, only ROM addresses for some functions. Go to the code data map and search for “Get Sprite Weakness”, “Set Particle Effect”, “Projectile Hit Solid Sprite”, “Projectile Deal Damage” and “BX r0”. Go to the function map and search for “FreezeSprite”. Setup labels for all these functions, don’t forget to offset it to ROM. In the end, you should have something like this : .gba .open "ZM_U.gba","TestIceBomb.gba",0x8000000 .definelabel FreeSpace,0x8760d50 .definelabel GetSpriteWeakness,0x8050370 .definelabel FreezeSprite,0x80506fc .definelabel SetParticleEffect,0x80540EC .definelabel ProjectileHitSolidSprite,0x80504cc .definelabel ProjectileDealDamage,0x8050424 .definelabel BXR0,0x808abf8 .close ==== Breakpoints & Small Edit ==== We’ll start by doing a super small edit, changing the particle effect of a bomb exploding. To do that, we need to know where this is done, we know that it’s a particle effect so we need to know when Set Particle Effect is called. This means we’ll have to set a breakpoint on the function, grab the start offset of the function (same as the definelabel) and start a test room with MAGE. Click on the code window of no$gba and press ctrl+B, paste the offset here and hit enter, now try to lay a bomb, the breakpoint should hit and no$gba will set the code window to be on focus. Now check the value in r14, press ctrl+G and enter that value, this will bring you to where the function was called, scroll up a little and you should see something that looks like “movs r2,13h”, that’s the instruction we are interested in since the value in r2 is the effect number, copy the ROM offset of the instruction (0x80521d8) and you can close no$gba and go back to your IDE. Let’s put a .org with the address we got previously and change that mov instruction, the way mov works is pretty simple, it just sets the register with the value, in other words mov r0,#0x2 <=> r0 = 2. We want to change that value to have a more “icy” effect, so let’s replace it with 0x28 .org 0x80521d8     mov r2,#0x28 0x28 is the particle effect for freezing a sprite, if you want to test the patch to see what the effect looks like you can do it right now. ==== Hijack ==== Now let’s do the real code, we’re starting with the hijack, it’s going to be different than last time since we want to inject code in the middle of a function. The best optimal spot for us is at 0x8050b40, it’s right in the middle of the Bomb Hit Sprite function, after the checks to see if the sprite is immune or solid to projectiles, but before the weakness checks which is what we want to change. Regarding the hijack, we’ll do it by loading the value of our freespace to r0 and then call BXR0, BXR0 is a function that takes a pointer to a function as a parameter and calls that function, so let’s use it : .org 0x8050b40     ldr r0,=FreeSpace+1     bl BXR0     .pool Let’s not forget to add 1 to switch to THUMB mode, bl means Branch Link, it saves r15 to r14 and jumps to the given function, it’s basically a function call. ==== Freezing the Sprite ==== Let’s dive into the main code now, first we’ll push r14 and then we’ll copy the vanilla code where we made our hijack, so : .org FreeSpace     push r14     add r0,r4,#0x0 The add instruction is this context is the equivalent to r0 = r4, it moves the value in r4 to r0, r4 previously contained a Sprite Data Pointer that needs to be moved to r0 so it can be used as a parameter for the function we’re going to call next, Get Sprite Weakness. You would think we can just do bl GetSpriteWeakness, but if you try to compile that it won’t work. This is similar to the problem with .pool, GetSpriteWeakness is too far from where we are now because bl doesn’t jump to a specific location, it jumps by a number of bytes. And we can’t use BXR0 like earlier either since it’s too far as well. So to actually call the function, we’re just going to move the offset in r15, it’s as simple as that. However, for the function to be able to return we need to use a bl to do that, so : ldr r1,=GetSpriteWeakness bl @@CallR1 @@CallR1: mov r15,r1 So here, we load our function in r1 because r0 holds our parameter, we bl to a label, and that label moves r1 to r15, when GetSpriteWeakness will return, it’ll jump back to right after the bl where the rest of our code will be. Get Sprite Weakness returns the weakness of the sprite we passed in r0, so it has a return value, those values will always be stored in r0. Now we need to check for the weaknesses of the sprite, weaknesses are flags, you can see them in the ROM data map, in the sprite stats. First we’ll check if it can be frozen (flag 0x40) and then we’ll check if it’s vulnerable to beam and bombs (flag 0x2). mov r1,#0x40 and r1,r0 First we move 0x40 to r1, and then we do a bitwise and (&) on r1 and r0, now if the sprite can be frozen, r1 will hold 1, and if it can’t, r1 will hold 0. From here we can just check the value and adapt to the result, in other words we need an if statement. To do that we need to compare the value in r1 with 0 so we’ll use the cmp instruction. cmp r1,#0x0 beq @@WeaknessCheck The cmp instruction will set the Z flag, and then we can use the beq instruction (Branch EQual) to jump only if r1 was equal to 0. We’ll keep the WeaknessCheck label for later and assume that r1 will always be 1 for now. We need to call Freeze Sprite now, it takes 2 parameters, a Sprite Data Pointer in r0 and a Freeze Timer in r1, for r0 we’ll do like earlier by getting it from r4 since it’s still here, and for r1 we’ll use 0xF0 (same value as vanilla game). Then after that we’ll call the function in the same way we did but with r2 instead of r1. This all produces the following code : add r0,r4,#0x0 mov r1,#0xF0 ldr r2,=FreezeSprite bl @@CallR2 ... @@CallR2: mov r15,r2 Put the CallR2 label right after CallR1 one to organize the code properly. For now the entire code looks like this : .org 0x8050b40     ldr r0,=FreeSpace+1     bl BXR0     .pool .org FreeSpace     push r14     add r0,r4,#0x0     ldr r1,=GetSpriteWeakness     bl @@CallR1     mov r1,#0x40     and r1,r0     cmp r1,#0x0     beq @@WeaknessCheck     add r0,r4,#0x0     mov r1,#0xF0 ; Freeze timer (EDITABLE)     ldr r2,=FreezeSprite     bl @@CallR2 @@WeaknessCheck: @@CallR2:     mov r15,r2 @@CallR1:     mov r15,r1     .pool I also added a .pool because of the ldr of the functions. Last step for freezing the sprite, adding a particle effect to make it look good. We’re going to do this right after the CallR2 but before the WeaknessCheck label. Set Particle Effect takes 3 parameters, r0 has Y Position, r1 has X Position and r2 has the effect number. For the position, let’s use the position of the sprite, our Sprite Data Pointer is still in r0 so let’s load the X and Y position. Go to the RAM data map and search for “Current Sprite”, as you can see Y and X positions are halfwords, respectively stored at offset 2 and 4, so we’re gonna need some ldrh. ldrh r0,[r4,#0x2] ldrh r1,[r4,#0x4] mov r2,#0x28 We load into r0 the Y position by offsetting with 2, and we load into r1 the X position by offsetting with 4. We also move into r2 0x28, it’s the same effect we used earlier. Now let’s just call Set Particle Effect with r3 ldr r3,=SetParticleEffect bl @@CallR3 ... @@CallR3:     mov r15,r3 Now our sprite is getting freezed if a bomb is hitting it and the can be frozen weakness flag is set. ==== Damaging the Sprite ==== Now we need to check if the sprite is vulnerable to bombs, so we’re going to do the same as before. But we lost our Sprite Weakness because it was in r0, and calling Get Sprite Weakness again would be inefficient, so instead let’s go back a little bit and save our sprite weakness to r5 : add r0,r4,#0x0 ldr r1,=GetSpriteWeakness bl @@CallR1 add r5,r0,#0x0 Now let’s go back to where we were and put back r5 into r0 @@WeaknessCheck: add r0,r5,#0x0 Let’s do a bitwise and (&) with 2 and compare : mov r1,#0x2 and r1,r0 cmp r1,#0x0 beq @@HitSolid We’ll need to get our Sprite Data Pointer back for the next function calls, so let’s do that right before the cmp with : add r0,r4,#0x0 Let’s assume our sprite is weak for now, we’ll need to call Projectile Deal Damage and set the parameters. r0 already contains our Sprite Data Pointer, and we need to set a damage value in r1, vanilla damage for a bomb is 0x8 so let’s use that, and then call our function with r2 : mov r1,#0x8 ldr r2,=ProjectileDealDamage bl @@CallR2 Now let’s handle if it’s not vulnerable, since it only has one parameter (Sprite Data Pointer in r0 and it’s already set) we just need to call it ldr r1,=ProjectileHitSolidSprite bl @@CallR1 This should look like this now : add r0,r5,#0x0 mov r1,#0x2 and r1,r0 add r0,r4,#0x0 cmp r1,#0x0 beq @@HitSolid mov r1,#0x8 ; Damage (EDITABLE) ldr r2,=ProjectileDealDamage bl @@CallR2 @@HitSolid: ldr r1,=ProjectileHitSolidSprite bl @@CallR1 ==== The Return ==== Our patch is almost complete, all that’s left to do is to handle the return. The return is just stack stuff, we pop what we pushed at the start and then bx. Regarding the push at the start, we also need to push r4 and r5 since we used them in our code and they are work registers, so replace our initial push with : push r4, r5, r14 And then at the end pop everything and return : @@Return: pop r4, r5 pop r0 bx r0 We also need to jump to the return label after call Projectile Deal Damage so that Projectile Hit Solid Sprite isn’t called, to do that just use a single branch instruction : ldr r2,=ProjectileDealDamage bl @@CallR2 b @@Return ==== Conflicts ==== Sorry I lied earlier, this wasn’t the last step, but this one really is. When we’re going to return from our code, we’ll end up in the middle of the vanilla function, which will cause conflicts. To prevent that, we’ll change when the vanilla function returns, cutting a part of its code : .org 0x8050b4c     pop r4     pop r0     bx r0 This is the same as the return code of the vanilla function, it’s slightly after our return because we need to preserve some instructions otherwise weird stuff will happen. ==== Assembling & Testing ==== Second patch done ! Assemble it and test it, you don’t need to do anything in MAGE this time other than launching a test room, if something went wrong at any point, try to debug it yourself before checking the source code (set a breakpoint at the beginning of the freespace and step through the instructions with F7). .gba .open "ZM_U.gba","TestIceBomb.gba",0x8000000 .definelabel FreeSpace,0x8760d50 .definelabel GetSpriteWeakness,0x8050370 .definelabel FreezeSprite,0x80506fc .definelabel SetParticleEffect,0x80540EC .definelabel ProjectileHitSolidSprite,0x80504cc .definelabel ProjectileDealDamage,0x8050424 .definelabel BXR0,0x808abf8 .org 0x80521d8     mov r2,#0x28 ; Change bomb exploding particle effect .org 0x8050b4c ; Make the return of the vanilla function earlier     pop r4     pop r0     bx r0     .org 0x8050b40     ldr r0,=FreeSpace+1 ; Hijack     bl BXR0 ; BXR0 calls the function in r0     .pool .org FreeSpace     push r4, r5, r14 ; Push r4 and r5 because we use them and r14 for the return     add r0,r4,#0x0 ; Put the sprite data pointer from vanilla code to r0     ldr r1,=GetSpriteWeakness     bl @@CallR1 ; Call Get Sprite Weakness     add r5,r0,#0x0 ; Save the weakness in r5 for later use     mov r1,#0x40 ; Can be frozen flag     and r1,r0 ; & operation with the weakness     cmp r1,#0x0 ; Check if can be frozen     beq @@WeaknessCheck ; Branch if equal -> Can't be frozen     add r0,r4,#0x0 ; Get sprite data pointer back (again)     mov r1,#0xF0 ; Freeze timer     ldr r2,=FreezeSprite     bl @@CallR2 ; Call Freeze Sprite     ldrh r0,[r4,#0x2] ; Get Y Position of the sprite     ldrh r1,[r4,#0x4] ; Get X Position of the sprite     mov r2,#0x28 ; Freezing sprite particle effect     ldr r3,=SetParticleEffect     bl @@CallR3 ; Call Set Particle Effect @@WeaknessCheck:     add r0,r5,#0x0 ; Get the weakness back     mov r1,#0x2 ; Vulnerable to beam/bombs flag     and r1,r0 ; & operation with the weakness     add r0,r4,#0x0 ; Get sprite data pointer back (again(again))     cmp r1,#0x0 ; Check if vulnerable     beq @@HitSolid     mov r1,#0x8 ; Damage     ldr r2,=ProjectileDealDamage     bl @@CallR2 ; Call Projectile Deal Damage     b @@Return ; Branch to return @@HitSolid:     ldr r1,=ProjectileHitSolidSprite     bl @@CallR1 ; Call Projectile Hit Solid Sprite @@Return:     pop r4, r5 ; Pop r4 and r5 because we pushed them earlier     pop r0 ; Pop the r14 value into r0     bx r0 ; Return @@CallR3:     mov r15,r3 @@CallR2:     mov r15,r2 @@CallR1:     mov r15,r1     .pool .close ----