11 March 2016 - Forum Rules

Main Menu

Final Fantasy II Restored

Started by redmagejoe, December 10, 2019, 03:09:14 AM

Previous topic - Next topic


Every little bit definitely helps. I admit that even with finding where the code fires, it's a little overwhelming thinking of trying to insert into that the necessary routines to prevent the code, such as checking for what the stat currently is before doing the increment/decrement and subsequent message display. My guess is that it runs through all of that and then after the fact it simply sets the stat back to 99 if the value at the end of the math is above $#63.

Also I'm going to leave what Jiggers found and PM'd me regarding the flag for Paul's Secret Stash: "Paul's Secret stash, my guess is the flag for it will be in the range of $6040 to $605F"


It'll most likely be just one bit changing from 1 to 0. There's 256 bits there, and I'm really confident that covers every change in the game. Every time an NPC vanishes, or shows up on another map. Using an item to unlock a specific story-progress door...

One thing to really look out for is places where the game code is NOT USED. Garbage data and code chunks that never get read during a complete playthrough. The more of those there are, the more room there is to fix stuff! Here's a table file for hex editors and anything else that can use 'em.

I've been using Mesen's debugger to start labeling bits here and there as I played through the start. If anyone else uses that emulator, I can try to set up some kind of sharing thing so we can all keep each other updated?
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.


I must be an idiot, because I cannot for the life of me figure out how to use the address that code is loaded from RAM to determine where to find the routines within ROM. Is there a correlation or do I need to actually view the addresses that fire the breakpoints within the context of the debug tools in the emulator?

Also, I'm not sure if this could be of help to anyone, but since there's already a fix for the Weapon/Magic leveling exploit (which I haven't looked at personally and haven't used in my playthrough, but I would assume only finalizes points towards exp after the round of turns is submitted, and thus should also prevent stats from going up by attacking and cancelling 46 times or such), a diff of a before and after ROM with the fix may be of use to narrowing down exactly what data is being manipulated once an attack order is sent. I don't know, just throwing out thoughts I had while thinking about this game's bugs.

Anyway, I decided to play around with this in FCEUX's 6502 debugger with breakpoint set on Read $6113, which is Firion's Intellect. I cast/cancelled Fire 26 times to guarantee an Int increase, and as soon as the battle ended, "You Won!" faded, "x Gil" drew then faded, breakpoint fired. Using Step Into, it appeared to go through the following lines:

05:A898:B1 7A     LDA ($7A),Y @ $6113 = #$06
05:A89A:AA        TAX
05:A89B:E8        INX
05:A89C:8A        TXA
05:A89D:20 3A AA  JSR $AA3A

05:AA3A:C9 64     CMP #$64
05:AA3C:90 02     BCC $AA40
05:AA3E:A9 63     LDA #$63 (skips this step because my INT isn't high enough, but this is important)
05:AA40:60        RTS -----------------------------------------

05:A8AC:91 7A     STA ($7A),Y @ $6123 = #$06 (becomes #$07 upon Stepping forward 1, likely this is indicating that after this instruction, the new value of INT, which was 6, is now 7)
05:A8AE:A6 AD     LDX $00AD = #$00
05:A8B0:A5 47     LDA $0047 = #$13

Not sure how helpful this is, but it does appear to do a comparison of the stat to see if it's gone up to 100, and if so, sets it back to 99. It seems that by this point in the instructions, it has already done the increment. Obviously the ideal would be to do a compare earlier to 99 and somehow skip the dialogue box and level up dialog altogether rather than doing the math first and correcting after the fact. I'm assuming that on the first line, LDA ($7A),Y @ $6113, the next line should be CMP #$63 if we were to want to have it do a comparison before anything else is looked at.

Sorry if this reads rambly, I'm still very inexperienced working with such low level programming and I'm trying to put words to concepts I know I'm trying to make happen with a toolkit I'm unfamiliar with. Essentially it does a conditional to set a stat >99 back to 99 after incrementing the stat, but the ideal behavior would be to have a conditional earlier before anything is written to 6113 to see if writing should even happen, so as to avoid not only the dialog, but more importantly, skip the routine that checks for the opposite stat to decrement.

For Firion, his base stats are 6110 (Strength), 6111 (Agility), 6112, (Stamina), 6113 (Intellect), 6114 (Spirit), 6115 (M.Power). Whenever 6114 goes up, there's a chance for 6110 to go down, 6113 -> 6112, and 6110 -> 6113. So For 6110, 6113, and 6114 only, there should be another routine off of those similar to what's listed above that should deal with 6113, 6112, and 6110 respectively.

So I set a breakpoint for reading 6110 and 6113. Curiously, at the start of a battle, it reads strength twice. It reads through the five stats, I would assume, but then there's a second read of Strength, but not Int, before the battle proceeds with no more breaks. I am currently trying to get a battle where I have a Strength increase and an Int decrease so that I can step through the process. I should also look into what it's doing the moment a command is issued to see what's being incremented for tallying up number of uses for the formula which determines a stat up. For instance, attacking 46 times guarantees a Strength increase, which means that without the cancel exploit fix, every time Firion attacks there should be a variable incrementing. Interestingly, the "Intellect down" happens before the "Strength up"...


QuoteI must be an idiot, because I cannot for the life of me figure out how to use the address that code is loaded from RAM to determine where to find the routines within ROM.

There are several ways. If you simply want to view ROM address,  you can use ROM offset checkmark in debugger, or hover your cursor over instruction and the address will be shown at the bottom of the window.

If you want to actually go there in ROM, the quickest way is to right click to the left of the instruction line in debugger (grey vertical line). Fceux must be paused beforehand.


Quote from: Cyneprepou4uk on December 25, 2019, 07:33:29 PM
There are several ways. If you simply want to view ROM address,  you can use ROM offset checkmark in debugger, or hover your cursor over instruction and the address will be shown at the bottom of the window.

If you want to actually go there in ROM, the quickest way is to right click to the left of the instruction line in debugger (grey vertical line). Fceux must be paused beforehand.

I must be blind because I didn't even notice that checkbox. Thank you again Cyne! This will help immensely! :thumbsup:

Right now I'm trying to figure out how to set a breakpoint in a way that catches the increment of the variable that handles number of times Firion has attacked, since that appears to be calculated even before Strength is read, to determine whether or not Strength does, in fact, increase.


That is a helpful post, when I feel well enough to poke at this game again.

What I'm seeing is that its reading one version of Intelligence ($6113), then writing to the OTHER one ($6123). So one set is a backup...? I noticed in the status menu it only reads one set. I assume the other set is kind of temporary for battle spells do alter. Like if there's a spell that doubles strength, you don't want strength to STAY doubled after battle, so it reads from that, but then leveling up a skill writes to the static set of stats.

So make sure you're watching for the other set of stats as well?
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.


I'm not sure what the difference between these two copies are, other than that xx2x seems more "permanent" than xx1x. 6120 is holding 109, which is made possible with 99 Strength + 10 from a piece of equipment, and displays as A9 in the status window ($6D), and in the post-battle level up of Strength, my Read 6120 breakpoint fired twice rather than just once, so it appears that a few things are happening that involves reading that value.

Looking back with a better understanding of context, I would assume that INX instruction is Increment X, and that is what is increasing the stat before even doing the CMP for 100/99. What we need to find is what instructions run before the LDA ($7A), Y before jumping the gun and trying to stick the CMP between the LDA and the TAX, INX, TXA instructions. In this case, my aforementioned suggestion of testing both a before and after of the cancel exploit fix may be helpful. Obviously for the purposes of testing, it's better to use a non-fixed version since it makes the stat ups easier to accomplish.


That sounds like a bug to me. Is there ANY game that purposefully displays hex numbers?

Here's some more RAM mapping.

7D7A - Battle Stats, 48 bytes per entity
7D7A offsets:                       
00 - Min Evasion                   
01 - Max Evasion                   
02 - Char Stat $29                 
03 - Min Magic Resist               
04 - Max Magic Resist               
05 - Char Stat $2E                 
06 - 0 at start, then...?
07 - Always #$14 for players       
08 - Ailment                       
09 - 0 at start, then...?           
0A - Current HP low                 
0B - Current HP high               
0C - Current MP low                 
0D - Current MP high               
0E - Max HP low                     
0F - Max HP high                   
10 - Max MP low                     
11 - Max MP high                   
12 - 0 at start, then Intellect     
13 - 0 at start, then Spirit       
14 -                               
15 -                               
16 -                               
17 -                               
18 - Min Accuracy                   
19 - Max Accuracy                   
1A - Attack                         
1B -                               
1C -                               
1D -                               
1E - Defense                       
1F - Char Stat $27                 
20 - Char Stat $28                 
21 -                               
22 -                               
23 -                               
24 - Char Stat $2F                 
25 -                               
26 -                               
27 -                               
28 -                               
29 - Command (00=attack) (FE=flee)
2A - Target (high bit set for targeting enemies)
2B -                               
2C - Ailment backup                 
2D - 0 at start, then backup of 09?
2E - ??                             
2F - ??                             

7CF3 - Firion's Attack counter (rises when confirming attack, does not reset between turns)
7CF4 - Maria's attack counter
7CF5 - Guys's attack counter

Not insanely helpful at the moment, but if you hex-edit the 7CFx things, it saves time selecting and canceling over and over?

There's still a handful of stats I don't understand. And this game feels a lot more convoluted for loading up stats for battle than FF1. It overwrites the save RAM stats as it re-calculates everything from scratch I think. And it sets a bunch of battle stats to 0 before calculating them, so what's that about?

I should be hunting down where stats change after battle next.
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.


I will do my very best to break down everything I can about stats and combat, having reached the end of the game now and been min-maxing, in a way that lends itself to figuring out how to fully disassemble the system. I should have provided a link in this thread's first post to a page that breaks down the way stats behave in the game, but I'm going to try to summarize them here and correlate them to what the code is doing.

Strength is used for determining Attack Power alongside weapon's attack value, which impacts the damage value of a single hit, in a system which, not unlike FF1, has multi-hits. It has a 1 in 5 chance of decreasing by 1 at the end of a battle if and only if the criteria for Spirit increasing by 1 is met. The formula for determining whether or not Strength increases can be simplified as being X/46 chance where X is number of times that character attacked in a single battle (so 46 attack/cancels will guarantee a Strength increase).
Strength+ -> Intellect-
Spirit+ -> Strength-

Agility has a singular purpose in that it is the baseline value for determining Evasion, before armor weight and shields are taken into account. Agility's chance to increase is directly correlated to your character's Evasion, and does not have a see-saw interaction with any stat, thus will never decrease.

Stamina is used to determine how much HP the character gains when HP does increase. That's it. Its chance to increase is based on seemingly the same, but a separate formula, based on the difference between the character's HP at the start of the fight and the end of the fight. The bigger the difference, the more likely Stamina and/or HP will increase. As it appears to be two of the same/similar formulas, it is very possible for a character to gain Stamina but not HP, or vis versa, with it being unlikely to only get one or the other if the character has lost, say, 90% of their HP. The criteria appears to be at least 1/8 of their Max HP lost, but don't have hard numbers. Stamina has the same 1 in 5 chance to decrease if Intellect increases, but does not itself decrease another stat.
Intellect+ -> Stamina-

Intellect is ASSUMEDLY used to determine your Black Magic damage (and possibly success chance of spells?), and correlates to the number of times a Black Magic spell is cast, X/26. It has a 1 in 5 chance of decreasing when Strength increases, and Stamina has a 1 in 5 chance of decreasing when Intellect increases. This is the stat that has spent the most time in my playthrough being at single digits on account of the bug where maxed stats still do a level-up check.
Intellect+ -> Stamina-
Strength+ -> Intellect-

Spirit is used to determine your White Magic damage/healing (and possibly success chance of spells?), and correlates to the number of times White Magic spells are cast, X/16. This is by far the easiest stat to raise in a single battle even without the exploit, given its low criteria. For whatever reason, target-all spells cast by enemies on the party will somehow interact with Firion's Spirit up counter, or perhaps it has its own counter that is simply referring to the wrong address. Strength has a 1 in 5 chance of decreasing when Spirit increases.
Spirit+ -> Strength-

M.Power determines how much MP a character gains when it does increase, and thus, like Stamina and HP, has the same criteria for increasing. The difference between MP at the start and at the end of battle affects the chance, again two different chances, for MP and/or M.Power to increase. M.Power may also affect the power of spells alongside Intellect and Spirit? Supposedly it also factors into Magic Defense.

Evasion Levels and %, from my understanding, correlates to the number of hits you can dodge. So if an enemy were to hit, say, 16 times, and your Evasion is only 8-99%, you could only dodge 8 of the 16 hits. Likewise, I believe the % pertains to how likely you are to avoid hits under that threshold. Having a high % alone seems to increase your chance of increasing Agility, but as it is a roll between 0 and 255, you'd still only have a 99/255 chance of gaining Agility. Evasion Levels, on the other hand, only increase when an enemy targets that character. This, like the cancel exploit, applies regardless of whether the enemy actually gets the chance to attack the character, as Preemptive 1-turn wipes on enemies that, ASSUMEDLY, are all targetting Firion for instance, still sees Firion gaining an Evasion Level from my testing. Given the 1-battle delay on updating its display in the menu though, this is not conclusive testing. I would assume 16 is the highest level of Evasion, but I have not gotten that far.

Magic Defense and % operates similar to Evasion, with magic targetting characters having the effect of increasing Magic Defense levels, and the % pertaining to how many "hits" of a spell level above 1 are successful. This is harder to increase currently due to the target-all Firion Spirit bug.

Accuracy is affected by your Weapon Skill in the weapon you are wielding. Thus a Level 16 Sword wielder has a high chance of hitting 14-16 times, with an Accuracy level listed as 16 in their window, and then the actual % being their chance for each of those hits, modified by a particular weapon's accuracy malus.

Weapon Skill has a chance to increase in the same way as Strength, though without the X/46. It increases by 1 per use of an attack, with the battle rank determining how a static bonus or malus. So trying to level a Weapon Skill at 10 from 0 exp on a Goblin at the start of the game would require you to attack ~107 times rather than 100. Not exact numbers, just an example. Spell levels work with this mechanic as well, demanding using them on higher threat enemies to maintain leveling efficiency of your weapons and spells.

I found the much more analytical source I'd used a while ago, and I'll add the link to the other one in the first post.


I think this might be what you're looking for? (Edit: Oh, yeah, this is near the same address you posted!)

There's also some stuff at 05:A675 to look at -- it sets some variables and jumps to this next block to do things. It does 3 of these, no comparing if it needs to do them at all first or not. My head hurts too much to go through more of it, but I think this is the right track!

  05:A874: ($16874)
  CLC                     ;
  LDA $47                 ; = #$10 <- Strength stat
  ADC #$10                ; Add #$10
  STA $48                 ; = #$20 now; secondary Strength stat
  CLC                     ;
  LDA $4A                 ; = #$13 <- Intelligence stat
  ADC #$10                ;
  STA $4B                 ; = $23
  LDX #$00                ;
  LDA $46                 ; = $2D
  JSR $FD11               ; Unsure what happens here exactly. Involves another jump to some kind of division routine?
  STA $46                 ; now = $26
  LDY $9E                 ; $9E seems to be character counter in a lot of this code, currently $00 for Firion. May be doubled as an index (Maria, usually $01, would become $02; and Guy, $04)
  SEC                     ;
  LDA ($44),Y             ; pointer to $7CF3 = #$FF
  BEQ L16903              ; If $0 then goto this address
  SBC $46                 ; Subtract #$26 from #$FF (carry is set)
  BCC L16903              ; If carry clear then goto this address
  LDY $47                 ; = #$10 <- Strength stat
  LDA ($6100),Y           ; Load stat
  TAX                     ; Transfer to X
  INX                     ; Increment X (add 1 to stat)
  TXA                     ; Transfer back to accumulator
  JSR CMP_100             ; ($AA3A) - compares to #100, and if carry is set, loads #99 into A
  STA ($6100),Y           ; Save stat
  LDY $48                 ; = #$20 ; secondary Strength stat
  LDA ($6100),Y           ; Do all the same things!
  TAX                     ;
  INX                     ;
  TXA                     ;
  JSR CMP_100             ;
  STA ($6100),Y           ; So now the main and backup Strength stat is +1 and capped at 99.
  LDX $AD                 ; = #$01 ; this is the counter for stat upgrades
  LDA $47                 ; = #$10
  SEC                     ;
  SBC #$13                ; Subtract #$13 from #$10, resulting in #$FD
  BEQ L168BD              ; If $0 then do this
  BCS L168C1              ; If carry set, then do this
  LDA #$5F                ; Neither branched; Loads #$5F into A
  BNE L168C3              ; Jump ahead
  LDA #$60                ; Subtraction resulted in #$0, so load #$60 into A
  BNE L168C3              ; Jump ahead
  LDA #$61                ; Subtraction resulted in carry being set, so load #$61 into A
L168C3: (05:A8C3 ($168C3))                 
  STA $7FBA,X             ; $7FBA seems to be a string of FFs until its set here; its being saved as 5F now
  INC $AD                 ; Increase the counter
  LDX #$00                ; 
  LDA $49                 ; = #$05
  JSR $FD11               ; The weird math thing again. Output: A = 1, X = 1, Y = 20
  BNE L16903              ; A = 1 so jump to end... but if it DIDN'T, then:
  LDY $4A                 ; = #$13
  LDA ($6100),Y           ; Load Intelligence stat
  CMP #$01                ;
  BEQ L16903              ; If Intelligence = 1 then jump ahead to end
  TAX                     ;
  DEX                     ; Transfer to X, decrement, transfer back
  TXA                     ; 
  STA ($6100),Y           ; Save Intelligence stat
  LDY $4B                 ; = #$23 ; other Intelligence stat
  LDA ($6100),Y           ; Note that it doesn't check if its 1 here
  TAX                     ; Otherwise, does the same stuff again.
  DEX                     ; 
  TXA                     ; 
  STA ($6100),Y           ; 
  LDX $AD                 ; Load the counter for the string of FFs at 7FBA
  LDA $4A                 ; = #$13 still (I accidentally pressed the wrong button here and skipped past all this so I don't know how accurate I'll be.)
  SEC                     ; 
  SBC #$12                ; Subtract #$12 from the stat being lowered
  BEQ $A8F6               ; If $0 then do this
  BCS $A8FA               ; If carry set then do this
  LDA #$5F                ; If neither then load #$5F
  BNE $A8FC               ; and jump ahead
  LDA #$63                ; 
  BNE $A8FC               ; 
  LDA #$60                ;
  ORA #$80                ; Set high bit
  STA $7FBA,X             ; Save in the list of FF
  INC $AD                 ; Increment stat counting thing
  RTS                     ; 

I've also got an assembly project working! Right now its just each bank's data slapped into .asm files with the .incbin command, so its... not really helpful at all. And since its not technically disassembled code, I don't know about the legality of uploading it yet.

Also its based off the patched English version with B-button dash so its not super authentic either.

I don't really know how to parse the file abw shared either, but that's basically the next step I guess? Mostly I just can't tell if there's missing data in it or not. Like at the very end of it, it says, "; ... skipping $11 00 bytes". Just so much commentary to sift through to see what's really there.
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.


That's the area I was looking at/for, yeah. As for trying to make sense of abw's disassembly, perhaps it would be advantageous to work with him on that? If you host it on Google Drive or GitHub or somesuch and link it here, it would definitely be of benefit to myself and others attempting to work on Final Fantasy II patching.

Really awesome to hear that you've begun work on a disassembly, Jiggers! And it's okay that it's not a disassembly of the original for the purposes of romhacking, I'm sure. I'm going to keep digging around through breakpoints and see if I can't narrow down specifically what needs to be changed. As for the legality of uploading a disassembly, you could do what Steve_Hacks did for the Crystalis Randomizer that I used, which was write a strip script that spits out the disassembly for you the individual user. The resulting Crystalis.s file is what I used to navigate and make changes to the Crystalis ROM for the Sky Tower Exit hack. He said his reasoning behind doing it that way was to avoid legal issues.


...I got no idea how to do anything like that, ahah...

I feel like I screwed this up enough to be safe though.

Having trouble compiling because of the branch operations. They spit out range errors. The first one for instance:

BEQ $0F ; $C066          ; C055  $F0 $0F       

Originally it was "BEQ $C066" which, reading it from the debugger window, makes sense. You want to see exactly what it does: branch to address $C066. Assembling it, I thought, no wonder its a range error, it should only be one byte. But even with $0F there, it does it. *shrug* So I'm stumped. I got to around $E000 with my macro to fix every BEQ, BNE, BCC, BCS, BMI, and BPL I came across, but why keep trying to fix them if this fix isn't working? And it just gives another kind of error if I try #$F0.

So the only thing I can think of it do is to go through the whole file and do the labels manually. I already spent basically all day cleaning this one up (only bank 0F so far) and I'm clueless about how to do things efficiently. I can do macros in Notepad++ but I still need to point to where the macro starts.

Hm. So I can undo the branch instruction edits, and the ones with the labels should be fine, its just the ones with $xxxx that I need to do manually. Or someone could figure out a way to script it? Edit again: I got Python Script for Notepad++ but I have no idea how Python works.

    BEQ $C066                ; C055  $F0 $0F
    LDA #$77                 ; C057  $A9 $77
    STA $FA                  ; C059  $85 $FA
    JSR $F476                ; C05B  $20 $76 $F4
    LDA #$0E                 ; C05E  $A9 $0E
    JSR L3FE03               ; C060  $20 $03 $FE
    JSR $B890                ; C063  $20 $90 $B8
    LDA #$0E                 ; C066  $A9 $0E

Every BEQ, BNE, BCC, BCS, BMI, and BPL that has a $ after it... remove the $, copy the 4 characters, find them in the comments (if they exist; if not do nothing), go back one line, and make a label. Like so:
    BEQ C066                ; C055  $F0 $0F
    LDA #$77                 ; C057  $A9 $77
    STA $FA                  ; C059  $85 $FA
    JSR $F476                ; C05B  $20 $76 $F4
    LDA #$0E                 ; C05E  $A9 $0E
    JSR L3FE03               ; C060  $20 $03 $FE
    JSR $B890                ; C063  $20 $90 $B8
    LDA #$0E                 ; C066  $A9 $0E

def colon(m):
    return \:

def branch(m):
    return r"\1"
def address(m):
    return r"\2"
def searchfor(m):

editor.research(r"(BEQ|BCC|BNE|BMI|BPL|BCS)\s\$(....)", r"\1 \2")
editor.copyRange(10, 14)
editor.research(r"\; searchfor", r"1\")

Gave it my best shot but its clear I don't know what I'm doing with this language. The only thing that actually ever worked was "editor.rereplace(r"(BEQ|BCC|BNE|BMI|BPL|BCS)\s\$(....)",r"\1 \2")"
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.


You probably should include BVC and BVS as well


Oh, yeah. Luckily there's not many of them and they all use labels Mesen pre-made.

I managed to work out a macro and used Control+F3 to do the searching, now I'm sifting through to find mistakes. 6 bytes still off. Will update the zip file when its done. And then I'm never doing this again. :( Like, I'll go through and comment when I discover how things work, and help identify variables and edit the code when its time to fix the bugs, but I am NOT going to convert another bank to assembly code...* Even though it seems we'll need to do Bank 05 next...


And a github I barely know how to use.  :-[

Its a start!

* Another Update: Slight change of heart, because the Bank 05 stuff that I think is the stat level up code that's most important is only like $1000 bytes and therefore only a quarter the size of the fixed bank...? Should just be... a lot easier to sort through and tidy up. So I'm doing that now...

Final Update: Bank 05 is up and running in Github. Mesen didn't have a chance to run all the code so it made some bad guesses, we'll fix 'em up when we get there.

Learned a lot more about the structure of the game just by watching which banks are swapped in while playing. Going to use the table file I made and see if I can spot some text data and mark that down. Next thing I'll do after that, someday hopefully soon, is to step through Bank 05 and update the variables and labels so its easier to see what its doing.
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.


This is amazing work, Jiggers. I admire the energy you're putting into this, and wish I could contribute more than just my working knowledge of the game from an end user standpoint. I think I've figured out where to make the necessary changes, but of course the issue now is space. Moving things around, updating pointers...

In bank_05, starting at line 3414 is the area we were looking at, with INX being the increment that increases the stat, and the two instructions DEX further down at line 3452 and 3456 are the decrements of the opposing stat.


QuoteAnd a github I barely know how to use

Try GitHub Desktop, it's quite simple. Make changes in your local folder and commit updates through it.

QuoteI managed to work out a macro and used Control+F3 to do the searching

I'd like to hear more details.


GitHub desktop's what I use. I had to manually remove the .gitignore files from the folder before it would take them out. Just silly things like that always tripping me up.

To get the branch labels set right, I first used the regex from earlier: (BEQ|BCC|BNE|BMI|BPL|BCS|BVS|BVC)\s\$(....)
Then used the Mark function, checked "Bookmark line" and did Mark All. Then in the Search menu > Bookmarks > Copy Bookmarked lines. Scroll to the end of the file and paste. Hold down Alt and drag the mouse cursor down the front, to go into column editing mode, and add in a ;
Then re-use the Regex and do a search and replace, where replace is "$1 $2 " to remove the $ from the branch operations.
Then scroll down to the last line of the file, the bottom of the address list, and highlight it. Use Control+F3 to copy that text and search for the next instance.

Then the macro is:
Home (twice, because sometimes it doesn't go to the very start of the line and just goes to the first character)
Home (twice)
Delete (twice) - removes the "; "
Control+End - goes back to the end of the file
Shift+Home - highlights the next address to search for

And repeat until done. Then if there's any addresses that are 8xxx or 9xxx I just put a B in front of them to mark them as Branch labels. I did that manually since there weren't many in Bank 05, but that's going to be a bigger step for banks with more code around that area.

QuoteThis is amazing work, Jiggers. I admire the energy you're putting into this, and wish I could contribute more than just my working knowledge of the game from an end user standpoint.

Thanks! And you're welcome I think too? XD And don't worry about it. Your experience is needed to know what to do too!

There's a file in the Github called rom layout notes... I'll be updating that while I cruise through data, making note of possible free space. Also filled out the DTE table last night.
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.



Alright, so I just have to try to wrap my brain around what I'm looking at. Again have a tendency to think in high-level, so looking at each instruction and trying to imagine it in the context of a (comparatively simple) instruction, I'm trying to figure out how I might rearrange instructions as needed, but first I need to know what each thing is doing. Thus my need for a disassembly. Since there are 4 JSRs to AA3A (which does the comparison to 100 and if so, sets back to 99), I'm referencing those sections of code below.

    CLC                      ; A874  $18
    LDA $47                  ; A875  $A5 $47
    ADC #$10                 ; A877  $69 $10
    STA $48                  ; A879  $85 $48
    CLC                      ; A87B  $18
    LDA $4A                  ; A87C  $A5 $4A
    ADC #$10                 ; A87E  $69 $10
    STA $4B                  ; A880  $85 $4B
    LDX #$00                 ; A882  $A2 $00
    LDA $46                  ; A884  $A5 $46
    JSR $FD11                ; A886  $20 $11 $FD
    STA $46                  ; A889  $85 $46
    LDY $9E                  ; A88B  $A4 $9E
    SEC                      ; A88D  $38
    LDA ($44),Y              ; A88E  $B1 $44
    BEQ L16903               ; A890  $F0 $71
    SBC $46                  ; A892  $E5 $46
    BCC L16903               ; A894  $90 $6D
    LDY $47                  ; A896  $A4 $47
    LDA ($7A),Y              ; A898  $B1 $7A
    TAX                      ; A89A  $AA
    INX                      ; A89B  $E8
    TXA                      ; A89C  $8A
    JSR $AA3A                ; A89D  $20 $3A $AA
    STA ($7A),Y              ; A8A0  $91 $7A
    LDY $48                  ; A8A2  $A4 $48
    LDA ($7A),Y              ; A8A4  $B1 $7A
    TAX                      ; A8A6  $AA
    INX                      ; A8A7  $E8
    TXA                      ; A8A8  $8A
    JSR $AA3A                ; A8A9  $20 $3A $AA
    STA ($7A),Y              ; A8AC  $91 $7A
    LDX $AD                  ; A8AE  $A6 $AD
    LDA $47                  ; A8B0  $A5 $47
    SEC                      ; A8B2  $38
    SBC #$13                 ; A8B3  $E9 $13
    BEQ L168BD               ; A8B5  $F0 $06
    BCS L168C1               ; A8B7  $B0 $08
    LDA #$5F                 ; A8B9  $A9 $5F
    BNE L168C3               ; A8BB  $D0 $06
    LDA #$60                 ; A8BD  $A9 $60
    BNE L168C3               ; A8BF  $D0 $02
    LDA #$61                 ; A8C1  $A9 $61
    STA $7FBA,X              ; A8C3  $9D $BA $7F
    INC $AD                  ; A8C6  $E6 $AD
    LDX #$00                 ; A8C8  $A2 $00
    LDA $49                  ; A8CA  $A5 $49
    JSR $FD11                ; A8CC  $20 $11 $FD
    BNE L16903               ; A8CF  $D0 $32
    LDY $4A                  ; A8D1  $A4 $4A
    LDA ($7A),Y              ; A8D3  $B1 $7A
    CMP #$01                 ; A8D5  $C9 $01
    BEQ L16903               ; A8D7  $F0 $2A
    TAX                      ; A8D9  $AA
    DEX                      ; A8DA  $CA
    TXA                      ; A8DB  $8A
    STA ($7A),Y              ; A8DC  $91 $7A
    LDY $4B                  ; A8DE  $A4 $4B
    LDA ($7A),Y              ; A8E0  $B1 $7A
    TAX                      ; A8E2  $AA
    DEX                      ; A8E3  $CA
    TXA                      ; A8E4  $8A
    STA ($7A),Y              ; A8E5  $91 $7A
    LDX $AD                  ; A8E7  $A6 $AD
    LDA $4A                  ; A8E9  $A5 $4A
    SEC                      ; A8EB  $38
    SBC #$12                 ; A8EC  $E9 $12
    BEQ A8F6                 ; A8EE  $F0 $06
    BCS A8FA                 ; A8F0  $B0 $08
    LDA #$5F                 ; A8F2  $A9 $5F
    BNE A8FC                 ; A8F4  $D0 $06
    LDA #$63                 ; A8F6  $A9 $63
    BNE A8FC                 ; A8F8  $D0 $02
    LDA #$60                 ; A8FA  $A9 $60
    ORA #$80                 ; A8FC  $09 $80
    STA $7FBA,X              ; A8FE  $9D $BA $7F
    INC $AD                  ; A901  $E6 $AD
    RTS                      ; A903  $60

;; sub start ;;
    CLC                      ; A904  $18
    LDA $46                  ; A905  $A5 $46
    ADC #$10                 ; A907  $69 $10
    STA $47                  ; A909  $85 $47
    LDX #$00                 ; A90B  $A2 $00
    LDA $44                  ; A90D  $A5 $44
    JSR $FD11                ; A90F  $20 $11 $FD
    STA $48                  ; A912  $85 $48
    SEC                      ; A914  $38
    LDA $45                  ; A915  $A5 $45
    BEQ L16955               ; A917  $F0 $3C
    SBC $48                  ; A919  $E5 $48
    BCS L16955               ; A91B  $B0 $38
    LDY $46                  ; A91D  $A4 $46
    CPY #$11                 ; A91F  $C0 $11
    BEQ L1695B               ; A921  $F0 $38
    LDA ($7A),Y              ; A923  $B1 $7A
    TAX                      ; A925  $AA
    INX                      ; A926  $E8
    TXA                      ; A927  $8A
    JSR $AA3A                ; A928  $20 $3A $AA
    STA ($7A),Y              ; A92B  $91 $7A
    LDY $47                  ; A92D  $A4 $47
    LDA ($7A),Y              ; A92F  $B1 $7A
    TAX                      ; A931  $AA
    INX                      ; A932  $E8
    TXA                      ; A933  $8A
    JSR $AA3A                ; A934  $20 $3A $AA
    STA ($7A),Y              ; A937  $91 $7A
    LDX $AD                  ; A939  $A6 $AD
    LDA $46                  ; A93B  $A5 $46
    SEC                      ; A93D  $38
    SBC #$12                 ; A93E  $E9 $12
    BEQ A948                 ; A940  $F0 $06
    BCS A94C                 ; A942  $B0 $08
    LDA #$62                 ; A944  $A9 $62
    BNE A94E                 ; A946  $D0 $06
    LDA #$63                 ; A948  $A9 $63
    BNE A94E                 ; A94A  $D0 $02
    LDA #$64                 ; A94C  $A9 $64
    STA $7FBA,X              ; A94E  $9D $BA $7F
    INC $AD                  ; A951  $E6 $AD
    BNE L1695B               ; A953  $D0 $06
    LDY $46                  ; A955  $A4 $46
    CPY #$11                 ; A957  $C0 $11
    BEQ L16923               ; A959  $F0 $C8
    RTS                      ; A95B  $60

I'm assuming, probably incorrectly, that this entire section is the end-of-battle tallying of the "commands issued" counters, using that to determine if a stat is going up, and then increasing it and decrementing the opposing stat. As for the context of what the game is trying to do, that's where I get lost. Like I can refer to opcodes and figure out what each instruction are doing in a vacuum, but correlating that to the actual outcome in the game's parameters confuses me. You stated before that at A896, Memory @ $47 holds Strength, and then the next line loads that value into the accumulator @ $7A, and then the next line Transfers A into X, then Increments X, then Transfers X to A. So in these 4 lines, data is simply being moved around before the JSR is done to the comparison. In essence, the ideal is for the comparison to be done, I'm assuming, directly after A898?

I'm going to wrack my brain a bit and try to read up on opcodes some more to see if I can't build an image of what's happening here in what order alongside the disassembly you've provided. Bear with me while I spitball. After A898, I would want to compare the stat to 99. IF TRUE THEN jump to a point that skips the increment and decrement code entirely. Which looks to be jumping to... somewhere past A8E9? I still have to figure out what the other instructions are doing before I can make a more educated guess.

Actually, you can see more or less that the code is already doing this to avoid going below 1, so it would just need to be repurposed and applied on the opposite end of the spectrum.

    LDA ($7A),Y              ; A8D3  $B1 $7A
    CMP #$01                 ; A8D5  $C9 $01
    BEQ L16903               ; A8D7  $F0 $2A

So I would want to have the line directly after A898 be CMP #$63 (A898 $C9 $63), and a BEQ to... somewhere. The side effect to doing these comparisons on the fly and avoiding the increment altogether is that AA3A could be cannibalized, since if code branches on = 99, there'd be no reason to have those several bytes in place to ensure that a stat doesn't go up to 100. That's at least 19 bytes by my count (removing the 4 JSR lines @ 3 bytes apiece, and then the AA3A section itself which is 7 more bytes), so that's a nice chunk of space. If I figure out where the BEQ is pointing (I'd assume Line 16903, but that doesn't correlate to the current format of the disassembly), then I can use that as a guide for how I would fill in the repurposed BEQ and possibly rewrite the Strength Up / Intellect down section. If I can figure that out and test it, then I can start working on the other 2 see-saw stats (Spirit+ / Strength- & Intellect+ / Stamina-).

EDIT: Alright, well I think I see where those branches are pointing to, but...

    RTS                      ; A903  $60

The 4 BNE, BEQ, BCC pointing to that address have different values after the opcode. My limited work with branches suggested to me that the address after the opcode should be a pointer which... again, forgive me for being so fresh, points to a specific address? But then those values should be the same for all 4 branch instructions, shouldn't they? Basically I just need to know what hex address to put after the $F0 in my above-mentioned test fix.
Never mind, I'm an idiot. Basic deduction (though I probably could have gone and read the manual) led me to find that it's the number of bytes skipped ahead by the branch. So assuming nothing else moves currently (which won't be the case)... 105 bytes between the new BEQ and the RTS we want to skip to. That's $69 so...

Test fix:

    LDY $47                  ; A896  $A4 $47
    LDA ($7A),Y              ; A898  $B1 $7A
    CMP #$63                 ; A89A  $C9 $63
    BEQ L16903               ; A89C  $F0 $69

Obviously this alone can't be plugged in or the whole thing breaks. I may need to analyze how to nudge this in or put it in some blank space and add a JSR to test it before worrying about moving data around. The issue right now in the way of testing this is offsets. The new instructions require inserting 4 bytes in a place that isn't an issue at first, because the JSR 3 bytes can be removed... But then there's still 1 extra byte offset. Even considering that the 4 JSRs are removed (12 bytes), we're adding 16 bytes, and the longer we wait to fix that offset, the more problems pile up. AA3A is another 7 bytes that can be removed which puts us at -3 bytes (which can be padded easily), but that's quite a ways away. I suppose it's feasible to go through the instructions and make sure there's no bad pointers to test this, which for those 4 I'm guessing it's just... Strength and... actually upon searching through that bank_05 disassembly, there's not enough CMP #$64s or JSR AA3As to suggest there's a routine for each stat. I think it's safe to assume that this section handles all stat increases, and there's surely something that checks a table or such to see if it even needs to decrement another stat.

EDIT: Actually upon further analyzing the comparison that prevents a stat from going below 1, I may be able to save more space, as it only does the comparison once on account of the Branch. I would assume this means the two copies of a stat, despite being separate, will always be identical, because the existing CMP-BEQ only compares once, and if it clears, it does the DEX on each without a second CMP. I may be able to utilize this to save even more space, though that also mandates even more padding. To move as little data as possible, I am now operating under the idea that I will leave AA3A alone (though it will never be used), and to replace the two JSRs per section, (3 + 3) I will simply replace the first one with the new CMP-BEQ (4) and the second one with 2 NOPs (2). This will have a minimal impact on pointers and addresses, and should fix the issue. Going to try and mock up a patch and test it now.

Test Fix:

0x168AA: C9 63 F0 65 AA E8 8A 91 7A A4 48 B1 7A AA E8 8A EA EA
0x16935: C9 63 F0 32 AA E8 8A 91 7A A4 47 B1 7A AA E8 8A EA EA

EDIT: HUZZAH! The above fix appears to work! 16 white magic casts, 46 attacks, and Firion gained neither Spirit nor Strength, which means 99 Spirit = no chance of Strength loss, and 99 Strength = no chance of Intellect loss (and though untested this should also mean 99 Int = no Stamina loss)! I'll make a quick IPS and set it aside to be uploaded later separately or used as part of a bug fix omnibus. With this patch, any stat that reaches 99 will no longer try to increase after a battle if criteria are met. This should apply to Strength, Agility, Stamina, Intellect, Spirit, and M.Power. I don't think this affects Magic Defense or Evasion since they are capped at 16 rather than 99.

FFII Maxed Stat Increase Fix.ips


Wow, that was fast. Good job!

So I don't need to keep going through this code to comment and sort it out more? XD I was making progress, but I'm exhausted.

What else was there that needed fixing and doesn't involve this horribly convoluted stat leveling system...
I know exactly what I'm doing. I just don't know what effect it's going to have.

I wrote some NES music! Its a legal ROM file. - I got a Ko-Fi page too.