• Welcome to Final Fantasy Hacktics. Please login or sign up.
 
January 19, 2021, 03:33:45 am

News:

Don't be hasty to start your own mod; all our FFT modding projects are greatly understaffed. Find out how you can help in the Recruitment section.


ASM understanding

Started by Orkney, January 10, 2021, 04:01:49 pm

Orkney

Hi,

I'm trying to write some ASM formulas, and i run into this code (in formula 15)

001891d0: 3402007f ori r2,r0,0x007f
001891d4: 3c038019 lui r3,0x8019
001891d8: 8c632d90 lw r3,0x2d90(r3) r3 = target current action data pointer
001891dc: 00000000 nop
001891e0: a0620013 sb r2,0x0013(r3)  Set target CT to -127.
001891e4: 3c038019 lui r3,0x8019
001891e8: 8c632d90 lw r3,0x2d90(r3)
001891ec: 34020001 ori r2,r0,0x0001
001891f0: a0620025 sb r2,0x0025(r3) 

I understand what it does, but i'm wondering, why there is two sequences ending to lw r3, 0x2d90(r3) ? r3 is not modified by the command sb r2 0x0013(r3) does it ?

Probably a dumb question...
  • Modding version: PSX

nitwit

1. Check if something else branches to the addresses from 1891d0 to 1891dc by searching the wiki for those values
2. Try nop-ing (zero out 4 bytes of an instruction) the first and second instance and see what happens. Do it twice: first time just see if the skill seems to function, second time look at registers and step through it instruction by instruction to see what's happening internally.

Maybe one of them is redundant? *shrugs*
  • Modding version: PSX

Glain

It's just redundant.

This happens a lot in the vanilla code, where it's like the compiler generated code to do one thing (in this case, set the action's CT), then generated code to do another thing (set the action's type), and just mashed them together without taking into account that it could have just reused the value in r3 instead of reloading the action pointer.

(Vanilla code isn't going to branch into the middle of the routine, and the only branches inside the routine skip to the end of the routine, so those two bits of code will always be run in sequence.)
  • Modding version: Other/Unknown

nitwit

Quote from: Glain on January 11, 2021, 09:19:21 amIt's just redundant.

This happens a lot in the vanilla code, where it's like the compiler generated code to do one thing (in this case, set the action's CT), then generated code to do another thing (set the action's type), and just mashed them together without taking into account that it could have just reused the value in r3 instead of reloading the action pointer.

(Vanilla code isn't going to branch into the middle of the routine, and the only branches inside the routine skip to the end of the routine, so those two bits of code will always be run in sequence.)
1. Do you have any additional advice for spotting redundant code?

2. Is it safe to jump into the middle of another subroutine? Is it advisable (seems no to me, harder to track and debug, almost like a goto)?
  • Modding version: PSX

Glain

1.  Just off the top of my head, a bunch of code can be made more efficient by keeping values stored in registers instead of reloading or recalculating them, and re-ordering code to remove nops can help quite a bit as well.  Getting a good understanding of what the code is doing in each case gives a good idea of which parts of it seem to be unnecessary.

2.  Generally speaking, it's absolutely not safe, and it should be assumed that a crash would result.  This will usually corrupt the stack pointer and saved register values, unless you've accounted for that.  It can be made to work in very specialized cases with the right (hacky) setup.
  • Modding version: Other/Unknown

nitwit

Quote from: Glain on January 12, 2021, 09:24:27 am2.  Generally speaking, it's absolutely not safe, and it should be assumed that a crash would result.  This will usually corrupt the stack pointer and saved register values, unless you've accounted for that.  It can be made to work in very specialized cases with the right (hacky) setup.
IIRC some of the formulas - I think the stat boosts for Yell and Accumulate - lack a closing jr and nop, and are actually sequential. Are they actually part of a larger routine, or referenced in a pointer table somewhere?

https://ffhacktics.com/wiki/3A_%2BBrave_(Y)
00186d00: 3c028019 lui r2,0x8019         
00186d04: 904238fa lbu r2,0x38fa(r2)     
00186d08: 3c038019 lui r3,0x8019         
00186d0c: 8c632d90 lw r3,0x2d90(r3)     
00186d10: 34420080 ori r2,r2,0x0080     
00186d14: a0620016 sb r2,0x0016(r3)      ; Store Y as 0x80 + Br decrement (boost)
00186d18: 3c038019 lui r3,0x8019         
00186d1c: 8c632d90 lw r3,0x2d90(r3)     
00186d20: 34020001 ori r2,r0,0x0001     
00186d24: 03e00008 jr r31               
00186d28: a0620025 sb r2,0x0025(r3)      ; Store attack type as Psuedo-Status

https://ffhacktics.com/wiki/39_%2BSP_(Y)
00186d2c: 3c028019 lui r2,0x8019
00186d30: 904238fa lbu r2,0x38fa(r2)
00186d34: 3c038019 lui r3,0x8019
00186d38: 8c632d90 lw r3,0x2d90(r3)
00186d3c: 34420080 ori r2,r2,0x0080
00186d40: a0620012 sb r2,0x0012(r3)      Store Y as 0x80 + SP decrement (boost)
00186d44: 3c038019 lui r3,0x8019
00186d48: 8c632d90 lw r3,0x2d90(r3)
00186d4c: 34020001 ori r2,r0,0x0001
00186d50: 03e00008 jr r31
00186d54: a0620025 sb r2,0x0025(r3)      Store attack type as Pseudo-Status

Huh, I didn't misremember.

1. Given that there's a lot of redundancy here, how much space could one save by consolidating these and similar ones and using one of the unknown skill bytes as bit flags to specifiy effects?

2. Do you know if the AI use #1 correctly without modification, or must I investigate that too?
  • Modding version: PSX

Glain

They're two separate routines that happen to be placed next to each other.  They both have a closing jr (at 0x186d24 and 0x186d50, respectively).  Neither one has a nop, but what's the relevance of that?  They are referenced in the formula pointer table, of course...

1.  I'm unaware of unknown skill bytes.  If you wanted to consolidate the routines into one by, say, providing the action offset as an argument, it looks like you could save some space.  It doesn't seem like anything amazing though, unless there are a bunch more routines like this.

2.  Are you asking if the AI would use Cheer Up?  I haven't tested that, although I can't say I remember enemy mediators using Praise very often...
  • Modding version: Other/Unknown

nitwit

Quote from: Glain on January 12, 2021, 06:00:40 pmThey're two separate routines that happen to be placed next to each other.  They both have a closing jr (at 0x186d24 and 0x186d50, respectively).  Neither one has a nop, but what's the relevance of that?  They are referenced in the formula pointer table, of course...
Ah, I glossed over it and missed the jr's because I expected nop's after them. My bad.

Quote from: Glain on January 12, 2021, 06:00:40 pm1.  I'm unaware of unknown skill bytes.  If you wanted to consolidate the routines into one by, say, providing the action offset as an argument, it looks like you could save some space.  It doesn't seem like anything amazing though, unless there are a bunch more routines like this.
Another instance of "not checking before I speak" syndrome, I was thinking of items. Looking at skills there isn't much free room, maybe one or two bits each that one can hack from the range, AoE, vertical, CT, MP cost, and formula bytes.

Quote from: Glain on January 12, 2021, 06:00:40 pm2.  Are you asking if the AI would use Cheer Up?  I haven't tested that, although I can't say I remember enemy mediators using Praise very often...
I think I saw an enemy mediator use Preach once, but I'm probably mistaken and the brave/faith modification skills seem low priority... but I was asking if the AI would use a skill that consolidates PA/MA/SP/CT/Brave/Faith changes into a new formula that uses bits in the assorted skill bytes to determine the stat affected.

I assume it needs additional changes to the AI routines to work, or a flag consolidation to specify the stat affected for both the skill effects and for the AI to consider.

Given that a lot of skills wouldn't do anything with those flags, it might make more sense to consider a set of flags as parameters to a formula, whose context varies depending on the formula. The things that never change between all formulas would get/retain their own flags whose meaning never varies no matter the formula used, and those that do change between most formulas would be moved to the parameter flags and their specific meaning would vary depending on the formula used.

Dedicating a byte or two to parameter flags and putting them in a position where they can be loaded with the fewest instructions might make a formula de-harcoding and consolidation project easier, if those flags would get checked the most often in formula routines.

If that's not clear, for a game I'm currently working on, byte 0x0c of skill data is the formula, and bytes 0x01 to 0x04 mean any of a number of things depending on the formula. Therefore bytes 0x01 to 0x04 are parameters to byte 0x0c, when you think about it.

Something like that.

Random Question: are some of the tables for skills 0x170 and above smaller than those above them? I just checked the gameshark tab after changing the item used bytes for Potion and Hi-Potion and those bytes are sequential. Seems to indicate that 0x170 and above don't have the full range of bytes under the "Attributes" heading as skills 0x000 to 0x16f.

I ask because I'll need to make data-editing spreadsheets that generate paste-able hex tables (the FFTPatcher tables, in base 16 form in the manner they exist in the game) if I want to complete my master plan RE a commented disassembly. If those skills don't have entries in an "Skill Attributes" table then I need to account for that. I can figure it out on my own if you are busy, I'm just asking in case you already know and can save me some time later.

If those skills have entries in a "Skill Attributes" table, and you have hacks that can reconfigure them to function like other skills, you should consider making those bytes editable in FFTPatcher if it's not a hassle.
  • Modding version: PSX