- •Downloads:
- •Update Log:
- •Table of Contents:
- •Prologue: Bare Basics
- •Chapter 1: Key Terminology & Abbreviations
- •7Zip Archive – Supposedly the best file archiver there is, but not used as much, and thus less convenient. Requires 7zip or winRar to use.
- •VisualBoyAdvance – most people’s emulator of choice, almost always referred to as “vba” for short.
- •Chapter 2: Using Nightmare Modules
- •I upload anything that I think might be useful to someone on that site. Just use the menus and search until you find it.
- •Chapter 3: File Management
- •In order to be a successful hacker you need to have a lot of good management.
- •Chapter 4: Pointer Tables
- •Chapter 5: Battle Animation Editor
- •Chapter 6: Character Editor
- •Chapter 7: Class Editor
- •Chapter 8: Item Editor
- •Item icon – obvious
- •Chapter 9: Spell Association Editor
- •Chapter 13: Movement Cost Editor
- •If the value next to a type of terrain is ‘255’ then it is uncrossable because a unit won’t have 255 movement points.
- •Chapter 16: Battle Palette Reference Editor
- •If you want to know how to actually edit battle palettes’ colors, you can find that in a later chapter that I will make.
- •Chapter 17: Arena Class Editor
- •It’s a bit of work, but making cGs is quite rewarding, and it’s easier than some stuff, that’s for sure! Good luck with your cg making!
- •Chapter 20: Lyn’s Ending Editor
- •Chapter 21: Tutorial Editing/Getting Rid of the Tutorial
- •Part 2: Downloading the Programs
- •Part 3: Preparing Your midi
- •If you still have more than 10 tracks, you should find another midi. Sorry but, there are limits in life.
- •Part 4: Applying Blazer’s Beta Music Insertion/Instrument Patch
- •Part 5: Converting Your midi
- •Part 6: Making Your midi Repeat and Transferring it to Your rom
- •If the b1 and bc are next to each other then I can almost guarantee you want to replace it, so hit “replace” and do that with every instance and once you’re finished you’re good to go.
- •Part 7: Track Pointers & Repointing
- •Part 8: Finale- Assembling Your Song & Profit
- •If you don’t have this module, you’ll have to use this outdated way of doing it. Do check to see if you have the latest Nightmare Modules in general, but otherwise… well, sorry I guess. Xp
- •Part 9: Possible Errors & Wrap-up
- •Part 10: Documentation and Stuff
- •Atrius’ Notes:
- •Charon’s Notes:
- •Part 11: The Music Hacking Run-Down (Shorter Version of Tutorial & Walls of Text)
- •Part 12: Credits & Thanks
- •Chapter 25: Music Editing with Zahlman’s Song Editor
- •If you actually do type in help and press ‘enter’ on your keyboard, it’ll give you a list of commands, and tell you some stuff. Here’s the important stuff:
- •It worked! Great! I know how to import a song!
- •Chapter 26: Exporting Music with gba2midi
- •Chapter 27: Battle Background Graphics
- •If it doesn’t, I suggest double-checking all your settings (everything should be compressed) and make sure your width is set to 30 and your height is set to 32.
- •Chapter 28: Music Array Module
- •Chapter 29: Sound Room Editing
- •Chapter 30: Chapter Unit Editing with Nightmare
- •Chapter 31: Death Quotes
- •Chapter 32: Event iDs
- •Chapter 33: Battle Conversations
- •Chapter 34: Triangle Attacks
- •Chapter 35-36: The Animation Modules & Repointing Tutorial
- •It should look like this:
- •Chapter 37: Support Editing
- •Chapter 38: Miscellaneous Nightmare Modules
- •In this chapter I’m going to quickly run through what some other nightmare modules do.
- •Vendor/Armory Editors – edits the contents of vendors and armories.
- •Vulnerary Editor – edits the amount of hp restored by a vulnerary. (Default: 10)
- •Vulnerary Editor – edits the amount of hp restored by a vulnerary.
- •Chapter 40: Text Editing with fEditor Adv
- •Chapter 41: Portrait Formatting & Preparation
- •Chapter 42: Portrait Insertion with fEditor Adv
- •I wouldn’t mess with the palette editor (the colorful boxes).
- •Chapter 43: Locating Palettes
- •Chapter 44: Editing Palettes
- •I don’t exactly have a color I want to use for this title screen background, so I’m just going to show you how to get the rgb of some random color on a portrait.
- •If something didn’t work right, make sure you:
- •Chapter 45: Working with gbage
- •Chapter 46: Chapter Data Editor
- •Vision Distance is for Fog of War (fow). If it’s ‘0’, it’s assumed there is no fog of war.
- •Hold it! (Unless you aren’t hacking fe7!)
- •Chapter 47: Map Creation
- •I’m tired of writing this tutorial. Honestly. So from now on, I’m going to stop making so many wasteful comments like the one I am typing right now.
- •Chapter 48: Map Insertion
- •If you’re looking to make a totally new chapter (instead of being limited to the old game’s exact same scenes with exact same events) then read on, because I’m going to hack events next!
- •Chapter 49: Event Assembler Basics
- •I would just always add end guards since it’s not something you need to worry about too much.
- •Chapter 50: Events – The Layout
- •Including the stlb
- •Chapter 51: Events – The Event Codes
- •Items is just a list of items with a max of 4 starting items. I prefer to use the 3rd method of writing them, with the brackets and all. Each item is separated by a comma.
- •Chapter 52: Event Construction
- •VillageGate: // name of tile data group
- •Chapter 54: Chapter Creation Finishing Touches
- •Chapter 55: Importing Tilesets
- •Part 2: The First Frame
- •Part 1b: Palette Preparing
- •Part 2: Testing the Foundation to Your Animation
- •If all goes well, your guy should be standing, kinda like this.
- •Part 3: Making the Rest of Your Frames
- •Chapter 58: Custom Battle Animations – Scripts
- •I just pulled a Xeld. Had to do that at least once in this tutorial.
- •If you don’t know what a sound sounds like, just test it out with your animation and find out. Experiment with the codes if you need to.
- •Chapter 59: Custom Spell Animations
- •0X85 command count for this spell: 10
- •It’s true! It did work! It’s still very much a work in progress, as you can see, but the point is we got he test frame working. The rest just takes time, patience, and the attitude that you can do it!
- •Chapter 60: Weapon Icons
- •If you did, you are successful. Despite the odd format of the icons, you have spotted them, and that is what is most important, in my honest opinion.
- •I have this show up:
- •Chapter 61: Map Sprites
- •Chapter 62: Proper Betatesting
- •Chapter 63: vba’s Tools
- •Chapter 64: Other vba Options
- •In this chapter I’m going to detail some of vba’s semi-obscure but not totally obscure options. Knowing how to use vba will help you test your game in various ways.
- •Chapter 65: Recording Videos & Sound
- •Chapter 66: Fixing the Desync with VirtualDubMod & Video Rendering
- •Chapter 67: ips Patching & General Patching Information
- •Chapter 68: ups Patching
- •I suggest you read the ips patching tutorial (at least the beginning) if you haven’t done so as I will not be as thorough with this chapter as I was the previous.
- •In an extremely similar manner you can apply patches. Take a look.
- •Chapter 69: jfp Patching
- •Chapter 70: xDelta Patching
- •Chapter 71: Nightmare Module Format
- •It is recommended (for reasons of readability by humans) that a newline
- •Is unused ("null") for editboxes.
- •Chapter 72: Miscellaneous Information Archive
- •Chapter 73: Useful Links & Websites
- •Chapter 74: Bonus – Assembly Hacking
- •Preparations:
- •Part 1: Background Info
- •Part 2: Inserting an Assembly Hack
- •Part 2: Breaking Down Your First asm Hack
- •I digressed a lot, but back to the point:
- •Part 3: Second Example – More Codes, More Fun
- •Read other people’s doc.
- •Part 4: More Examples – “Speed-Analyzing”
- •It’s thumb. Write to offset 0. Start with label “Initial”. Push 5 registers and the last register, then start a loop counter in r2 with starting value 0x00.
- •Ifat *Conditional id* *asm routine pointer*
- •I may have mentioned this before, but finding where to hack routines is difficult. And I’m sure I mentioned that finding space for them is difficult.
- •It’s not super long, but it’s got some new things we need to learn. Let’s get started.
- •Part 5: Finding asm Routines & Basics of Using a Debugger
- •Warning: terms may not be accurate. In fact, they almost definitely aren’t accurate, as you’ve probably figured out by now.
- •I don’t know what the flags do either, but they’re there, right next to the window. That’s g.
- •I hope to hear of your achievements in the near future!
- •Final Chapter: Credits, Thanks, and the Epilogue
Part 3: Second Example – More Codes, More Fun
Before we go onto more examples, I suggest doing some experimenting. Take that simple ASM hack I gave you that makes the mode Hector Hard Mode and change some of the values. Change the 0x40 to something else. Change the offset to something else. Be careful you don’t choose an offset that breaks the game though—if you’ve memory hacked before, which you should have, you should know how to edit characters stats through memory. Well, find the address of say, the level of some unit, and type that in instead. Make their level 99 because no one’s level in Fire Emblem has ever gone that high and it’d be interesting. (I shouldn’t be saying this because it’s such a fundamental, but use MS Calculator or something if you need to convert from hexadecimal to decimal, decimal to binary, or anything like that.)
Once you feel comfortable enough making small edits how you like, let’s continue on to learn more opcodes. Also, I’m going to expect you to experiment and play around on your own from here on out. I might not say so explicitly but it’ll take good practice to really know these codes. Your hacks don’t have to make some awesome change or even be useful—it’s all for learning purposes. However, you CAN make useful hacks with what knowledge you have right now—after all, making the game use Hector Hard Mode allows one to use the bonus levels feature, which autolevels enemies according to a value you can set using the Chapter Data Editor, which is a nice feature to have handy.
The next example is a huge step up from the last one, but whatever, you can study and perfect things on your own time—let’s get to it.
https://dl.dropbox.com/u/8875056/hacking/asm/Character%20Data%20Clearer%202.zip – This hack is known as the Character Data Clearer hack and is capable of clearing unit data by a table. I used it in TLP to clear all the character data for use in the post-game, because everyone’s levels are reset. Furthermore, I *think* that it is also used in Elibian Nights to reset the character data after a tale has been completed. The manual way is to spam the KILL code on every possible unit which is not only a stupid copy/paste job but is also time-consuming in game as the player has to wait for characters to be killed in the background (with the worst part being that you can hear the death sound -_-).
So we know what it does—let’s open it up and see what it’s all about. Keep in mind that I am not always the best note-taker myself because I tend to be lazy and some of these codes are so common/simple for me that I didn’t always feel the need to document them if I already knew what they did. However, other times I write a LOT of notes, mainly when I’m using new codes or combinations of codes or am trying to fix bugs. In general, if you have the time, write notes—aside from taking up time, it doesn’t hurt, and it might ultimately save you more time than it took to write them.
These first four lines are just notes. Okay, so we DON’T need spaces after the “@” symbol. Seems I just forgot about that detail. :P Anyhow, I don’t note every register here, but I do note some things. As for what it means, you’ll figure it out later.
We know what the first two lines do. However, the third line is new. However, don’t be alarmed: it’s just like the Event Assembler. “Initial” is a label. We can reference it. Whatever offset we are at, that is what “Initial” is equal to. Right now, that means offset 0x00. However, when writing code, that offset isn’t always obvious. If you want to jump between parts of the code, you create labels and reference them.
HOWEVER (you’ll be reading me “however” you a lot), you won’t see this when disassembling the game’s assembly. These disappear. The assembler uses them, but in terms of actual code, they aren’t there. It’s just like the .thumb codes—they’re used to write the code, but the output will ultimately all be nonsensical numbers.
Let’s move on.
push {r0-r6, lr}
This is an important code and a difficult concept to teach. What it does is it “pushes” registers. The basic idea is that it stores the values in the registers for later use—you can bring them back later. It stores them in what’s called a “stack”. It puts them on the top. When you use the opposite of the push command, which is pretty much a must, you bring back what’s currently on top of the stack. In this case, we are “pushing” r0 THROUGH r6 (the hyphen means through) as well as lr, or r14 (the comma is used to specify a separate one). You could type push {r0, r1, r2, r3, r4, r5, r6, lr}, but it’d be pretty silly and inefficient.
If you need some analogies as to how this push thing works, here’s a Skype chat with a friend I had (I am “ballin1337”, FYI) where I casually explained the push/pop. FYI, the pop works the same way, except you type “pop” instead of push, and if you “push” lr, you must “pop” pc in return (and of course you must also pop all the other registers that you pushed). Anyhow, here is the conversation—read it if you want, or skip ahead if you get it already.
[1/18/2013 7:14:30 PM] ballin1337: next is a new rather important but complicated code
[1/18/2013 7:14:33 PM] ballin1337: the "push" code
[1/18/2013 7:14:39 PM] ballin1337: it actually kind of does what it says
[1/18/2013 7:14:43 PM] ballin1337: and works with another code, called the "pop" code
[1/18/2013 7:14:46 PM] ballin1337: what they do
[1/18/2013 7:14:54 PM] ballin1337: is they move data into stacks
[1/18/2013 7:14:56 PM] ballin1337: and move them out of stacks
[1/18/2013 7:15:04 PM] ballin1337: so lemme give you an analogy
[1/18/2013 7:15:19 PM] ballin1337: so let's say you're working at your desk.
[1/18/2013 7:15:33 PM] ballin1337: on your desk, you have a bunch of folders
[1/18/2013 7:15:38 PM] ballin1337: because you were working on this stupid history project
[1/18/2013 7:15:57 PM] ballin1337: so now it's all cluttered
[1/18/2013 7:16:01 PM] ballin1337: and you don't have ANY space on your desk
[1/18/2013 7:16:06 PM] ballin1337: annoying, right?
[1/18/2013 7:16:12 PM] Jubbeeh: most defs
[1/18/2013 7:16:21 PM] ballin1337: so you're like
[1/18/2013 7:16:27 PM] ballin1337: "I can procrastinate on my history homework"
[1/18/2013 7:16:31 PM] ballin1337: "but math is due TOMORROW"
[1/18/2013 7:16:38 PM] ballin1337: thing is, you don't have any space to work with
[1/18/2013 7:16:44 PM] ballin1337: so what do you do?
[1/18/2013 7:16:48 PM] ballin1337: you push everything off the desk
[1/18/2013 7:16:55 PM] ballin1337: and make room for your math homework.
[1/18/2013 7:17:08 PM] ballin1337: now everything's on the floor
[1/18/2013 7:17:16 PM] ballin1337: you roughly know where it was on the table though
[1/18/2013 7:17:18 PM] ballin1337: so you can put it back easily
[1/18/2013 7:17:25 PM] ballin1337: but right now, you're doing math
[1/18/2013 7:17:32 PM] ballin1337: not only that
[1/18/2013 7:17:38 PM] ballin1337: but you made a note of where you were in your history project
[1/18/2013 7:17:46 PM] ballin1337: to make sure you could start it back up at any time
[1/18/2013 7:17:52 PM] ballin1337: without having to figure out what the hell you were doing
[1/18/2013 7:17:58 PM] ballin1337: now
[1/18/2013 7:18:00 PM] ballin1337: you do your math homework
[1/18/2013 7:18:05 PM] ballin1337: while screaming in agony
[1/18/2013 7:18:14 PM] ballin1337: eventually though
[1/18/2013 7:18:15 PM] ballin1337: you finish
[1/18/2013 7:18:23 PM] ballin1337: and the next day, you decide it's time to get back to that history work
[1/18/2013 7:18:26 PM] ballin1337: so what do you do?
[1/18/2013 7:18:40 PM] ballin1337: you take the folders
[1/18/2013 7:18:45 PM] ballin1337: "pop"'m on the desk
[1/18/2013 7:18:48 PM] ballin1337: and pop'm open
[1/18/2013 7:18:52 PM] ballin1337: ready to use
[1/18/2013 7:18:58 PM] ballin1337: ready to get straight back to work, right where you left off.
[1/18/2013 7:19:02 PM] ballin1337: and that's push/pop.
[1/18/2013 7:19:06 PM] Jubbeeh: XD
[1/18/2013 7:19:16 PM] Jubbeeh: pop'm back
[1/18/2013 7:19:18 PM] Jubbeeh: winning
[1/18/2013 7:19:22 PM] ballin1337: XD
[1/18/2013 7:19:27 PM] ballin1337: a little bit of a stretch but eh
[1/18/2013 7:19:41 PM] ballin1337: as for how this applies
[1/18/2013 7:19:42 PM] ballin1337: easy
[1/18/2013 7:19:47 PM] ballin1337: registers often have values in them already
[1/18/2013 7:19:49 PM] ballin1337: and said values are important
[1/18/2013 7:19:55 PM] ballin1337: you don't want to just screw up whatever is in there!
[1/18/2013 7:20:00 PM] ballin1337: the game might need it!
[1/18/2013 7:20:05 PM] ballin1337: so what you do is
[1/18/2013 7:20:09 PM] ballin1337: you "push" them
[1/18/2013 7:20:11 PM] ballin1337: to save them
[1/18/2013 7:20:20 PM] ballin1337: then, you can use them freely
[1/18/2013 7:20:22 PM] ballin1337: and once you're done inserting your own code
[1/18/2013 7:20:25 PM] ballin1337: you "pop" them back
[1/18/2013 7:20:35 PM] ballin1337: so that the game is right back where it left-off
[1/18/2013 7:20:46 PM] ballin1337: how it's done is
[1/18/2013 7:20:52 PM] ballin1337: push {r0} for instance
[1/18/2013 7:20:56 PM] ballin1337: anything in these curly brackets
[1/18/2013 7:20:57 PM] ballin1337: is pushed
[1/18/2013 7:20:59 PM] ballin1337: remember though
[1/18/2013 7:21:01 PM] ballin1337: ANYTHING you push
[1/18/2013 7:21:04 PM] ballin1337: MUST be popped back
[1/18/2013 7:21:06 PM] ballin1337: the game will freak out otherwise
[1/18/2013 7:21:11 PM] ballin1337: it's like pushing it off your desk
[1/18/2013 7:21:18 PM] ballin1337: and losing the project information
[1/18/2013 7:21:27 PM] ballin1337: now you have no idea what the **** to do anymore
[1/18/2013 7:21:32 PM] Jubbeeh: right
[1/18/2013 7:21:37 PM] ballin1337: [s]and you were sleeping during class and you don't have any friends[/s]
[1/18/2013 7:21:52 PM] Jubbeeh: seems close enough to irl XP
[1/18/2013 7:21:57 PM] ballin1337: you can also do push {r0, r1, r2}
[1/18/2013 7:21:58 PM] ballin1337: LOLOL
[1/18/2013 7:22:08 PM] ballin1337: I was totally kidding, just for the record xD
[1/18/2013 7:22:15 PM] Jubbeeh: (jk i have a couple friends XP)
[1/18/2013 7:22:15 PM] ballin1337: so you can separate them by comma's
[1/18/2013 7:22:18 PM] ballin1337: but often it's easier to do
[1/18/2013 7:22:25 PM] ballin1337: something like push {r0-r6}
[1/18/2013 7:22:31 PM] ballin1337: which pushes ALL the registers between r0-r6
[1/18/2013 7:22:33 PM] ballin1337: also, "lr"
[1/18/2013 7:22:41 PM] ballin1337: is like a special name for "r14"
[1/18/2013 7:22:46 PM] ballin1337: it stands for "last register", IIRC
[1/18/2013 7:22:57 PM] ballin1337: just make sure you pop'm back accordingly.
[1/18/2013 7:23:01 PM] ballin1337: I've had glitches cuz of forgetting to do that
[1/18/2013 7:23:06 PM] ballin1337: or typing "push" both times and stuff
[1/18/2013 7:23:07 PM] ballin1337: XD
[1/18/2013 7:23:14 PM] ballin1337: it's the worst because like
[1/18/2013 7:23:17 PM] ballin1337: even when debugging
[1/18/2013 7:23:19 PM] ballin1337: you might not realize
[1/18/2013 7:23:21 PM] ballin1337: until the VERY end of the code
[1/18/2013 7:23:23 PM] ballin1337: lol
Now that that text pillar is done with (or your scrolling down is done with, whichever suited your fancy), let’s cover the next code.
mov r2, #0x00 @ Starts off memory table check counter at 0
You should already know what this does—it stores the value 0x00 into r2. Remember, there might already be a value in r2 because we’re just interrupting the game with our ASM routine. For that reason, we used the push {} opcode to make sure we stored the values safely for registers r0 through r6 (and the mysterious “lr”). Now we can put whatever we want in the registers r0-r6 without worry because we can bring back the old values once we’re done using the pop {} command. See how useful those are?
Now, the comment says we’re making some memory table check counter. What’s that?
It is a method of making a loop. We are setting the loop counter to “0x00”. Every time we go through the loop, r2 will increase by 1, meaning the loop counter will increase by 1. Thus we can keep track of how many times we go through the loop, and we can also use r2 in other ways, like to say “let’s check entry #r2”, where r2 corresponds to which run we’re on. If we’re on run #3, then it will check entry #3. If you’ve programmed in other languages, this idea of a loop is probably pretty basic, but if not, make sure you pay close attention and practice this once I’m done explaining.
Main: specifies that this part of the code is called “Main”. I did this because it is the main loop. Now if I need the code to jump back to this part, I can reference it as “Main”.
Add r2, #0x01 is a simple code. It just adds values and/or registers together. If you look at thumbref, you can see all the variants. This one says “ADD Rd, # Rd = Rd + #”, meaning that since Rd = R2 and the # is 0x01, R2 = R2 + 0x01. In other words, we’re adding 1 to R2, or increasing the loop counter by one. Subtraction works very similarly.
Cmp r2, #0x2A introduces another major concept: conditional branching. What it does is compares two values and looks for some condition—if they’re equal, not equal, if one is greater than the other, less than or equal to the other, etc., according to what you choose, and then if it means the condition, it branches you off to somewhere else, and if it doesn’t, then it continues reading from where you left off.
In this case, it is comparing the value in r2 to 0x2A. If this is the first loop, then r2 is 0x01 right now. So what’s the condition? We can tell by the next code, “beq”. “b” stands for branch and what comes after is the part of the mnemonic that decides what condition it’s looking for.
It’s the first one on the list here, “EQ”, meaning “Equal”. In other words, if checking to see if the two values in the “cmp” code above are equal to each other, and if it is, it goes to the offset after it, called “End”. Before I explain that, let me quickly mention the other two columns above: the “Opcode” column refers to the bits that control what opcode it is (you probably won’t ever need to worry about this), and I have no idea what the status flags do, sorry. I never had to use them so I never learned them. XP
Okay, so we had
So “cmp” is compare and it’s comparing r2 to 0x2A (42) and if they are EQUAL, it goes to the end; if not, it just keeps going to the line after. Now, what is “End”? “End” is a label, just like “Initial” and “Main”. I specify the location of the label “End” by putting it at a certain part in the code some time down below. The assembler will find the offset of “End” and jump to it. Fun times.
Now, what is the significance of this? It says that “if the loop counter has reached 0x2A, or if this is the 42nd time it is going through, then go to the end”. It’s letting the routine quit, or stop. There has to be some way for a loop to stop, after all. This one will stop after 42 checks. The significance of 42 is that the game starts to get glitchy (at least in my experience) after about 42 characters are deployed. In fact, I had to limit the player to only 40 characters in the post-game, I think, due to some silly glitches I couldn’t figure out that only happened to characters 42 and on or something, forcing them to make some choices (though they can re-recruit any character if they so wish, so it’s tolerable).
We’re
going to tackle this next part a lot faster than we have the last few
parts—we’re learning more and more codes and you’ve already
seen variations of some of these. Practice and/or use thumbref if you
need to!
First, we load the word 0x0202BD08 into r5. We intend for this to be an offset. It’s actually one entry before the first character slot in the memory, as the note mentions. Character data in FE7 starts at 0x0202BD50—each entry is 0x48 bytes. 0x0202BD50 – 0x48 = 0x0202BD08. As for why we choose this offset, it has to do with the math, which we’ll see ahead.
Then we load the value 0x48 into r3. Next, we use the “mul” opcode to multiply two registers together. The “mul” opcode is pretty cool, but it’s limited in that it can ONLY multiply two registers together, which means you have to waste a register “mov”ing the value to multiply by beforehand (as I did with mov r3, #0x48). The first register is the destination register, or the register whose value changes, as per almost every code except the confusing str code. So mul r3, r2 says that r3 = r3 * r2. On our first loop, r3 = 0x48 (this is actually for every loop because it gets set to 0x48 right before the multiplication) and r2 = 0x01 (the loop counter is set to 1 on our first loop). In THIS case, r3 is STILL 0x48, but on every subsequent loop, r3 will be some other multiple of 0x48.
Next, we add the two registers r3 and r5 together. What this does is adds the value in r3 to r5 and stores it in r3. Since r5 contains the base address 0x0202BD08, if we add 0x48 to that, we get 0x0202BD50. That’s the address of the first character in the memory. On the 2nd loop, we’ll add another 0x48 to that, getting the offset of the second character in the memory. So on and so forth: we just made the game calculate the offset of the character in the memory respective to our loop counter.
Next, we use ldr r4, [r3] which instead of loading a word that we specify, loads a word at the offset r3. R3 is currently 0x0202BD50. The first word in a unit’s memory is a pointer to their character data, though the GBA Codebreakers Code on gamefaqs calls it something like “portrait” (it DOES change the portrait, but it changes like, a lot of other things as well). By doing ldr r4, [r3] we are loading the word AT r3, because that’s what the brackets [] mean. The destination register Rd is r4, so we’re loading it into r4. Now, this offset will vary, but for this example’s sake let’s say it is 0x08BDCE4C. Thus r4 = 0x08BDCE4C.
The next command loads a byte into r4. What it loads is what’s in the brackets: it says [r4, #0x04]. A look at thumbref says that what it’s loading here is the byte (ldrb) at the offset of (r4 + 0x04). In other words, it’s loading the 4th byte in the character data. If you’re experienced enough, you should be able to look at the character editor Nightmare Module and see the base offset is 0x08BDCE4C and that the 4th byte in a hex editor is the character ID. The character ID is significant because it is an index that is like a shortcut as to what character we’re referring to. In FE7, 0x01 is Eliwood, 0x02 is Hector, 0x03 is Lyn, etc.
The code after starts another counter, though this time it stores it in r6. And so once we find our character ID, we continue on to the next part:
From
a practical standpoint, we haven’t really done anything yet, but in
actuality we’re making good progress. Next is this “TableCheck”
part of the code. I wouldn’t label it “TableCheck” if I wasn’t
going to reference it later—keep that in mind. (Otherwise I could
just use a comment like @ Table Check, right?—on a side note, I
don’t think labels can have spaces and certain other characters, so
be careful.)
We have add r6, #0x01 which works just like the last time, which is why I didn’t even make a comment for it—we’re adding 1 to the counter.
Next, we load the value 0x08DDFFFF into r5. You should know this without me telling you, but I’m noting it because 0x08DDFFFF is an odd offset. As it turns out, it’s 1 before 0x08DE0000. I chose it for a reason, and while in retrospect (it’s been a while) I probably could have coded this better to avoid choosing weird offsets, you’ll see why I did soon.
The next code does something like the ldrb opcode we saw before, but now it takes the byte at the offset (r5 + r6) and loads that into r5. A look at thumbref will once again tell you this. On our first run, r6 is 0x01, so it loads the byte at 0x08DDFFFF + 0x01, or 0x08DE0000. Aha! We set the offset to be one before 0x08DE0000 before because we knew that on our first loop, we’d be adding one, because we’re using the loop counter like that.
Now, we have this byte from 0x08DE0000, a place in our ROM (because the ROM is stored in the memory at 0x08000000), stored in r5. What is this value? It is, in fact, data that the user of the hack is defining. We are dealing with a user-created array or table. What it expects here is a value of a character ID—a character ID of a character whose data we want to remove. In other words, this hack expects the user to put their list of characters they want removed at 0xDE0000 in a hex editor. Then, when the hack is used, it will load the byte at this offset. Thus, r5 now contains the character ID of the character we want to remove.
Keep in mind that before, we got the character ID of the first character in the memory, and we stored it in r4. That was the first loop.
Next, we’ve got some fancy conditional branching.
We’re going to start by comparing the character ID we got from the table at 0x08DE0000 to 0x00. If it is, then that’s supposed to mean it’s the end of the table, and we return to “Main”, or the beginning of the first loop. You’ll see why this is important in a short bit.
Then, it it’s NOT 0x00, it checks to see if r4 and r5 are equal, i.e. if they have the same value. If they are, then that means the character ID of the current character in the memory is EQUAL TO (beq) the character ID in the table, and that we should thus remove the character from the data. To do so, it jumps to some code under the label “MakeChange”, which we’ll get to later. If NOT, then it’s supposed to keep reading after the beq code—and what’s after that is the “b TableCheck”, which is a direct branch to the code under the label “TableCheck”. TableCheck is what we’re in right now. Let’s say the first character ID we got from the table wasn’t a match—it was 0x0A, and our character ID was 0x15.
It jumps back to “TableCheck”, where it adds one to the loop counter, r6. Then it does the SAME THING, but this time it will check 0x08DDFFFF + 0x02, or 0x08DE0001. It will load the byte there, by virtue of
ldrb r5, [r5,r6] @ Loads the byte/char # there
and check to see if THAT matches.
And if THAT doesn’t, it will continue on until it hits a 0x00, meaning “you can stop checking the table now—there are no more entries”, or a match. If it hits a 0x00, it will go back to the MAIN loop and check the NEXT character in the memory—and go through the code ALL over again. That’s quite a bit of looping and checking for it to do, but the processor will be fine—it can do all of this in a fraction of a second. You probably won’t even be able to notice it in-game.
Let’s say it finally finds a match in the table—then it goes to “MakeChange”. I wonder what’s that…
We FINALLY get to the part where we actually DO something. This ain’t called “MakeChange” for nothing. It’s a simple but effective code that stores 0x00 into r0 and then stores the value of r0 into the address of r3 via str r0, [r3]. First, note that when I move 0x00 into r0, it will replace all the other “digits” in the register with 0 as well, meaning r0 = 0x00000000 (a register is technically a word long).
Thus when we store r0 into r3, we are actually storing the WORD 0x00000000 into the offset r3. What is r3? Well, way back when, we calculated the offset of a character in the memory. So what r3 is, is the offset of the current character in the memory we are checking—checking to see if they should be removed. Funny thing with Fire Emblem is that when you change the character data pointer, the first word in a unit’s/character’s memory data, to 4 00’s (0x00000000), the game will end up deleting it and moving up all the entries below. In other words, it’s an easy way of getting rid of the character. That’s exactly what we’re doing here—we’re replacing the first 4 bytes of the character’s data in the memory with 00’s, and once this routine is done, the game will do the hard work and shift everything up for us.
…Sadly, it’s not done. It then uses the basic branch command to go back to the first loop—that’s what “b Main” is for. It will NOT end until it reaches that code near the very beginning:
cmp r2, #0x2A @ If its entry 0x2A
beq End @ Go to the end
In other words, until it has checked 41 characters/reached its 42nd check, it will not stop checking to see if any of the characters in the party should be removed. After all, we have to be thorough!
Once it IS finished though, it will go to “End”.
What is “End”?
I said it a while ago, but that doesn’t change how important it is. We pushed some registers at the beginning, so we’d better push them back, or the game’s going to fuss. Remember also that the opposite of pushing “lr” is popping “pc”.
My final explanation for this example: what it does is pushes the offset of where we were before the code starts. Then, when we pop it, we are popping it into r15, meaning we are making the game GO to the offset where we were, because pc/r15 is the offset of where we are. So if we change r15, we are in fact changing where we are in the game’s ASM. Again: we are changing pc/r15 to where we were BEFORE we started running this code. This is our way of ending this routine and returning back to whatever we were doing before it.
…Wooh.
With that knowledge, go ahead and make a table at 0xDE0000 for characters to remove, add the ASMC code to an event, and try magically removing characters from oblivion. You know a LOT about ASM hacking now—practice what you can by making small edits, and…
And actually, you can do a lot more now—however, there is one major problem. To be able to make changes, you have to know offsets to change. How did we know to use 0x0202BD08, and in part 2, 0x0202BC13, etc.?
Well, one way to know is to debug.
Another way to know is to look at other people’s doc.
I am tired of ranting (it may seem impossible but the pain in my left hand is too great), so I will say it clearly now (and I’m sure I’ll remind you because I’m like that and love adding parantheses all the time, like right now).
