• Welcome to Final Fantasy Hacktics. Please login or sign up.
 

Pokey's ASM Tutorial: Part #0

Started by pokeytax, October 24, 2011, 09:17:12 pm

pokeytax

October 24, 2011, 09:17:12 pm Last Edit: October 24, 2011, 09:22:38 pm by pokeytax
ASM Tutorial #0: Breaking Code

NEEDED
pSX debugger, psxrel.exe {http://ffhacktics.com/smf/index.php?topic=5185.0}
a Final Fantasy Tactics ISO {left as an exercise for the reader}

In this lesson we are going to do the simplest thing we can: break something. Creating something is hard, but destroying something is easy. I may need to go back and do tutorial #-1 before moving on to tutorial #1, if this one is too complicated and I took too much knowledge for granted! But we'll start here for now.

You can learn to ASM.

Victory Condition

Today's goal is to break "Monster Talk" - disable its effect so the RSM slot can be used for other stuff.

Step 0: Basic Knowledge
Hex numbers are represented by "0x00". "0x08" is 8. "0x10" is 16. "0x1A" is 26. "0x100" is 256. You don't need to be able to add hex to ASM hack, that's what computers and calculators are for; but, you do need to not mix up your hex and decimals.

Step 1: Set Up A Repeatable Test Condition

To examine Monster Talk, we need to set up a situation where it has an effect. While the byte might be checked if a human talks to a human, it'll definitely be checked if a human talks to a monster; and equally importantly, we'll need a monster to test if our hack worked or not. So enter a battle with a monster and savestate when it's someone with Talk Skill's turn in range of a monster, so we can keep reloading and testing. I suggest Ramza because he's a little easier to pick out of a crowd. If at any point you goof up, reload your savestate.

Step 2: Set Up A Breakpoint

In order to find out where the code we want to edit is located, we need to set up a breakpoint. A breakpoint is a trigger that stops everything when certain memory is loaded, written, or executed.

Lots of stats are checked over the course of using Talk Skill, but what we are interested in is when it asks whether the player has Monster Talk. So we are going to put a READ breakpoint on our human unit's Monster Talk byte, which will freeze the action when Monster Talk is checked. First, though, we have to look up that byte.

Step 2A: Unit Data

The most important page on the wiki is ffhacktics.com/wiki/Formula_Hacking. It tells you about unit data in battle. Just about everything that happens in battle starts or ends with unit data, and this details what most of the bytes mean. This will let you figure out where to look, but you need to know which unit to check as well.

The following are the offsets for each unit, taken from the Final Fantasy Tactics Gameshark Handbook, which is a useful source in its own right.


 UNIT  |  ppp qqq rrr sss ttt uuu  DETAILS
======================================================
No. 17 |  1924CC  [Ally #1]
------------------------------------------------------
No. 18 |  19268C  [Ally #2]
------------------------------------------------------
No. 19 |  19284C  [Ally #3]
------------------------------------------------------
No. 20 |  192A0C  [Ally #4]
------------------------------------------------------
No. 21 |  192BCC  [Ally #5]
------------------------------------------------------
No. 01 |  1908CC  [Enemy / Guest #1]
------------------------------------------------------
No. 02 |  190A8C  [Enemy / Guest #2]
------------------------------------------------------
No. 03 |  190C4C  [Enemy]  
------------------------------------------------------
No. 04 |  190E0C  [Enemy]  
------------------------------------------------------
No. 05 |  190FCC  [Enemy]  
------------------------------------------------------
No. 06 |  19118C  [Enemy]  
------------------------------------------------------
No. 07 |  19134C  [Enemy]  
------------------------------------------------------
No. 08 |  19150C  [Enemy]  
------------------------------------------------------
No. 09 |  1916CC  [Enemy]  
------------------------------------------------------
No. 10 |  19188C  [Enemy]  
------------------------------------------------------
No. 11 |  191A4C  [Enemy]  
------------------------------------------------------
No. 12 |  191C0C  [Enemy]
------------------------------------------------------
No. 13 |  191DCC  [Enemy]  
------------------------------------------------------
No. 14 |  191F8C  [Enemy]  
------------------------------------------------------
No. 15 |  19214C  [Enemy]  
------------------------------------------------------
No. 16 |  19230C  [Enemy]  
------------------------------------------------------


In order to get the unit's Monster Talk byte, we have to add the unit offset from the table above and the data offset from Formula Hacking.

How do we know which Ally our unit is? You can either set your units up properly on the pre-battle screen, using the grid in Xifanie's ASM tutorial or the FFT Gameshark Handbook, or look for the one whose data matches the unit you're playing with. I prefer to just use Ramza and look for the ally whose very first byte is "01" for Chapter 1 Ramza. (If you're in a later chapter, it'll be "02" or "03".)



How do we know which support we want to check? After all, Monster Talk can be an innate ability or an equipped support ability. In the first hack I ever did, I checked the equipped support ability, but it turns out that we don't want to check either of these. The best place to go, and the place the game goes to check RSM, is bytes 0x8B - 0x95. These bytes always tell you exactly what RSM are active, whether innate, equipped, or hacked in. Monster Talk is part of byte 0x91, so that's the byte we want to set a breakpoint on.

Trying to add hexadecimal numbers in your head when you're starting out is asking for trouble. Just take your unit offset [1924cc] and data offset [91] and add them together in Windows Calculator's Hex mode [19255D]. That's where our breakpoint is going.

Step 2B: Seriously, Set Up A Breakpoint

Now that you are where you want to be, open the debugger by selecting Debug > Monitor > r3000.

To actually set a breakpoint, you need to right-click and Add in the Breakpoint pane of the debugger. I keep this pane in the lower right.





Make sure it's set to Read/Write, not Execute. Write is irrelevant right now but generally a good idea to leave in, since you'll want to know if something is sneaking in and touching the data before it's retrieved.

You must enter your offset specifically as hex; type in "0x19255D", not "19255D".



At this point, you should have Ramza able to Act and your breakpoint in place - the trap is set. Switch back to the game window and spring it by choosing your Talk Skill...

Step 3: Search

When the game froze up, that was a good thing! Switch over to the debugger and you'll see the message "Breakpoint on memory read" at the bottom of the screen and a ">" cursor next to a line of code in the Disassembly window. This is the line after your breakpoint triggered. The code you should see is the following:

0x133bd4:  lbu r2, 0x0091(r20)
0x133bd8: >nop
0x133bdc:  andi r2, r2, 0x0002
0x133be0:  beq r2, r0, 0x00133bf4

This is a very common code formation - it's the simplest way to check a flag and then make a decision based on the check. In fact, if you don't know anything else about ASM, this is probably the best thing to remember.

lbu says to load the byte into register 2.
nop says nothing happens. (This is what the hex string "00000000" means.) This is there because lbu needs a minute to think; you can ignore it for now.
andi says to either set register 2 to 0x0002 if Two Hands is checked, or to zero if Two Hands is not checked.
bne says to ignore this line if register 2 is not zero (Two Hands), but jump ahead if register 2 is zero (no Two Hands).

You can tell that this is addressing Two Hands and not Monster Talk because we have the line

"andi r2, r2, 0x0002"
instead of
"andi r2, r2, 0x0010".

If you pull up the Formula Hacking page on the wiki, you will see that each of the eight flags on this byte has its own value to check.



These correspond to the eight bits in a byte, but right now you just need to know you're looking for 0x0010.

Since we don't want to mess with Two Hands right now, we'll skip this. Press F9 and switch to the other window and Ramza will be walking again.

Now move the cursor onto the target - you should get another freeze. Switch back to the debugger.

0x133bd4:  lbu r2, 0x0091(r20)
0x133bd8: >nop
0x133bdc:  andi r2, r2, 0x0002
0x133be0:  beq r2, r0, 0x00133bf4

The same exact line of code. Another false alarm, press F9 and switch back. This time, press X and select a monster to talk to.


Yeah, it does this sometimes when you have to stop at multiple breakpoints. It's a nuisance. I usually try to press F9 and immediately click on the play window to cancel the "ghost" directional input. You can also turn the cursor repeat speed down in FFT's options if it's becoming a real problem.



0x1899e0:  lbu r2, 0x0091(r2)
0x1899e4: >nop
0x1899e8:  andi r2, r2, 0x0010
0x1899ec:  bne r2, r0, 0x001899fc

Success! This has a check for Monster Talk.

Everything you need to worry about right now is in these four lines, so forget about the rest.

lbu says to load the byte into register 2.
nop says nothing happens. (This is what the hex string "00000000" means.) This is there because lbu needs a minute to think; you can ignore it for now.
andi says to either set register 2 to 0x0010 if Monster Talk is checked, or to zero if Monster Talk is not checked.
bne says to ignore this line if register 2 is zero (no Monster Talk), but jump ahead if register 2 is nonzero (yes Monster Talk).

Step 4: Destroy

0x1899e0:  lbu r2, 0x0091(r2)
0x1899e4: >nop
0x1899e8:  andi r2, r2, 0x0010
0x1899ec:  bne r2, r0, 0x001899fc

So if we can make r2 always equal to zero when we get to the bne, the game will never ever trigger the Monster Talk code.

How do we do that? Well, it's easy to break something, there are lots of ways.
Maybe the most obvious way is to change andi r2, r2, 0x0010 to andi r2, r2, 0x0000. That will make r2 always equal to zero.


You could also change either lbu or andi to

or r2, r0, r0
ori r2, r0, 0x0000
and r2, r2, r0
add r2, r0, r0
addi r2, r0, 0x0000

which are all other ways of saying r2 = 0.

Or you could change bne to bne r0, r0, 0x00198000, so that instead of skipping ahead when r2 != 0, it skips ahead when 0 != 0 - never.


You can go ahead and enter this right now. Click on the Memory pane of the debugger and press CTRL+G, then enter "0x1899e8" - that's where the line of code we want to change is stored. Click on that "10" and type in "00". (It's stored as 1000 instead of 0010 because numbers are stored smallest-to-largest instead of largest-to-smallest.)

Be careful you type exactly two zeroes. Your entries will get screwed up fast if you just type one zero, then click away because it says "00" just like you want. Always type the full two-digit hex byte in.

Now if you click somewhere on the Disassembly window, you should see the line change to "andi r2, r2, 0x0000" to reflect your changes. You just made the hack!

Step 5: Verify and Share

To test your hack, delete your breakpoint (highlight it and press Delete) and press F9 to restart game flow. You should see a 00% success rate. Congratulations, you have made an almost useless skill completely useless!

How do you duplicate your hack? The hack is at RAM location 0x1899e8. Most RAM locations in battle are converted to BATTLE.BIN locations by subtracting 0x67000. So get out the Windows Calculator again and do the subtraction - this hack is at 0x1229e8.


BATTLE_BIN
0x1229e8
00


ASM Tutorial #1: Modifying Code & Reading ASM (to come)
  • Modding version: PSX

Choto

Well written Pokeytax, thanks for making a dummy-friendly ASM tutorial!

Pylgrim


Joseph Strife

This is a "must read" for everyone new. Great job Pokeytax :)
Gaffgarion: It's in the contract!
Ramza: Does your contract says: "When you find a former squire, that now is a Holy knight that has kidnapped a princess, in a bridge by a waterfall fighting a brigade you are supposed to kill everybody that helps him!"
Gaffgarion: ... Sure!
Ramza: ... Let me see your contract...
Gaffgarion: ... No...


Aqueous

That was really useful, thank you. Every step was covered, at no point did I feel lost :)