GBAFE Assembly for Dummies, by Dummies

Project 1: Changing Mounted Aid

Alright. You’ve had a crash course in the assembly opcodes, and a basic idea of what each part of no$gba does. Next thing we’re going to do is take that knowledge and apply it by making a small hack.
The idea behind this guide was to not only explain the basics of assembly hacking, but also to show how to I would tackle a problem. Whether that works for you remains to be determined, but hopefully this part will help you develop a plan of your own.

Project 1

Currently, mounted aid is as follows:
Male: 25 - Con
Female: 20 - Con
I never liked this. I have decided to change it to the following:
Unpromoted mounted: 20 - Con
Promoted mounted: 25 - Con

To do this, we need to find where the game checks whether a unit is female or not, and modify it so it checks if the unit is promoted instead. To do that, we need to figure out how a unit is determined to be female. Fortunately, we know that: if you look at your Class and Character Editors folder in your Nightmare modules (I told you they’re a good reference!), you’ll see a file called Ability 2.txt. And if you open that, you’ll notice line 66 says 0x40 Female. This means the 6th bit in this byte-length bitfield (2^6 = 0x40, we start counting at 0) is set if the unit is female.
Given this knowledge, a logical course of action is to set a break on read on a particular unit’s character or class abilities (it doesn’t matter which, and you’ll see why later) and then see where it checks for this particular bit. However, we’re not going to do that, and again, you’ll see why later.

Another possibility is to set a break on read to a particular unit’s Con bonus, because we know that Aid depends on Con, and thus Con will have to be calculated at some point. In order to find out where that’s located, we shall refer to my notes, affectionately dubbed the Teq Doq. Under Character and Battle Struct, you should find this:

This means that given a unit’s data struct pointer, the 0x1A’th byte will be the constitution bonus. So how do we find a unit’s data struct pointer? There’s a pointer to the current unit’s data struct at 3004E50. Remember this address.

Given this knowledge, we’re going to select Seth with the A button, and then go to that offset:

If you reverse those bytes (remember, little endian), you get 202BE4C. That is where Seth’s unit data currently resides, and from before, we know that 202BE4C + 1A should be his constitution bonus. Let’s confirm that’s actually the case:

which should take you here:

Seems the constitution bonus is 0, which is as expected, since Seth hasn’t had an opportunity to use any Body Rings yet. But we can also verify by changing the value, and checking whether it changes in the game. If I change the byte to 01…

…we verify that we have the correct byte.
Now that we’ve done that, let’s set a break on read to that byte. Press ctrl + B and enter the following:

Notice that we can let no$gba do the math for us! Very handy. Hit Enter or press OK, and the breakpoint should show up in the breakpoint viewer. Now we have to perform an action that makes the game read the con bonus byte, so go ahead and pull up the stat screen (if it’s already up, exit out/change characters and return), and you should have a pause here:

Recall that a break on read will pause after the command that did the break, which means the ldsb r0,[r1,r0] at 87332 is what caused the halt.
Now we have to figure out what exactly is going on.
Given that r0 = 0x1A from 87330, we can deduce that r1 has the unit data pointer, since [r0 + r1] = con bonus. A glance at the register list proves that r1 = 202BE4C.
87334 adds r0 and r3. Given that r0 is the con bonus, I’m going to assume that r3 has either the character constitution value, the class constitution value, or the sum of the two. If you look up a bit, you’ll find that the last option is the correct one. So r0 will have Seth’s total constitution.
Now I’m going to keep an eye on what happens to r0. At some point, I’m expecting to have the value subtracted from 0x19 (25d) to calculate Aid. 87336 shows that r0 gets copied to the stack (str r0,[sp]), and then r0 is immediately overwritten.
At 87348, we have a function call: bl 80870BC. So far, nothing has been done with the constitution value still sitting on the stack. It’s possible that the Aid calculation will be performed in this new function, but I get the feeling that’s unlikely.
What I’m going to do is make a note somewhere that we had a break at 87334, and then continue seeing if there are any more break on reads to 202BE4C+1A. If there are not, then we know that this is the code we’re looking for, somehow, and we’ll look further into the function at 870BC.

To resume play, we’re going to click back in the game window. And whatdya know, there’s another break, this time at 189FA:

Well, this looks promising. The code looks similar to the previous break, which makes sense, since we’re calculating constitution again, although the registers are switched around. More importantly, the next line is mov r0, #0x19, which is the number we’re interested in! Let’s follow that unconditional branch at 189FE. Simply select it, and then press the right arrow key on your keyboard. You should be taken to 18A12:

r0 = r0 - r1, where r0 will be 0x19, and r1 is the con value. Hooray! We’ve found the relevant section of code! (You can verify this by seeing if there are any more breaks, which there shouldn’t be).

Next step is to find the check for whether a unit is female. We don’t know exactly what the check looks like, but we do know that there’s going to be a conditional branch. So we’re going to scroll upwards until we find either a branch of some sort (keep an eye out for those arrows), or we find a push indicating the beginning of the function.
Well, we don’t have to look very hard. 189EA has a conditional branch to 18A00, which is immediately after the unconditional branch that we take to the sub r0,r0,r1 opcode. And the code in those two sections actually looks awfully similar…

In fact, the only difference is the last line in each block: mov r0,#0x19 vs mov r0,#0x14. Or, in decimal, 25 vs 20, which is the difference between male and female mounted aid. Given that, we can conclude that the conditional branch at 189EA is taken if the unit is female, and not taken if the unit is not female.

Now let’s take a look at the code that makes up the comparison/branch combination:

That top branch is unconditional, so it’s not interesting, except to show that we want to look at what comes immediately afterward.
The next two lines put the value 0x4000 in r0.
Finally, we check if that bit is set in the value in r1, and branch if is set (if r0 & r1 != 0). We’re not quite sure what r1 is, although you might be able to hazard a guess. Let’s scroll up and find out where r1 is set.
First, I’m going to look for a conditional branch. I assume there is one, otherwise we’d have taken the unconditional branch at 189E0. r1 will have had to be set before that.

Found another branch at 189CC, which takes us to 189E2, as expected. Then I check each opcode argument one by one, going backwards, from that point, looking for r1 on the left side of the arguments column, since that means r1 is being modified. We see 189C4 has orr r1,r0, and that seems to be the most recent time r1 was edited. So we scroll up a bit further to see what the values being orr’d are.

Well, we actually found the beginning of the function, since there’s a push. Let’s analyze what happens.
First, we push r4 and r14. r14 is usually pushed, unless the function is very short, but r4 being pushed implies that it will be used.
mov r4,r0 is saving the parameter that was passed in, implying that it will be used multiple times. Currently, r4 is 202BE4C, which is a specific unit’s data pointer. We can infer that this function takes a unit data pointer, and given what we’ve seen happens later, returns the unit’s Aid. That makes this function a “getter”, because it, uh, gets data. If this is a getter function, that makes our job much easier, because every time the game needs to calculate Aid, it’ll call this function. Any changes that we make to Aid won’t have to be repeated elsewhere (for instance, when calculating whether a unit can rescue another unit).
ldr r3,[r4] gets the first word in the unit data struct. If you refer to the Teq Doq, it says “pointer to character data”. That refers to the Character Editor table, where you can edit things like name, description, portrait, initial weapon ranks, etc.
Similarly, ldr r2,[r4,#0x4] loads the second pointer in the unit struct, which points to class data.
The next two are basically the same. We load the word at +0x28 of the character and class structs, respectively. If you want to find out what those are, open up the class and/or character editor nightmare modules (.nmm) files in a text editor, and find out what’s at the 0x28th (40d, since nightmare modules are indexed in decimal) offset:

Notice, however, that we are using ldr, not ldrb, meaning that we load all the character abilities at once. Remember how I said that we shouldn’t set a break on read on class abilities back when we were trying to set breaks? There were 2 reasons for that.

  1. We would have set a break on read on the second byte, logically, since that has the bit that represents the ‘female’ ability. So if class abilities start at +0x28, we would set a break on read to +29. Problem is, since all the abilities are loaded at once, as a word, setting a break on read to the second byte would never break.
  2. Ok, you say. We’ll just set a break on read to the first byte. And technically, this would work. However, you would almost certainly get a ton of breaks that aren’t related to checking the Female ability, due to checking other bits. I opted to not use this method because it was inefficient, not because it wouldn’t work.

Now that we have the class abilities in r0 and the character abilities in r1, we orr them together to get the sum of the unit’s abilities in r1. This is the value we wanted to know the origin of. Given this, we can figure out what the conditional branch at 189CC is for; it’s checking if bit 0 is set in the abilities, which is the ‘Mounted’ ability. Or, in pseudocode, the function ends up looking like:

if unit is not mounted:
   calculate constitution
   r0 = con - 1
   goto End
else:
   if unit is not female:
       calculate con
       r0 = 25
       goto CalculateMountedAid
   else:
       calculate con
       r0 = 20
   CalculateMountedAid:
   r0 = r0 - con
End:
return Aid in r0

We would like to change the if unit is not female to if unit is promoted. First, we have to find out how to determine if a unit is promoted. That’s actually easy: it’s another ability. Specifically, byte 2, 0x1, or bit 0x100 in the ability word. Female was byte 2, 0x40, or 0x4000 in the ability word. We generated that value with
mov r0,#0x80 lsl r0,r0,#0x7
How many times do we need to shift 0x80 to get 0x100 instead of 0x4000? Pretty easy. Once to the left. So instead of lsl r0,r0,#0x7, we need lsl r0,r0,#0x1.
Are we done? Not quite. Previously, we branched if the bit was set, because the result of and r1,r0 would be non-zero. Now, we want to branch if the bit is not set, ie, the result of and r1,r0 is zero. So we just need to change bne 0x8018A00 to beq 0x8018A00. Do this by clicking on the line, writing the appropriate command, and hit Enter.

->

Now to test. We expect nothing to change, because Seth is a promoted mounted unit, so he should still use the 25-con formula. And if you check…

…indeed he does. Now, how to check whether unpromoted mount returns the right result? We don’t have one on the map. Well, that’s ok. We’ll just pretend that Seth is unpromoted.
First, remove your original breakpoint (the break on read to Seth’s con bonus), and set a break at 189EA, right on the beq.

Then close the stat screen and reopen it. The game should pause.

See the ‘false’? That’s expected, because the bit was set, so the result is non-zero. But we don’t need to change the value. Remember that conditional branches depend on the flags that are set/unset? Well, beq will branch if the z (zero) flag is set. It currently isn’t, but if we were to check it manually…

…suddenly it’s true! The game is convinced that Seth is unpromoted. Let’s see if that claim is backed up by the stat screen.

It is! 20 - (11 + 1) = 8, as we expected. We have successfully accomplished what we set out to do.

…Or have we? We haven’t verified that this same function is used to calculate Aid when, say, seeing whether to display the Rescue option on the unit menu. So let’s do that. Set a break somewhere in the function, pull up the unit, and see whether you get a break. You did? Ok, good. See why having a getter is handy? And why not having one would be a pain in the ass if you need to track down every single instance? Like, say, for constitution and move?
No, I’m not bitter at all. Why do you ask? Anyway, MOVING ON!

If you close no$ or reload the rom, these changes will vanish, and that just won’t do. You could implement the changes manually in the rom; it’s literally 4 bytes. But instead, we’re going to make this into a file that can be inserted with Event Assembler, either by itself, or as part of a bigger project. Search for ‘buildfile’ on the forum for more details.

I use the following template for my hacks:

//FE8 Something or other
//By Tequila

#ifndef _FE8_
    ERROR "You're not assembling to an FE8 ROM!"
#endif

#include EAStdlib.event
#include "Extensions/Hack Installation.txt"

#ifndef FreeSpace
    #define FreeSpace 0xB2A610
    ORG FreeSpace
#endif

PUSH

POP

Let’s go through this line by line:

First, anything that begins with // is a comment. You can use /* */ around something to comment that something out; it also works over multiple lines. So the first two lines are a description of what this hack is for. In this case, I would call this something like “FE8 Mounted Aid Rework” or something.

#ifndef _FE8_ is for the radio buttons on Event Assembler’s main screen. If you accidentally select the wrong rom to assemble to, EA will throw an error, since you probably don’t want to put an FE8 hack in an FE7 rom, for instance.

I #include a couple of files that come with EA. EAStdlib (EA Standard Library) contains definitions for characters, classes, and items, along with a few other things. Hack Installation.txt has macros for, well, installing hacks, which will be covered a bit more in projects 2 and 3.

The FreeSpace bit isn’t actually necessary for this particular hack. I’ll explain when it’s useful in the PUSH/POP part.

ORG stands for origin; it basically means “start writing at this offset”.

PUSH and POP are for the ‘cursor’ position (ie, where things are getting written to). Why is this useful? Let’s illustrate with an example. Assume we have 3 chunks of data, 2 of which need to go in free space (called F1 and F2) and 1 which needs to go at offset 0x1000 (called X). We can make sure everything goes in its proper place as follows:

ORG 0x1000
<write X here>

ORG FreeSpace (whatever that might be)
<write F1 here>
<write F2 here>

First, the cursor is set to 0x1000, and X is written beginning there. Once that’s done, the cursor value is changed to FreeSpace. First, F1 is written, beginning at FreeSpace. Then F2 is written immediately following F1. No issues whatsoever. Now let’s change the order:

ORG FreeSpace
<write F1 here>

ORG 0x1000
<write X here>

ORG ????
<write F2 here>

We write F1 and X without any issues, but then F2 is a problem. If you don’t have an ORG, EA will keep writing after the last thing it wrote, which was X at 0x1000, and you definitely don’t want to keep writing there. If you know how long F1 is, you could replace the ???? with value (FreeSpace + length_of_F1). But what if F1 changes at a later point? Or maybe you want to insert something in between? We don’t want to have to keep updating the ORG; that sounds suspiciously like work.
Instead, we’re going to let EA take care of space management for us by using PUSH to save the current cursor position, and POP to retrieve it, as follows:

ORG FreeSpace
<write F1 here>

PUSH

ORG 0x1000
<write X here>

POP

<write F2 here>

Once F1 has been written, we save the current cursor position with PUSH, go to 0x1000 to write X, then retrieve the cursor with POP and continue writing F2 after F1. Handy, no?
Basically, you write your inline stuff (anything that has an ORG) after the PUSH, POP once you’re done, then write the stuff in free space.

For this mounted aid rewrite hack, all our changes were inline, so everything will be after the PUSH. First, we changed line 189E4, so begin with
ORG $189E4
You can use $ or 0x to indicate hex; they’re interchangeable, but if you don’t have either one, EA will assume the value is decimal, and that may lead to issues.
At this address, we’re going to write the hex of the opcode we changed. Since opcodes are halfwords/shorts, we’re going to use SHORT:

ORG $189E4
SHORT 0x0040

We can do the same with the other line we modified:

ORG $189EA
SHORT 0xD009

Lastly, I recommend adding comments to explain briefly what each thing is for.

//FE8 Mounted Aid Rework
//By Tequila

#ifndef _FE8_
ERROR "You're not assembling to an FE8 ROM!"
#endif

#include EAStdlib.event
#include "Extensions/Hack Installation.txt"

#ifndef FreeSpace
#define FreeSpace 0xB2A610
ORG FreeSpace
#endif

PUSH

ORG $189E4
SHORT 0x0040 //lsl r0,1; checking if unit is promoted rather than female
ORG $189EA
SHORT 0xD009 //changed bne to beq

POP

And that’s it! Save this, either as a .txt or .event (EA looks for .event files first, but it really doesn’t matter), then assemble it with Event Assembler, or #include the file as part of a larger project.

Congratulations, you made a hack. A short one, to be sure, but that’s fine. Gotta start somewhere, after all.

18 Likes