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

ASM Help

Started by Aqueous, March 22, 2015, 04:31:04 pm

Aqueous

March 22, 2015, 04:31:04 pm Last Edit: March 22, 2015, 04:37:59 pm by Aqueous
Hi All,

I'm trying to learn some of this but have a couple of small questions at the moment that are stumping me and it'd be great if someone could help :).

For example, I would like to use a hack posted on here so that people regenerate 10% of their MP per turn. However, I personally find 10% a bit too high so I'd like to tweak it to 5%.

Now, I would've hoped this would be as simple as taking the existing code and inserting a couple of lines to simply divide where "10%" is stored by 2. However, there's a couple of issues I'm not sure about.

This is the hack at present:

lui r3,0xcccc
lhu r2,0x002e(r16)
ori r3,r3,0xcccd
multu r2,r3
mfhi r3
srl r3,r3,0x03
sh r3,0x0196(r16)
lbu r2,0x0024(r16)
ori r3,r0,0x0010
sb r3,0x01b1(r16)
jr r31
nop

So my two problems:

1. Can I even use constants? If so, I could just add divu r3,0x02 -> mfhi r3?
2. If constants can be used, how do I tell the difference between an offset being referenced or a constant?

For example, on line 1 that looks like a value is being loaded from an offset since a constant of 'cccc' would be way too high, surely? But then with SRL it appears to be a constant of $03.

For now, I've just changed it to look at Current MP rather than Max MP so at least it discourages spamming spells.

Thanks in advance.

Xifanie

That is... some strange code...

Pretty sure instead of:
lui r3,0xcccc
lhu r2,0x002e(r16)
ori r3,r3,0xcccd
multu r2,r3
mfhi r3
srl r3,r3,0x03

This would achieve the same thing:
lhu r2,0x002e(r16)
ori r3, r0, 0x000A
divu r2, r3
mflo 3

You could also add this if there was enough space (I doubt there is, since this would take 1 extra line) to round up if the number is uneven:
lhu r2,0x002e(r16)
ori r3, r0, 0x000A
divu r2, r3
mfhi r2
beq r2, r0, 0x0008
mflo r3
addiu r3, r3, 0x0001

Of course, there are many ways to go at it.

Multiplying a value by 0xCCCCCCCD means only 80% of the original value makes it into the hi register.
It's basically the same way as doing Value * 4 / 5. Which could be translated as:
lhu r2,0x002e(r16)
ori r3, 0x0005
sll r2, r2, 0x0002
divu r2, r3
mflo r3
(which then gets divided by 8 with srl to get 10%)

Which isn't more or less effective AFAIK.

If you just want to change 10% to 5%...
Change srl r3,r3,0x03 to srl r3,r3,0x04
So your 80% / 8 = 10% becomes 80% / 16 = 5%
  • Modding version: PSX
Love what you're seeing? https://supportus.ffhacktics.com/ 💜 it's really appreciated

Anything is possible as long as it is within the hardware's limits. (ie. disc space, RAM, Video RAM, processor, etc.)
<R999> My target market is not FFT mod players
<Raijinili> remember that? it was awful

Aqueous

Thank you Xif - for now I'll just change the SRL to 0x04 - for some reason I thought that would make it divide by an additional 10 because I was looking at the binary examples in the tutorial on the website but completely forgot: duh, it's binary.

I had a question though (sorry!)

I think this is the source of my confusino: I'm still a little confused by the ori operation. I've read it as basically being an or operation but I've never encountered "or" outside of a condition of some kind. If I say ori r3, r3, 0xcccd - what exactly is that doing? Load into r3, r3 or 0xcccd? How does it decide which? The highest? Since you said later on in your post about 0xcccccccd, that seems like it's sticking r3 and 0xcccd together and storing that in r3?

So when you do ori r3, r0, 0x000a is that setting r3 to 0x000A(10) then you're dividing the Max MP / 10? If so, yeah that makes a lot more sense to me and I'm wondering why the values go so high in the original code for something as mundane as dividing by 10.

Pride

ori can be used to set a register to a specific value. Thats why you see something like ori r2,r0,0x????. So when the immediate is compared to 0, the value stored into the register has to be the immediate.

The main culprit of the weird coding has to be the compiler that was used.
  • Modding version: PSX
Check out my ASM thread. Who doesn't like hax?

Choto

March 23, 2015, 10:11:43 pm #4 Last Edit: March 23, 2015, 10:17:14 pm by Choto
OR and AND commands can be interpreted mathematically to shed some light on how they work. take a look at the following tables. The first is for OR and the second is for AND.





anything OR'd with 0 is itself. so that ori r3, r0, 0x000A is basically saying "r3 = 0x0A OR'd with Zero", and so it becomes r3 = 0x0A.

when it's used as ori r3, r3, 0xcccd, it's adding 0x0000cccd to whatever r3 already is. So:

lui r3, 0xcccc -        r3 = 0xcccc0000
ori r3, r3, 0xcccd -   r3 = 0xcccccccd

I think this type of usage has to do with floating point multiplication and division. Decimal values are stored in a wierd way in ASM.... but honestly I've always been able to circumvent that usage by doing exactly what Xif mentioned. So it's not something you even have to understand unless the need arises.

Don't worry, this is the hardest ASM will ever be. Only gets easier.

Also, I saw your other comment. Do you have the psX emulator with a debugger? You can set a "breakpoint" in it at any point in the code and the game will freeze when it hits it. Then you can step command by command and view exactly what's in the registers. Then you can reference the addresses that are used in the registers and codes in the "Data Locations.txt" file which holds pretty much all the data we've found out (which is quite a substantial amount at this point).

Check here: http://ffhacktics.com/smf/index.php?topic=9608.0

Aqueous

Thanks for the help guys, think I've got it now.

Choto: Thanks for the link to those files, I've already got the tools you attached but the reference sheet is really helpful.

As for using psX debugger, I have indeed tried doing that as indicated in the tutorials. My main problem at the moment is working out what to set as a breakpoint and when said breakpoint is going to be referenced. For example, in Xif's tutorial on the website, she knows exactly what she needs to set as a breakpoint and when it's going to be referenced so everything goes swimmingly. However, without knowing you can often miss it completely and for a beginner it makes you think you're just doing it wrong.

I tried to see when the game was checking one of the conditions from RAD's code so I put a breakpoint on the part that checks if a character has a Monk JLvl of X but could never get it to trigger. First I tried to see if it triggered when accessing Formation Menu, then Job Wheel, then Job Change, then I thought maybe it triggers when JP changes so I gained JP on my Monk in battle but nothing etc. I was probably selecting the wrong byte but could've also been doing it wrong.

I'll keep trudging along though. For now I think I'll look at just editing some formulas because there's a bit of an overlap with some of them.

Choto

One method you could use is looking at where the jumps in the hack are. Large routines are in free space but at some point you have to jump to get there. So you look at where the jumps occur in vanilla code, then check that routine on the wiki to find out what the routine is doing at the time. I'll take a look at RAD later and see if I can point out a good example

Aqueous

Great idea, thank you.

I'm guessing the jumps are mostly happening in SCUS so the bits of code inserted there are probably the jumps :)

Aqueous

I've having real issues with breakpoints in psX so any help would be greatly appreciated.

Generally I'm setting Execute breakpoints for lines of code that are being executed! Seems logical to me.... But the breakpoints aren't triggering, stuff is just continuing like normal. I posted in Jack of All Trades' Jump thread trying to turn Pride's hack into connecting the Jump skillset to another skillset and had the problem there originally, lines of code that -were- executing were not triggering a breakpoint.

Is there anything not obvious I should know about setting them?

Thank you.

Pride

Lets use my Jump hack since its what you mentioned.


addiu r29,r29,-0x0018
sw r31,0x0010(r29)
lbu r3,0x0092(r18)
nop
andi r3,r3,0x0002
beq r3,r0,0x00000024
addu r6,r17,r0
jal 0x0019a5f8
ori r5,r0,0x0034
lbu r3,0x0012(r18)
lw r31,0x0010(r29)
addiu r29,r29,0x0018
jr r31
nop


lbu r5,0x0092(r3)
nop
andi r2,r5,0x0002
beq r2,r0,0x00000058
nop
ori r2,r0,0x0034
sb r2,0x0000(r16)
addiu r16,r16,0x0001
ori r2,r0,0x00ff
sb r2,0x0000(r16)
jr r31
nop


So we want to set a memory breakpoint, specifically when the game checks the units 4th set of Support skills (byte x92) since that it what my hack checks. If you pull up your data locations.txt (which you have right??), you know the unit data starts at x1908cc (ID * 0x1c0 to get to the unit you want to find). If its player first unit, I know off hand that offset is x1924cc. Now add the byte location (x92) to the unit data location (x801924cc) and this will be your breakpoint (x8019255e).







At first the game continues to run after setting the break point. What would cause a the game to read my hack? Probably selecting the "Act" menu since the hack adds "Jump" to the "Act" menu. The game breaks on a first location, not my hack. So I run the game again (F9) and on the second break, it finds my hack. I'm now able to step through and go through it line by line.
  • Modding version: PSX
Check out my ASM thread. Who doesn't like hax?

Xifanie

You might be experiencing a processor instruction set architecture incompatibility. It is a known issue, along with another I forgot about (it tends to be one or the other). Basically, if it's the case with you, you're screwed. There IS a way to make it work though:

  • Force a break (Debug, Break)

  • In the disassembler window, ctrl+g (go) and type in the desired offset

  • Press F6 (Run to cursor)


At this point, if you interrupt the debugging process in any way, you have to start all over.

Tell me how that works. Yes it's annoying, but for me it's the only way I can use execute breakpoints, so I do use it.
  • Modding version: PSX
Love what you're seeing? https://supportus.ffhacktics.com/ 💜 it's really appreciated

Anything is possible as long as it is within the hardware's limits. (ie. disc space, RAM, Video RAM, processor, etc.)
<R999> My target market is not FFT mod players
<Raijinili> remember that? it was awful

Choto

Did you hit any breakpoints in the past ever?

Aqueous

Quote from: Pride on March 30, 2015, 12:10:13 pm
Lets use my Jump hack since its what you mentioned.

At first the game continues to run after setting the break point. What would cause a the game to read my hack? Probably selecting the "Act" menu since the hack adds "Jump" to the "Act" menu. The game breaks on a first location, not my hack. So I run the game again (F9) and on the second break, it finds my hack. I'm now able to step through and go through it line by line.


I've tried it mostly in execute lines so far rather than read/write but I will try with that too and see if it works properly. With execute it doesn't though, it just breezes past the line of code without breaking. I think Xif's suggestion is working though :)

Quote from: Xifanie on March 30, 2015, 03:03:01 pm
You might be experiencing a processor instruction set architecture incompatibility. It is a known issue, along with another I forgot about (it tends to be one or the other). Basically, if it's the case with you, you're screwed. There IS a way to make it work though:

  • Force a break (Debug, Break)

  • In the disassembler window, ctrl+g (go) and type in the desired offset

  • Press F6 (Run to cursor)


At this point, if you interrupt the debugging process in any way, you have to start all over.

Tell me how that works. Yes it's annoying, but for me it's the only way I can use execute breakpoints, so I do use it.


Thank you Xif, this seems to be working. It's creating a Breakpoint automatically this way then when I continue playing it triggers it. Not sure if that's how you intended for it to work but it seems to be doing it, at least it has on this occasion.

Quote from: Choto on March 30, 2015, 10:29:36 pm
Did you hit any breakpoints in the past ever?


Only ever when the game is loading up, I've never hit any whilst in-game before though....

Choto

probly not related to this case but in case it is: If you set a breakpoint on startup, it won't be there by the time you get to the situation that procs it. As the game goes through various states (world map screen, pre-battle, battle, etc.) different files are loaded into RAM and so, whatever is in RAM is overwritten (i.e. your breakpoint).

So if it is applicable, set your breakpoint right before you hit the situation you think will proc it.

Xifanie

Quote from: Aqueous on March 31, 2015, 02:46:02 pmIt's creating a Breakpoint automatically this way then when I continue playing it triggers it.


Yes, see, that's the weird thing.

Using this method because of your architecture incompatibility:
If you delete the breakpoint, it won't work anymore.
If you Debug/Run (F9), the breakpoint will stay in the list but it won't work anymore. You have to repeat the process all over again.
I'm pretty sure loading a quicksave will make the breakpoint not work either.

And of course manually adding an execute breakpoint to the list isn't doing crap in our case. It is VERY strange behaviour, but I'm glad I could inform you about it; it's not your fault, you're not stupid. You just have to handle that debugger differently than other people. I really wish the author of pSX would have fixed it, but it's never going to happen. So annoying. >_>
  • Modding version: PSX
Love what you're seeing? https://supportus.ffhacktics.com/ 💜 it's really appreciated

Anything is possible as long as it is within the hardware's limits. (ie. disc space, RAM, Video RAM, processor, etc.)
<R999> My target market is not FFT mod players
<Raijinili> remember that? it was awful

Aqueous

April 15, 2015, 08:05:24 am #15 Last Edit: April 15, 2015, 03:07:37 pm by Aqueous
Got a few hacks coming up that I thought I'd share. Some are tested, some aren't as of yet, I'll indicate which are or aren't. Please feel free to tell me if some of these have already been done, and I'll post the ASM for critique too. These are just things I've written for my own mod so don't know how useful they'd be to others.

First one:

Remove Hardcoding on Stat Reduction Formulas

Untested

Uses the Inflict Status ID to decide which stats to reduce. Enter the following into Inflict Status ID in FFTPatcher to reduce the following stats:
1 - SP
2 - PA
4 - MA
8 - Brave
16 - Faith

These are additive. So if you enter "3" into Inflict Status the ability should reduce SP and PA.

Two ways to apply it. First is replacing the existing code, second is using a jump to Kanji space (I will add this in a post edit later today)

Replacing Existing Code:

<Patch name="Remove Hardcode on Stat Reduction Formulas">
<Description>Uses Inflict Status ID to determine stats to reduce: 1 for SP, 2 for PA, 4 for MA, 8 for Brave, 16 for Faith - works additively (ie. Entering "3" will reduce SP and PA)
</Description>
<Location file="BATTLE_BIN" offset="120860">

0800A4AF
0C00A5AF
1980033C
F9386290
FB386490
7F004230
902D638C
01008530
0200A010
02008530
120062A0
0200A010
04008530
140062A0
0200A010
08008530
150062A0
0200A010
10008530
160062A0
0200A010
25006490
170062A0
0C00A58F
01008234
0800A48F
0800E003
250062A0

</Location>
</Patch>

ASM:


 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
 
   
   
   
   
 
RAM           Battle BIN     ASM                                 Comments
00187860120860sw r4,0x0008(r29)Free r4 for use
00187864120864sw r5,0x000c(r29)Free r5 for use
00187868120868lui r3,0x8019
0018786c12086Clbu r2,0x38f9(r3)Load Ability X
00187870120870lbu r4,0x38fb(r3)Load Inflict Status ID
00187874120874andi r2,r2,0x007fCap r2 at 127
00187878120878lw r3,0x2d90(r3)Load Current Action Data Pointer
0018787c12087Candi r5,r4,0x0001SP Check - (Check if Inflict Status ID includes 1)
00187880120880beq r5,r0,0x000018788cBranch to next
00187884120884andi r5,r4,0x0002PA Check - (Check if Inflict Status ID includes 2) - runs before branch above
00187888120888sb r2,0x0012(r3)Store X in -ve SP change
0018788c12088Cbeq r5,r0,0x0000187898
00187890120890andi r5,r4,0x0004MA Check - (Check if Inflict Status ID includes 4) - runs before branch above
00187894120894sb r2,0x0014(r3)Store X in -ve PA change
00187898120898beq r5,r0,0x00001878a4
0018789c12089Candi r5,r4,0x0008Brave Check - (Check if Inflict Status ID includes 8) - runs before branch above
001878a01208A0sb r2,0x0015(r3)Store X in -ve MA change
001878a41208A4beq r5,r0,0x00001878b0
001878a81208A8andi r5,r4,0x0010Faith Check - (Check if Inflict Status ID includes 8) - runs before branch above
001878ac1208ACsb r2,0x0016(r3)Store X in -ve Brave change
001878b01208B0beq r5,r0,0x00001878bc
001878b41208B4lbu r4,0x0025(r3)Load Attack type flag
001878b81208B8sb r2,0x0017(r3)Store X in -ve Faith change
001878bc1208BClw r5,0x000c(r29)Restore r5
001878c01208C0ori r2,r4,0x0001
001878c41208C4lw r4,0x0008(r29)Restore r4
001878c81208C8jr r31
001878cc1208CCsb r2,0x0025(r3)Store Attack Type Flag (psuedo status)


Since it's untested, might be additional code that enforces the hardcoding that I've missed. Or typos!

Xifanie

It is considered "bad practice" to use r1 for anything else than addresses. The game will never do that, so in only very exceptional cases should you need to store it and load it back later. The game nearly always reassigns a value to r1 right before using it to load/store a value to/from another register. We don't really know the consequences of using r1 for values rather than addresses, but we avoid doing so in case.

lbu r2,0x38f9(r3)
andi r2,r2,0x007f

This is a big no. Load opcodes require a delay opcode to allow properly loading the value.
Else what is going to happen? Let's say the value in r2 was 0x0008476A.
lbu r2,0x38f9(r3) <- Starts loading 0x04 from 0x801938F9
andi r2, r2, 0x007F <-  performs andi r2, 0x0008476A, 0x007F because the lbu wasn't done loading the value into the register.
This only applies to loading, not storing; so this affects lb/lbu/lh/lhu/lw. This is a console bug, and proper emulators will bug if you do that as well.


beq r4,r0,0x00001878bc
lbu r1,0x0025(r3)
sb r2,0x0017(r3)
ori r2,r1,0x0001

Same thing here. lbu r1,0x0025(r3) is in a branch's delay slot, and r1 will not be done loading if the branch returns true.

Worst case scenario, if you don't have anything "useful" to add to a load/branch/jump delay slot, just put a nop there. Yes this will lengthen your hack, but this is necessary if you want your hack to work on all consoles/emulators.

I'd suggest reading or re-reading the Coding Standards Sticky topic.
  • Modding version: PSX
Love what you're seeing? https://supportus.ffhacktics.com/ 💜 it's really appreciated

Anything is possible as long as it is within the hardware's limits. (ie. disc space, RAM, Video RAM, processor, etc.)
<R999> My target market is not FFT mod players
<Raijinili> remember that? it was awful

Aqueous

April 15, 2015, 09:37:41 am #17 Last Edit: April 15, 2015, 10:12:31 am by Aqueous
Hi Xif, thanks for the feedback, I didn't realise loading also required a delay. It's no worries, like you alluded to I was just trying to save space...especially when writing hacks that use Kanji space since it seems quite coveted.

I'll change the register used and will either stick a nop behind or change the order in some cases so that by the time a register needs to be used it's finished loading. I also usually don't bother storing existing register values and then reloading them but in this case I'm concerned that when the routine is called, there are values in those registers already in some cases that would then be used by the next routine.

Edit - how about this:

sw r4,0x0008(r29)
sw r5,0x000c(r29)
lui r3,0x8019
lbu r2,0x38f9(r3)
lbu r4,0x38fb(r3)
- Swapped these around
andi r2,r2,0x007f
lw r3,0x2d90(r3)
andi r5,r4,0x0001
beq r5,r0,0x000018788c
andi r5,r4,0x0002
sb r2,0x0012(r3)
beq r5,r0,0x0000187898
andi r5,r4,0x0004
sb r2,0x0014(r3)
beq r5,r0,0x00001878a4
andi r5,r4,0x0008
sb r2,0x0015(r3)
beq r5,r0,0x00001878b0
andi r5,r4,0x0010
sb r2,0x0016(r3)
beq r5,r0,0x00001878bc
lbu r4,0x0025(r3)
sb r2,0x0017(r3)
lw r5,0x000c(r29)
ori r2,r4,0x0001
- Swapped around
sb r2,0x0025(r3)
lw r4,0x0008(r29) - Moved the load before the jump (just in case something needs it immediately in the next routine)
jr r31
nop

I rejigged the order so in all cases of loading from memory there's at least one line before that register is used. Updated my previous post.

Xifanie

Well, I haven't tested your hack or anything, so I cannot say if it will interfere with anything else. We all sometimes make the mistake to overwrite a register that will be used later on without saving/loading it.
r29 is the stack offset, so, you really want to make sure you're not overwriting data that was saved there before it is used. I'm just not seeing any addiu r29, r29, 0xFFC0 with addiu r29, r29, 0x0040 or the like, so it does catch my eye...

As far as the code you just posted is concerned, if you doubled checked, you'd have noticed you don't even need that last nop, you have just move the sb r2,0x0025(r3) there instead (unless I'm missing something, of course). Otherwise this is looking fine to me.
  • Modding version: PSX
Love what you're seeing? https://supportus.ffhacktics.com/ 💜 it's really appreciated

Anything is possible as long as it is within the hardware's limits. (ie. disc space, RAM, Video RAM, processor, etc.)
<R999> My target market is not FFT mod players
<Raijinili> remember that? it was awful

Aqueous

I'll check into whether any data is being overwritten tonight once I can load up the game.

Thank you for all the help Xif.