User Tools

Site Tools


zero_mission:intermediate_guides:yohannasm

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
zero_mission:intermediate_guides:yohannasm [2024/03/23 19:10] felixwrightzero_mission:intermediate_guides:yohannasm [2024/03/23 19:39] (current) felixwright
Line 5: Line 5:
 Quick Links: Quick Links:
  
-[[ general:guides:hxd_guide                           | HxD Hex Editing Guide              ]]\\ + 
-[[ https://metroidconstruction.com/resource.php?id=90 Metroid Advance Game Editor (MAGE) ]]\\ +[[ https://metroidconstruction.com/resource.php?id=90                 | Metroid Advance Game Editor (MAGE) ]]\\ 
-[[ https://problemkaputt.de/no$gba.zip                | No$GBA Debugger                    ]]\\ +[[ general:guides:hxd_guide                                           | HxD Hex Editing Guide              ]]\\ 
-[[ https://github.com/Kingcom/armips/releases         | ARMIPS Releases (GitHub)           ]]\\ +[[ https://code.visualstudio.com/                                     | Visual Studio Code                 ]]\\  
-[[ https://labk.org/maps/                             | GBA Metroid Data Maps              ]]\\ +[[ 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              ]]\\ 
  
 <WRAP center 75% leftalign> <WRAP center 75% leftalign>
Line 36: Line 39:
  
 First things first, we’ll need to setup an environment to test and run our code, for that we will need six things : 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 VS Code with the ARMIPS extension, feel free to use whatever you feel the most comfortable with. +    * 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. 
-    * ARMIPS, this will handle assembling our ASM code to binary code and inserting it into the ROM, [[ https://github.com/Kingcom/armips/releases it’s available here. ]] + 
-    * [[ general:guides:hxd_guide | A Hex Editor ]]this will be good for finding freespace and it’s a useful thing to have around. +    * [[ https://github.com/Kingcom/armips/releases ARMIPS, ]] this will handle assembling our ASM code to binary code and inserting it into the ROM. 
-    * A debugger, aka an emulator, I highly recommend using no$gba because it’s a very good debugger, [[ https://problemkaputt.de/no$gba.zip | grab it here. ]]+ 
 +    * [[ 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 | 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.     * A vanilla USA ROM of Metroid Zero Mission.
-    * 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… [[https://metroidconstruction.com/resource.php?id=90 | Grab it here]].+ 
 +    [[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. 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.
Line 114: Line 122:
  
 The stack has 3 main purposes for us : 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. +    * 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.\\   
-    * 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]…+ 
 +    * 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. 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.
  
Line 183: Line 195:
  
  
-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 :+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'' :
  
 <code arm> <code arm>
Line 197: Line 209:
 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. 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.+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 ==== ==== Hijack ====
Line 209: Line 221:
 </code> </code>
  
-.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…+''.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.+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 ==== ==== Actual Code ====
Line 226: Line 238:
  
 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). 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 :+To start, we load the IRAM address to r0 by using ''ldr'' (LoaD Register), the syntax is like this :
  
 <code arm> <code arm>
Line 232: Line 244:
 </code> </code>
  
-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 :+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 :
  
 <code arm> <code arm>
Line 238: Line 250:
 </code> </code>
  
-Here we are loading into r1, the halfword contained in r0 at offset 0. In C this would translate to :+Here we are loading into ''r1'', the halfword contained in ''r0'' at offset ''0''. In C this would translate to :
  
-<code arm>+<code c>
 r0 = 0x300006e; r0 = 0x300006e;
 ushort r1 = *(ushort*)r0[0]; ushort r1 = *(ushort*)r0[0];
 </code> </code>
  
-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.+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 : Let’s decrement our value now :
Line 255: Line 267:
 This is fairly simple, sub just subtracts the given register with the given value so this is the same as doing r1 -= 3. 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 store :+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) :
  
 <code arm> <code arm>
Line 288: Line 300:
  
 Last very important thing to do is to add .pool at the very end of our code (right after the bx r0). 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.+ 
 +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 ==== ==== Assembling & Testing ====
Line 294: Line 307:
  
 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 :  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. +''armips.exe Test.asm'' 
-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 :+ 
 +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 :
  
 <code arm test.asm> <code arm test.asm>
Line 333: Line 347:
 If it still doesn’t work, come ask for help in the MAGConst discord server, here’s an invite link if needed. 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.+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 ! Congratulations, you just created your first ASM patch !
Line 351: Line 365:
  
 For that we will need to : For that we will need to :
-    * Hijack the bomb exploding on sprite subroutine +    * Hijack the bomb exploding on sprite subroutine\\ 
-    * Check the weaknesses of the sprite + 
-    * If it can be freezed, we freeze it +    * Check the weaknesses of the sprite\\ 
-    * Prevent conflicts with vanilla code+ 
 +    * If it can be freezed, we freeze it\\ 
 + 
 +    * Prevent conflicts with vanilla code\\
  
 ==== Setup ==== ==== Setup ====
zero_mission/intermediate_guides/yohannasm.1711221029.txt.gz · Last modified: 2024/03/23 19:10 by felixwright