Invincible Guardian

If it has bytes, we can change it. Talk about anything relating to hacking and emulation here.

Moderators: General Moderator, Game Moderator

Re: Invincible Guardian

Postby Drakkhen » Fri Feb 24, 2012 3:00 am

ff3pdof24.zip Obsoleted

Here's a version that stays 24-bit until the end of the function.

Didn't test it very thoroughly, but it seemed to be working. Let me know if you find any problems.
Last edited by Drakkhen on Mon Jul 08, 2013 11:02 am, edited 1 time in total.
Drakkhen
Active User
Active User
 
Posts: 71
Joined: Thu Sep 09, 2004 9:06 am
Location: Dry Land, Earth

Re: Invincible Guardian

Postby Assassin » Fri Feb 24, 2012 11:02 am

impressive! my super-robust version of the patch takes about twice as many free space bytes as that (mid-to-high 90s versus your 48). you optimize like a mofo, seriously.

a few things:

1)
Code: Select all
C2/2C13: 18            CLC
C2/2C14: E6 E8         INC $E8
C2/2C16: 69 00 00      ADC #$0000


here you're trying to make any overflow from the INCrement carry into the top word? from the documentation i've seen, INC doesn't affect the carry flag. so you'll want to go with this:

Code: Select all
INC $E8
BNE skip    (branch if bottom word didn't wrap past 65535)
INC         (if it did, carry into the the top word)
skip:


i think that works.

------

2) you do a few instances of instructions that can move some of $EB into $EA (i.e. "LSR $EA"). but $EB could hold gibberish leading into this, which'll pollute $EA. so you'll want to zero $EB at some point earlier. my inclination is after returning from the last $47B7 call. but then again, C2/47B7 doesn't actually touch $EB, from what i can tell, so that's partly superstition. :/ thus, it'll be better for code space to zero it before the "REP #$20" at C2/2BE7.

------

3) minor correction, and an optimization chance.

Code: Select all
Caps damage at 65535 and exits the function

C2/679D: A5 EA         LDA $EA       (High byte of damage)
C2/679F: 29 FF 00      AND #$00FF    (Did damage overflow into 3rd byte?)
C2/67A2: D0 08         BNE $67AC     (If so branch)
C2/67A4: A5 E8         LDA $E8       (Lower 2 bytes of damage)
C2/67A6: 80 02         BRA $67AA
C2/67A8: 7B            TDC
C2/67A9: 3A            DEC           (Damage = 0xFFFF)
C2/67AA: 8D B0 11      STA $11B0
C2/67AD: 28            PLP
C2/67AE: 60            RTS           (Goodbye)

the first BNE needs to be adjusted, as it currently branches into the middle of an instruction. i think you want it to go to $67A8.

here's a slight optimization:
Code: Select all
Caps damage at 65535 and exits the function

C2/679D: A5 EA         LDA $EA       (High bytes of damage)
C2/679F: C9 01 00      CMP #$0001    (Did damage overflow into 3rd byte?)
C2/67A2: A5 E8         LDA $E8       (Lower 2 bytes of damage)
C2/67A4: 90 02         BCC $67A8     (Branch if damage didn't use top bytes)
C2/67A6: 7B            TDC
C2/67A7: 3A            DEC           (Damage = 0xFFFF)
C2/67A8: 8D B0 11      STA $11B0
C2/67AB: 28            PLP
C2/67AC: 60            RTS           (Goodbye)

by making the Carry Flag hold the result of our test, this allows us to save 2 bytes via a technique that Imzogelmo and I call "Removing the BRA". :D but do note that this version of the function is reliant on zeroing $EB with Recommendation #2 above (which should be done regardless).

-------

4)
Code: Select all
C2/6796: 08            PHP
C2/6797: A5 EA         LDA $EA
C2/6799: 0A            ASL
C2/679A: 28            PLP
C2/679B: 80 EB         BRA $6788

i'm wary of this. what you're trying to do is add the original $EA to the halved $EA, right? but you can't assume that ($EA DIV 2) * 2 == original $EA. i'm not 100% sure this code is flawed (what's your analysis?), but i'd favor:

Code: Select all
Multiplies damage by 1.5

LDA $EA
PHA
LSR $EA
LDA $E8
ROR
ADC $E8
STA $E8
PLA
BRA $6788

and that actually saves 1 byte, assuming it works.
Last edited by Assassin on Fri Feb 24, 2012 11:47 am, edited 3 times in total.
Reason: changed "CMP #$0100" to "CMP #$0001" under #3.
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Drakkhen » Fri Feb 24, 2012 9:07 pm

Corrected on all accounts, along with some improvements of my own. ZIP file updated accordingly.
Drakkhen
Active User
Active User
 
Posts: 71
Joined: Thu Sep 09, 2004 9:06 am
Location: Dry Land, Earth

Re: Invincible Guardian

Postby Rainflush » Fri Feb 24, 2012 11:27 pm

Um, so if the patch "assumes a headerless ROM", does that mean it would be incompatible with a headered ROM?
User avatar
Rainflush
Regular User
Regular User
 
Posts: 118
Joined: Wed Apr 28, 2010 2:54 pm

Re: Invincible Guardian

Postby Assassin » Sat Feb 25, 2012 1:10 am

that's correct. but you can always remove or add back the header with SnesTool (version 1.2 is available on Zophar.net, iirc).
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Rainflush » Sat Feb 25, 2012 4:42 am

Hm, all right then, are there any potential risks involved in altering the ROM this way? What's the intention of the header anyway?
User avatar
Rainflush
Regular User
Regular User
 
Posts: 118
Joined: Wed Apr 28, 2010 2:54 pm

Re: Invincible Guardian

Postby Assassin » Sat Feb 25, 2012 11:26 am

none that i know of. but make a backup of your ROM anyway. it only requires 3+ MB, and it nullifies any risk.

copiers add the header when generating a ROM image from the cartridge. i'm not sure why they do it. if the ROM dump needs multiple images, the header can indicate that the resulting images are part of a series. also, the cartridge size data isn't very specific, only having sizes of 2 Megabits, 4, 8, 16, and 32 (and maybe another one for giant games?). if your ROM is 24 Megabits as FF3us is, it just gets reported as 32 in the data. in contrast, the header data is far more specific about the ROM image size, which might help the copier load the game faster than it would if it had to recalculate the size every time?
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Rainflush » Sat Feb 25, 2012 6:47 pm

I see, well I'll give it a shot then, thank you.
User avatar
Rainflush
Regular User
Regular User
 
Posts: 118
Joined: Wed Apr 28, 2010 2:54 pm

Re: Invincible Guardian

Postby Assassin » Sat Feb 25, 2012 10:34 pm

are you planning on distributing both the 16-bit and 24-bit versions of the patch? i think it's worthwhile.
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Assassin » Tue Feb 28, 2012 7:16 pm

from the 16-bit patch (for the Genji Glove * 3/4):

Code: Select all
C2/2C13: AD B0 11      LDA $11B0
C2/2C16: 4A            LSR
C2/2C17: 6D B0 11      ADC $11B0
C2/2C1A: 1A            INC
C2/2C1B: 6A            ROR
C2/2C1C: 8D B0 11      STA $11B0
C2/2C1F: 28            PLP
C2/2C20: 60            RTS


a small blind spot: if that code is executed with the damage in A being 43690 to start, we'll get A = 65535 right before the INC, and 0 after the INC and ROR, which is bad.

EDIT: moving the INC right before "ADC $11B0" should fix things.
Last edited by Assassin on Tue Feb 28, 2012 9:04 pm, edited 1 time in total.
Reason: provided likely remedy
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Drakkhen » Wed Feb 29, 2012 2:10 pm

ff3pdof16.zip Obsoleted

New 16-bit version reduces free space usage to -1 and fixes the aforementioned bug.
Last edited by Drakkhen on Mon Jul 08, 2013 11:04 am, edited 1 time in total.
Drakkhen
Active User
Active User
 
Posts: 71
Joined: Thu Sep 09, 2004 9:06 am
Location: Dry Land, Earth

Re: Invincible Guardian

Postby Assassin » Wed Feb 29, 2012 2:55 pm

-1? that's some sharp optimization! :)

but what i liked about prior versions of the 16-bit patch is that if (Attack * Level * Level) used 4 bytes, the call to C2/6780 would harmlessly cap it down to three bytes (or rather, cap the DIV 256 of that value to 2 bytes).

the new version no longer fully does that. if the result of the third multiply would enter a 3rd byte after adding (result of second multiply / 256) to it, your code catches that. but if the result of the third multiply already occupied 3 bytes, it's not caught, and things will be flubbed.

now admittedly, a 32-bit (Attack * Level * Level) isn't possible outside of a RAM (or ROM) hack that gooses level past 99 (or finds a way to jack up another stat). still, since the fix by necessity sets out to make a more robust function than what Square had, i've also favored making it robust enough to handle a simple stat hack.

going sans free space is always nice, but imho, free space isn't at enough of a premium to sacrifice flexibility in dealing with a semi-plausible scenario. i know that the initial version of my patch just assumed that (Attack * Level * Level) would never exceed 24-bits. but it didn't sit well with me. which is part of the reason i put off finishing the thing.
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Assassin » Wed Feb 29, 2012 3:04 pm

the "robust again" version i'm envisioning would have a "CMP #$0100" between the second "JSR $47B7" and "PLA". and a "BCS do_TDC_DEC_capping" at the start of the "Add A to $EB with damage overflow check" function (which would obviously be skipped by the second caller of that function). admittedly, that'd bring the -1 free space usage to at least +4, probably like +8.
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Drakkhen » Wed Feb 29, 2012 9:56 pm

Well, I just barely managed to free up enough space for the necessary code changes. So I sure hope there are no more problems.

ZIP file updated.
Drakkhen
Active User
Active User
 
Posts: 71
Joined: Thu Sep 09, 2004 9:06 am
Location: Dry Land, Earth

Re: Invincible Guardian

Postby Assassin » Wed Feb 29, 2012 10:31 pm

lol, they should name a compression algorithm after your work. perhaps The Black Hole? or somebody could publish the entire works of Shakespeare on a single page. and on that page, there would only be one word: "Drakkhen".

people would scoff in disbelief, until reading the name outloud a second and third time invoked the decompression process before their very eyes...
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Assassin » Mon Mar 05, 2012 11:24 pm

regarding the 16-bit version: a couple minor issues with the documentation portion:
- "Add A to $EB with damage overflow check" and "(Store damage in $EB)" should have $E8 instead.
- "(Branch if no Genji Glove)" and "(Branch if no Offering)" are switched.

-----------

as for the 24-bit code:

you can save 2 bytes at the end of the "Caps damage at 65535 and exits the function" routine with a "JMP $2B97". though i dunno how offput you are about doing two separate jumps before finally reaching an RTS. :/

EDIT: and another 3 by getting rid of the silly JMP at the beginning of the C2/2B9D function, like you did in the 16-bit patch.

----------

both the 24-bit code and the 16-bit code will have an issue with the "ADC $11AE" when Gauntlet is not equipped. namely, the result will end up 1 too high, because Carry is set going into the add, as a result of your Gauntlet test. the original game manages to avoid this without an explicit CLC because Carry will always be clear going into C2/2BBA. however, you're probably going to need a "CLC" before the "ADC $11AE". not an issue in the 24-bit version, which has a NOP ripe for the taking, and consumes extra space regardless. however, the 16-bit version has no NOP, and is on the precipice.

....can he pull another rabbit out of a hat?!?
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Assassin » Tue Mar 06, 2012 4:21 am

you've done enough mad optimizing, so i'll give this round a try:

http://home.comcast.net/~assassin17/dra ... ry-opt.txt

the instructions without addresses in front of them are added. the lines with "--" in front of them are subtracted. the lines with "**" in front of them have been modified. my code drops 11 bytes, and adds 10 bytes, leaving 1 byte to spare. note that i did NOT adjust branches as needed, nor change any instruction addresses, so most of those in the file are now wrong.

anyway, it gets better. Lenophis pointed out to me in late January that because the Runic function (C2/352B) called shortly before C2/2B9D doesn't preserve Y, we don't need to either. 2 more bytes saved.

there's also a gimmick involving Y that can let you save 1 more byte (unaltered snippet follows):

Code: Select all
C2/2BDF: EB            XBA           (Low A = result of second multiply / 256)
C2/2BE0: 48            PHA           (Store above result)
C2/2BE1: C2 20         REP #$20      (Set 16-bit Accumulator)
C2/2BE3: A5 E9         LDA $E9       (Get upper two bytes of first multiply)
C2/2BE5: 20 B7 47      JSR $47B7     (Multiply those two bytes by level)
C2/2BE8: C9 00 01      CMP #$0100    (Check for overflow)
C2/2BEB: 68            PLA           (Restore result from second multiply)


replace the PHA/PLA with TAY/TYA. per one piece of 65816 documentation (which i can provide if needed), doing a TAY when the Index registers are in 8-bit mode and the Accumulator is in 16-bit mode will auto-fill the top half of A with 0. that'll let us drop the first "PHA" in the function (but you've got to keep the TDC).

for some reason i find using this unseemly, but if it's documented, and none of the emulator authors are stuck in an era where this nuance eludes them (i'm guessing that would've been 1995 or earlier), it should be safe. eh.

EDIT: okay, the code i linked to won't run properly because i got the stack ordering wrong. so we have to get rid of the first PHA in the function by either:
1) the above suggestion.
2) changing the above snippet so there's a "LDA #$00" before the XBA, and the "PHA" is after the "REP #$20". that uses 2 more bytes than #1, but should just make it inline.
/EDIT

so somewhere between break-even and 4 bytes to spare, depending.
Last edited by Assassin on Tue Mar 06, 2012 8:52 am, edited 1 time in total.
Reason: some corrections
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Rainflush » Wed Mar 14, 2012 12:56 am

In regard to the header, SNESTool is apparently incompatible with Windows 7, though I've yet to search for alternatives. Also, the ROM I'm dealing with has any number of patches applied to it already, if those patches were created for a headered ROM, will problems arise when the header is stripped away?
User avatar
Rainflush
Regular User
Regular User
 
Posts: 118
Joined: Wed Apr 28, 2010 2:54 pm

Re: Invincible Guardian

Postby Assassin » Wed Mar 14, 2012 5:10 am

nope, but as i said before, back up your ROM anyway. now, auto-patching (i.e. when you have a ROM and patch file with the same pre-extension name in the same directory, and allow the emulator to temporarily apply the patch) will be messed up by removing the header.
User avatar
Assassin
Moderator
Moderator
 
Posts: 1195
Joined: Tue Sep 14, 2004 5:10 am

Re: Invincible Guardian

Postby Rainflush » Wed Mar 14, 2012 5:22 pm

Hm, I just went into the hex editor and lopped off the 512 bytes myself, it seems to work just fine, and the damage overflow bug is fixed!
User avatar
Rainflush
Regular User
Regular User
 
Posts: 118
Joined: Wed Apr 28, 2010 2:54 pm

PreviousNext

Return to ROM Hacking and Emulation

Who is online

Users browsing this forum: No registered users and 3 guests

cron