News: 11 March 2016 - Forum Rules
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia, Danke

Poll

Patching Mysidian Tower Orbs Behavior: http://www.romhacking.net/forum/index.php?topic=29704.msg388989#msg388989

Leave it, flawed as it is; the remakes retained this behavior after all
3 (13.6%)
Keep the random single-character bonus, but remove the 4th character from the RNG
4 (18.2%)
Grant the bonus to all characters, giving them a much-earned buff at this point in the game.
15 (68.2%)

Total Members Voted: 22

Author Topic: Final Fantasy II Restored  (Read 27721 times)

abw

  • Sr. Member
  • ****
  • Posts: 344
    • View Profile
Re: Final Fantasy II Disassembly / Bug Fix Project
« Reply #100 on: January 19, 2020, 11:52:01 am »
I already NOPd them out in my gold fix IPS because nothing else seemed to point to that code. Is it fine for them to have been NOP'd? I didn't see any way that it could be used after all the branches basically got cut off from it, leaving those 7 bytes isolated. Let me know if I should revert those NOPs
If you NOP those out, you're disabling the low byte comparison (you know BNE didn't happen, so Z is set, which leaves you with A = #$96, so at this point you now know the high 2 bytes of party gold are equal to the high 2 bytes of the cap and it's time to check the low byte), so gold amounts between $989600 (9,999,872) and $9896FF (10,000,127) won't be handled correctly.

I'm currently mocking up a solution for spell experience/levels based on the example you provided here. Would you be so kind as to review it for errors?
Looks pretty close to me, only a couple of things to note:
Code: [Select]
0x0165CC|$05:$A5BC:B0 33        BCS $A5F1   ; if we hit the cap, don't increase level or experience
-----------------------------------------
>>0x0165CE|$05:$A5BE:69 09    ADC #$09    ; base growth penalty
Unless you're intentionally changing it, that one should stay ADC #$0A to match the original - since you only reach the ADC when the BCS isn't taken, you know C is clear just like it was in the original after the CLC.

Code: [Select]
0x0165D7|$05:$A5C7:90 28        BCC $A5F1       ; not enough growth => no experience gain
0x0165D9|$05:$A5C9:C8           INY             ; offset for spell experience
0x0165DA|$05:$A5CA:90 25        BCC $A5F1       ; if credit < penalty, no growth, otherwise grow by the remainder
You only need one of these BCCs - INY doesn't change the value of C.

Alternatively, and my personal preference, we could ignore overflow protection here, call this part done, and focus on capping the counters $7CF3-$7CF6 and $7CF7-$7D36 to a number well below 255, say 200 ($C8). There's absolutely no benefit to having any of the counters that high since spells, skills, and stats can only increase by 1 per battle, and it is unnecessary to reach 200 to attain any of these increases.
I agree with this - working overflow protection into all the calculations that need it would be annoying.

redmagejoe

  • Full Member
  • ***
  • Posts: 178
    • View Profile
Re: Final Fantasy II Restored
« Reply #101 on: January 19, 2020, 01:19:10 pm »
I will make the changes accordingly. Thank you for the info. Gil Fix and Spell Up fix will be updated, and then re-uploaded / linked. I appreciate your review of my code. Significantly less changes to the original code in my new version. :)

Code: [Select]
0x0165C5|$05:$A5B5:B1 7E    LDA ($7E),Y ; spell level
0x0165C7|$05:$A5B7:AA      TAX       
0x0165C8|$05:$A5B8:E8      INX        ; increase spell level
0x0165C9|$05:$A5B9:20 6D A8 JSR $A86D  ; cap X at 15
0x0165CC|$05:$A5BC:B0 33 BCS $A5F1   ; if we hit the cap, don't increase level or experience
-----------------------------------------
>>0x0165CE|$05:$A5BE:69 0A    ADC #$0A    ; base growth penalty
0x0165D0|$05:$A5C0:85 54    STA $54    ; total spell growth penalty (= spell level + 10)
0x0165D2|$05:$A5C2:38      SEC       
0x0165D3|$05:$A5C3:A5 53    LDA $53    ; total spell use credit
0x0165D5|$05:$A5C5:E5 54    SBC $54    ; total spell growth penalty (= spell level + 10)
0x0165D7|$05:$A5C7:C8      INY        ; offset for spell experience
0x0165D8|$05:$A5C8:90 25    BCC $A5F1  ; if credit < penalty, no growth, otherwise grow by the remainder
0x0165DA|$05:$A5CA:18      CLC       
0x0165DB|$05:$A5CB:71 7E    ADC ($7E),Y ; spell experience
0x0165DD|$05:$A5CD:C9 64    CMP #$64   
0x0165DF|$05:$A5CF:90 1E    BCC $A5EF  ; if experience < 100, just update experience, otherwise increase lvl
0x0165E1|$05:$A5D1:88      DEY        ; offset for spell level
^^^
^^^
^^^
0x0165E2|$05:$A5D2:EA      NOP
0x0165E3|$05:$A5D3:8A      TXA       
0x0165E4|$05:$A5D4:91 7E    STA ($7E),Y ; write the new spell level
0x0165E6|$05:$A5D6:C8      INY        ; offset for spell experience
0x0165E7|$05:$A5D7:98      TYA       
0x0165E8|$05:$A5D8:48      PHA        ; save offset for spell experience
0x0165E9|$05:$A5D9:A6 AD    LDX $AD    ; battle message index
0x0165EB|$05:$A5DB:18      CLC       
0x0165EC|$05:$A5DC:A5 00    LDA $00    ; counter for number of spell slots processed
0x0165EE|$05:$A5DE:69 30    ADC #$30    ; spell IDs start at #$30 within character stat data
0x0165F0|$05:$A5E0:A8      TAY        ; offset for spell ID
0x0165F1|$05:$A5E1:B1 7A    LDA ($7A),Y ; spell ID
0x0165F3|$05:$A5E3:38      SEC       
0x0165F4|$05:$A5E4:E9 C0    SBC #$C0   
0x0165F6|$05:$A5E6:9D BA 7F STA $7FBA,X ; start of list of string IDs battle messages
0x0165F9|$05:$A5E9:E6 AD    INC $AD    ; battle message index
0x0165FB|$05:$A5EB:68      PLA        ; restore offset for spell experience
0x0165FC|$05:$A5EC:A8      TAY       
0x0165FD|$05:$A5ED:A9 00    LDA #$00    ; reset spell experience to 0
0x0165FF|$05:$A5EF:91 7E    STA ($7E),Y ; write the new spell experience
0x016601|$05:$A5F1:E6 44    INC $44    ; offset for current character's current spell slot use counter

Everything after the NOP (only need 1 now for buffer space, since I put the second CLC back) is the same, the TAX and INX was moved up, and a single BCS was added. All the other changes were reverted as per your review.



As for the gold thing, that code that I NOP'd out, I'm assuming, can still run because even though we changed the branches, they're a BCC followed by a BNE. So if C=1 and Z=1, then the program can still run those three lines of code, yeah?

Code: [Select]
0x03F005|$0F:$EFF5:F0 02    BEQ $EFF9  ; BNE $F000 would be better since then you could eliminate the following BCS
0x03F007|$0F:$EFF7:B0 07    BCS $F000  ; cap party gold at 9,999,999
; control flow target (from $EFF5)
0x03F009|$0F:$EFF9:AD 1C 60 LDA $601C  ; Party gold byte 0
0x03F00C|$0F:$EFFC:C9 80    CMP #$80   
0x03F00E|$0F:$EFFE:90 0F    BCC $F00F

Yeah, I see it now. I'm reverting the NOPs (except for the removed BCS) as we speak. And updated!
« Last Edit: January 19, 2020, 04:45:12 pm by redmagejoe »

Leviathan Mist

  • Jr. Member
  • **
  • Posts: 40
    • View Profile
Re: Final Fantasy II Disassembly / Bug Fix Project
« Reply #102 on: January 19, 2020, 02:11:16 pm »
Are you planning to do anything about the ability to switch weapons mid-battle in order to level up different weapons? This is quite abusable if you start with shields and then switch to another weapon like swords after attacking a few times with the shields, as the game will apply all shield weapon exp to the swords at the end of battle.

redmagejoe

  • Full Member
  • ***
  • Posts: 178
    • View Profile
Re: Final Fantasy II Restored
« Reply #103 on: January 19, 2020, 02:18:18 pm »
Are you planning to do anything about the ability to switch weapons mid-battle in order to level up different weapons? This is quite abusable if you start with shields and then switch to another weapon like swords after attacking a few times with the shields, as the game will apply all shield weapon exp to the swords at the end of battle.

That is significantly more difficult to amend, as there are not separate counters in the game for each weapon type, only a "numbers of time attack orders were issued" counter. I'm not sure if there's even enough room in memory for an additional 15 (per character) counters to be added. That may be an issue that needs to remain due to technical limitations, but I'll put it on my investigation to-do list. Was that behavior fixed in the PSP version?

Note there will be divergences, as even in the PSP version, spells and levels still seem to fill the experience bar even at Level 16. I haven't tested enough to see if it fills and resets to 0 like it did in the NES before this fix, but there will be improvements that strive to fix behaviors that even weren't remedied in the latest remakes. Likewise, technical limitations may result in fixes made in the remakes to not be feasible to put in this patch.

I just tested on the PSP version, and it also only tallies experience for the weapon that was worn at the end of battle. Therefore, due to technical limitations and the effort required, I am going to leave that behavior as is rather than label it a bug. You and I both know that it doesn't make sense, but I'm going to label it "working as intended".


So now I'm looking for where in code the 5 spells usable outside of combat handle experience. The spells are Cure, Life, Esuna, Teleport, and Warp.
« Last Edit: January 19, 2020, 04:45:19 pm by redmagejoe »

Leviathan Mist

  • Jr. Member
  • **
  • Posts: 40
    • View Profile
Re: Final Fantasy II Disassembly / Bug Fix Project
« Reply #104 on: January 19, 2020, 03:54:09 pm »
I would think the only feasible way to do it, short of recoding the entire weapon leveling system, would be to disable the ability to switch weapons during battle.

Anyway, this is getting to the point where I feel comfortable attempting a playthrough using your patches. I will probably be streaming it live when I do.

redmagejoe

  • Full Member
  • ***
  • Posts: 178
    • View Profile
Re: Final Fantasy II Restored
« Reply #105 on: January 19, 2020, 04:33:15 pm »
That's awesome, Leviathan! Plugging the project is much appreciated! I'd like to make more progress on this if you're thinking of just using the existing patches, so I'll try to focus a bit more. I haven't posted the max spell level up fix yet because I still want to tack down the out-of-combat experience increase. It's usable right now, and all my desired change would do is stop spells from displaying "16-02" or the like if you use them outside of combat, but... I'm a bit of a perfectionist.

If you do decide you want to move forward with it soon, I'll go ahead and link the current version of that patch. I do want to get those spells working, so maybe I'll look at those after I finish this and the Evasion/MDef delay that I got distracted from. ::) Also updated my last post with my findings on the weapon skills.
« Last Edit: January 19, 2020, 04:45:31 pm by redmagejoe »

Cyneprepou4uk

  • Sr. Member
  • ****
  • Posts: 325
  • I am the baldest romhacker
    • View Profile
Re: Final Fantasy II Disassembly / Bug Fix Project
« Reply #106 on: January 19, 2020, 04:44:23 pm »
Instead of disabling weapon switching, you can add gained exp to the weapon you were using before it switches, and start counting exp for a new weapon from zero
iromhacker.ru - NES ROM hacking tutorials for beginners. Please use Google Translate browser extension

redmagejoe

  • Full Member
  • ***
  • Posts: 178
    • View Profile
Re: Final Fantasy II Restored
« Reply #107 on: January 19, 2020, 05:01:28 pm »
First post has been updated with Spell IDs, as well as expanding on important memory values alongside those provided by Jiggers. Spell IDs I want to look for are D0 (Warp), D4 (Cure), D5 (Life), D7 (Esuna), and E6 (Teleport).

Code: [Select]
; control flow target (from $ABBF, $ABE5, $AC5A)
0x03AC95|$0E:$AC85:A9 42    LDA #$42
0x03AC97|$0E:$AC87:85 E0    STA $E0
0x03AC99|$0E:$AC89:A6 6E    LDX $6E
0x03AC9B|$0E:$AC8B:A5 80    LDA $80
0x03AC9D|$0E:$AC8D:0A      ASL
0x03AC9E|$0E:$AC8E:05 6E    ORA $6E
0x03ACA0|$0E:$AC90:A8      TAY
0x03ACA1|$0E:$AC91:BD 0C 61 LDA $610C,X ; Character #1 Current MP low byte
0x03ACA4|$0E:$AC94:18      CLC
0x03ACA5|$0E:$AC95:F9 10 62 SBC $6210,Y ; Character #1 spell slot #1 level
0x03ACA8|$0E:$AC98:9D 0C 61 STA $610C,X ; Character #1 Current MP low byte
0x03ACAB|$0E:$AC9B:BD 0D 61 LDA $610D,X ; Character #1 Current MP high byte
0x03ACAE|$0E:$AC9E:E9 00    SBC #$00
0x03ACB0|$0E:$ACA0:9D 0D 61 STA $610D,X ; Character #1 Current MP high byte
0x03ACB3|$0E:$ACA3:B9 11 62 LDA $6211,Y ; Character #1 spell slot #1 experience
0x03ACB6|$0E:$ACA6:18      CLC
0x03ACB7|$0E:$ACA7:69 02    ADC #$02
0x03ACB9|$0E:$ACA9:C9 64    CMP #$64
0x03ACBB|$0E:$ACAB:B0 04    BCS $ACB1
0x03ACBD|$0E:$ACAD:99 11 62 STA $6211,Y ; Character #1 spell slot #1 experience
0x03ACC0|$0E:$ACB0:60      RTS

Think I found the code I was looking for! Guessing we need to have a JSR (need 2 bytes, we can remove CLC most likely and have a BCC or BCS as needed) to the previous routine for handling spell levels, or simply use free space to make a new sub-routine. Let me see if I can figure this out before abw swoops in with a delicious spoon to pop into my mouth. :P Would want it to be before LDA $6211,Y, and branch down to $ACB0... Let's see how many bytes I need. 7 bytes... we can steal one back by removing that CLC since if this doesn't branch, it means the Carry is already Clear. But we're in need of 6 bytes. Perhaps I should simply JSR here to the free space in ROM? Even then, we'd need to steal 2 more bytes for the 3 our JSR needs... I wonder if I can get away with just dragging more of the existing code into the sub-routine.

Code: [Select]
; code -> free
0x03FFDD|$0F:$FFCD:B9 10 62   LDA $6210,Y  ; Character #1 spell slot #1 level
0x03FFE0|$0F:$FFD0:C9 0E      CMP #$0E
0x03FFE2|$0F:$FFD2:AA         TAX
0x03FFE3|$0F:$FFD3:B0 03      BCS $FFD8
0x03FFE5|$0F:$FFD5:B9 11 62   LDA $6211,Y  ; Character #1 spell slot #1 experience
0x03FFE8|$0F:$FFD8:60         RTS

Code: [Select]
0x03ACB3|$0E:$ACA3:20 CD FF JSR $FFCD
0x03ACB6|$0E:$ACA6:B0 09        BCS $ACB1
0x03ACB8|$0E:$ACA8:69 02    ADC #$02   
0x03ACBA|$0E:$ACAA:C9 64    CMP #$64   
0x03ACBC|$0E:$ACAC:B0 05    BCS $ACB3
0x03ACBE|$0E:$ACAE:99 11 62 STA $6211,Y ; Character #1 spell slot #1 experience
0x03ACC1|$0E:$ACB1:60      RTS       
0x03ACC1|$0E:$ACB1:EA           NOP

0x03ACC3|$0E:$ACB3:8A           TXA
0x03ACC4|$0E:$ACB4:18      CLC       
0x03ACC5|$0E:$ACB5:69 01    ADC #$01   
0x03ACC7|$0E:$ACB7:C9 10    CMP #$10   
0x03ACC9|$0E:$ACB9:B0 08    BCS $ACC3 
0x03ACCB|$0E:$ACBB:99 10 62 STA $6210,Y ; Character #1 spell slot #1 level
0x03ACCE|$0E:$ACBE:A9 00    LDA #$00   
0x03ACD0|$0E:$ACC0:99 11 62 STA $6211,Y ; Character #1 spell slot #1 experience
0x03ACD3|$0E:$ACC3:60      RTS

Does this look like an acceptable solution? This is the way with the least amount of address-shifting I could think to work in the LDA $6210,Y => CMP #$0E => BCS

As for where to find free space should I have to resort to sub-routines, I'm assuming it's best to try and find the smallest free space available for the smallest additions, so that larger regions of memory can be used for more ambitious changes. I found a 19-byte region of free space near the end. I'll try to work within that for anything that I absolutely cannot cram into rewritten/optimized code, and beyond that there's a 21-byte and a 68-byte chunk, before I go anywhere near the 200+/400+/600+ regions. There's also two or three 3-byte chunks but there's not much you can do with 3 chunks in terms of sub-routines.

It appears to work, though I wish I could figure out a way to squish it in. It is what it is. If someone thinks it could be better optimized or there's a better way to apply this fix, I'll wait to finalize and put the IPS up. If this is the best solution, I'm thinking I'll also stick in 5 bytes after the TAX in my sub-routine with LDA #$00 and STA $6211,Y to force experience to 00. I only did that since I'm testing on a file where the exp is already above 00, but it's probably redundant and a waste of 5 bytes in what remains of the 7 bytes in that free space.

@abw I belive 0x03AB18 may be the start of the routine for swapping the positions of spells around in the menu, if you're still labeling.
« Last Edit: January 20, 2020, 01:48:14 am by redmagejoe »

Jiggers

  • Sr. Member
  • ****
  • Posts: 305
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy II Restored
« Reply #108 on: January 19, 2020, 08:32:04 pm »
There's surely some unused RAM somewhere. FF2 does seem to make better use of it than FF1, so I'm not sure where... In FF1, a bunch of map stuff is reloaded after battle, but the battle part of the game never touches it, so it could be overwritten and nothing would change on the map when returning (actually I should double-check that a battle doesn't reset bats in dungeons...)

In the code that backs up character stats before the battle begins, some of the player RAM might not be fully used. You could possibly save the starting weapon type there, then reference that at the end. If you start with a sword, switch to an axe... all the experience goes to the sword, still. Or would that just swap the issue? Start with a level 1 weapon, swap it in battle, and level it up faster by using the higher level weapon?
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.

redmagejoe

  • Full Member
  • ***
  • Posts: 178
    • View Profile
Re: Final Fantasy II Restored
« Reply #109 on: January 19, 2020, 08:41:11 pm »
Instead of disabling weapon switching, you can add gained exp to the weapon you were using before it switches, and start counting exp for a new weapon from zero

I'll add it to my improvements list and see how feasible that is. The negative side effect is that because of how the math works, there's a barrier you have to overcome based on growth penalties, and by forcing the math then and there, if you were to, say, switch from Daggers to Swords back to Daggers, you would have to overcome that penalty multiple times. This would be the same for the Swords. In essence it would make it, in scenarios where you're swapping weapons in battle, more difficult to level both weapon types, and possibly even give you no experience, as opposed to simply sacrificing the experience of one to boost the other (current behavior). I'll have to think carefully about this idea.

There's surely some unused RAM somewhere. FF2 does seem to make better use of it than FF1, so I'm not sure where... In FF1, a bunch of map stuff is reloaded after battle, but the battle part of the game never touches it, so it could be overwritten and nothing would change on the map when returning (actually I should double-check that a battle doesn't reset bats in dungeons...)

In the code that backs up character stats before the battle begins, some of the player RAM might not be fully used. You could possibly save the starting weapon type there, then reference that at the end. If you start with a sword, switch to an axe... all the experience goes to the sword, still. Or would that just swap the issue? Start with a level 1 weapon, swap it in battle, and level it up faster by using the higher level weapon?

Current behavior is that the weapon you are wearing at the end of battle gets all the experience, regardless what you started with. And yes, we don't want to swap the issue, and as stated above, trying to force it to track each weapon's experience would ultimately lead to making it harder for any one of the weapons swapped to gain experience. We would have to think about touching up the current formula to accommodate this new behavior. I'd rather wait until all the bugs are fixed before even thinking of such an undertaking, but I will give it serious consideration.



Updated my previous post with potential solutions to the out-of-combat experience gain.

And we're back to Evasion/Magic Defense delay! Boy it sure seems like I've let myself get distracted a lot from this. abw was kind enough to update the comments on them as well, so maybe I can actually finish this fix this time.

Code: [Select]
; level up Evasion
0x01660B|$05:$A5FB:A9 7D    LDA #$7D    ; $7D37; Character #1 counter for times physically attacked by enemy
0x01660D|$05:$A5FD:85 45    STA $45   
0x01660F|$05:$A5FF:A9 37    LDA #$37    ; $7D37; Character #1 counter for times physically attacked by enemy
0x016611|$05:$A601:85 44    STA $44   
0x016613|$05:$A603:AD 37 AC LDA $AC37  ; base Evasion credit for being physically attacked
0x016616|$05:$A606:85 46    STA $46    ; base credit amount for being attacked at all
0x016618|$05:$A608:A0 30    LDY #$30    ; Skill offset for Evasion level
0x01661A|$05:$A60A:84 47    STY $47   
0x01661C|$05:$A60C:20 42 A8 JSR $A842  ; level up Evasion/Magic Resist if applicable
; level up Magic Resist
0x01661F|$05:$A60F:A9 7D    LDA #$7D    ; $7D3B; Character #1 counter for times magically attacked by enemy
0x016621|$05:$A611:85 45    STA $45   
0x016623|$05:$A613:A9 3B    LDA #$3B    ; $7D3B; Character #1 counter for times magically attacked by enemy
0x016625|$05:$A615:85 44    STA $44   
0x016627|$05:$A617:AD 38 AC LDA $AC38  ; base Magic Resist credit for being magically attacked
0x01662A|$05:$A61A:85 46    STA $46    ; base credit amount for being attacked at all
0x01662C|$05:$A61C:A0 32    LDY #$32    ; Skill offset for Magic Resist level
0x01662E|$05:$A61E:84 47    STY $47   
0x016630|$05:$A620:20 42 A8 JSR $A842  ; level up Evasion/Magic Resist if applicable

Code: [Select]
; level up Evasion/Magic Resist if applicable
; control flow target (from $A60C, $A620)
0x016852|$05:$A842:A4 9E    LDY $9E    ; Character index
0x016854|$05:$A844:B1 44    LDA ($44),Y ; counter for times physically/magically attacked
0x016856|$05:$A846:F0 24    BEQ $A86C  ; no pain => no gain
0x016858|$05:$A848:18      CLC       
0x016859|$05:$A849:65 22    ADC $22    ; battle rank?
0x01685B|$05:$A84B:65 46    ADC $46    ; base credit amount for being attacked at all
0x01685D|$05:$A84D:A4 47    LDY $47    ; offset for Evasion/Magic Resist skill level, whichever one we're dealing with at the moment
0x01685F|$05:$A84F:F1 7E    SBC ($7E),Y ; Evasion/Magic Resist skill level
0x016861|$05:$A851:E9 0A    SBC #$0A    ; your first 10 points don't count for anything
0x016863|$05:$A853:90 17    BCC $A86C  ; not enough pain => no gain
0x016865|$05:$A855:C8      INY        ; offset for Evasion/Magic Resist skill experience, whichever one we're dealing with at the moment
0x016866|$05:$A856:71 7E    ADC ($7E),Y ; Evasion/Magic Resist skill experience
0x016868|$05:$A858:C9 64    CMP #$64   
0x01686A|$05:$A85A:90 0E    BCC $A86A  ; if experience < 100, just update experience, otherwise increase level
0x01686C|$05:$A85C:88      DEY        ; offset for Evasion/Magic Resist skill level, whichever one we're dealing with at the moment
0x01686D|$05:$A85D:B1 7E    LDA ($7E),Y ; Evasion/Magic Resist skill level
0x01686F|$05:$A85F:AA      TAX       
0x016870|$05:$A860:E8      INX        ; level++
0x016871|$05:$A861:20 6D A8 JSR $A86D  ; cap X at 15
0x016874|$05:$A864:8A      TXA       
0x016875|$05:$A865:91 7E    STA ($7E),Y ; save new Evasion/Magic Resist skill level
0x016877|$05:$A867:C8      INY        ; offset for Evasion/Magic Resist skill experience, whichever one we're dealing with at the moment
0x016878|$05:$A868:A9 00    LDA #$00    ; reset experience to zero
; control flow target (from $A85A)
0x01687A|$05:$A86A:91 7E    STA ($7E),Y ; save new Evasion/Magic Resist skill experience
; control flow target (from $A846, $A853)
0x01687C|$05:$A86C:60      RTS

Code: [Select]
; cap X at 15
; control flow target (from $A5D0, $A861, $A97A)
0x01687D|$05:$A86D:E0 10    CPX #$10   
0x01687F|$05:$A86F:90 02    BCC $A873 
0x016881|$05:$A871:A2 0F    LDX #$0F   
; control flow target (from $A86F)
0x016883|$05:$A873:60      RTS 

As I'm understanding more and more as I work with ASM and this game in particular, I find myself asking more pointed questions. It seems like this delay could be avoided altogether if instead of using indirect addressing in the STA ($7E),Y it used absolute addressing. Is there a reason, other than the extra byte needed, that we're not saving directly to the necessary values? Is there another step perhaps in "handing over" the value in $7E to its necessary memory location, or is $7E merely acting as a pointer to the correct place? What ramifications would there be in simply changing from indirect to absolute here? Would help if we actually knew what that absolute address was... Can't even tell from RAM mapping.

Interestingly, changing equipment or doing anything else that reinitializes stats also updates the MDef/Evasion level. We want this change to happen the moment we return from battle though, as UI elements shouldn't require the player to incite an update.



Distracted again! But I may have an overflow protection for combat counters.

Code: [Select]
Replace each INC $7xxx,X with a JSR to separate routines, or same routine if some clever addressing can be worked out.

0x031478|$0C:$9468:FE F3 7C INC $7CF3,X ; Character #1 physical attack counter
0x031E68|$0C:$9E58:FE F7 7C INC $7CF7,X ; Character #1 spell slot #1 battle use counter
0x031E75|$0C:$9E65:FE 3F 7D INC $7D3F,X ; Character #1 black magic use counter
0x031E7A|$0C:$9E6A:FE 43 7D INC $7D43,X ; Character #1 white magic use counter
0x03257B|$0C:$A56B:FE 37 7D INC $7D37,X ; Character #1 counter for times physically attacked by enemy
0x03261D|$0C:$A60D:FE 3B 7D INC $7D3B,X ; Character #1 counter for times magically attacked by enemy

BD F3 7C   LDA $7CF3,X   <<< this is the crux of whether we can use a single sub-routine or need 6
C9 08      CMP #$C8      <<< cap counter at 200 so overflow won't happen in any scenario
B0 03      BCS +3
FE F3 7C   INC $7CF3,X   <<< this too
60         RTS

@abw: Please see my previous post and this one and give your input. I know it's a lot to read, but I feel like I'm thriving with your support. :)
« Last Edit: January 20, 2020, 04:26:40 am by redmagejoe »

Disch

  • Hero Member
  • *****
  • Posts: 2770
  • NES Junkie
    • View Profile
Re: Final Fantasy II Restored
« Reply #110 on: January 20, 2020, 02:37:55 am »
is $7E merely acting as a pointer to the correct place?

This.

Code: [Select]
STA ($7E),Y does not write to $7E+Y, it reads a pointer from $7E,$7F, adds Y to that pointer, and writes to that address.

Changing this to absolute likely isn't possible unless $7E/7F contain the exact same values every time this code is run.



PS:  Yes, this is Disch.  Computer problems made my several years long "automatically sign me in" thing drop, I have no idea what my password is, and I can't reset it because I created the account like a decade ago and have no clue what email I used with it or even if I still have that email, so I had to make a new account

Cyneprepou4uk

  • Sr. Member
  • ****
  • Posts: 325
  • I am the baldest romhacker
    • View Profile
Re: Final Fantasy II Restored
« Reply #111 on: January 20, 2020, 07:16:41 am »
Quote
As I'm understanding more and more as I work with ASM and this game in particular, I find myself asking more pointed questions. It seems like this delay could be avoided altogether if instead of using indirect addressing in the STA ($7E),Y it used absolute addressing. Is there a reason, other than the extra byte needed, that we're not saving directly to the necessary values? Is there another step perhaps in "handing over" the value in $7E to its necessary memory location, or is $7E merely acting as a pointer to the correct place? What ramifications would there be in simply changing from indirect to absolute here? Would help if we actually knew what that absolute address was... Can't even tell from RAM mapping.

Absolute is a direct $0000-$FFFF range.

Indirect LDA($7E),Y is when you put low byte of address in 007E, and high in $007F. Then you add Y value like in indexed addressing.

If you change indirect to absolute to make code work the same exact way as it currently does, it won't actually change anything in a scale of a frame. Speaking of magic delays or whatever.
iromhacker.ru - NES ROM hacking tutorials for beginners. Please use Google Translate browser extension

redmagejoe

  • Full Member
  • ***
  • Posts: 178
    • View Profile
Re: Final Fantasy II Restored
« Reply #112 on: January 20, 2020, 01:18:23 pm »
Hmm, so it's not an issue of addressing... I just can't wrap my head around what instruction is missing that's carried out when you change equipment or enter a battle. All other stats update on the stat screen, and even the value on RAM Watch for the level reads 1 less than it should be until something triggers the update. So I don't think it's that the value isn't being loaded, simply that it's not actually been stored. Also, hey Disch! Lots of legacies popping into this thread, it's pretty humbling. :D

If I better understood how exactly indirect addressing was utilized (i.e. How are the absolute addresses being pointed to by those pointers in the first place? Surely it's not implicit?), I could actually probably use that exact idea for my proposed fix to the combat counters, and have a single subroutine that simply uses an indirect indexed (Y) Load Accumulator and then Increment with all six counters initializing (?) to the same pointer before going to the JSR. It's not the end of the world if I have to make six nearly identical sub-routines, but I'd like to avoid taking up more of our available free bytes than necessary.
« Last Edit: January 20, 2020, 04:06:39 pm by redmagejoe »

Jiggers

  • Sr. Member
  • ****
  • Posts: 305
    • View Profile
    • My Ko-Fi Page
Re: Final Fantasy II Restored
« Reply #113 on: January 20, 2020, 05:22:01 pm »
In my own disassembly, I can't find "STA $7E" in the one and a half banks I sorta set up... but in my notes, $7E seems to be the pointer to character stats... No idea if that's what is being in the code you're talking about. But either way it works the same! So I'll see if I can help explain it. And hopefully not come off as condescending... just trying to help!

So first, the first byte of each character's stats has an absolute address in RAM:
$6100 - character 1
$6140 - character 2
$6180 - character 3
$61C0 - character 4

So there might be a table with the bytes laid out, 61, 00, 61, 40...

There's usually a variable that's just 0 to 3, depending on what character is being looked at, whose turn it is. That number gets doubled, then the pointers get loaded like so:

Code: [Select]
LDA CharacterNumber            ; (0-3)
ASL A                          ; double it
TAX
LDA CharacterPointerTable, X   ; Gets $00, $40, $80, or $C0
STA $7E                        ; becomes low byte of pointer
LDA CharacterPointerTable+1, X ; gets $61
STA $7F                        ; becomes high byte of pointer

Then Y can be an offset to a specific stat, $0 to $3F

So that LDA ($7E), Y (where Y is 9) is then looking at $6109, or $6149, or so on!
« Last Edit: January 20, 2020, 05:28:04 pm by Jiggers »
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.

redmagejoe

  • Full Member
  • ***
  • Posts: 178
    • View Profile
Re: Final Fantasy II Restored
« Reply #114 on: January 20, 2020, 05:30:03 pm »
I see, I see! So a table will be predefined in the code itself for using the indirect addressing... So basically if my desired absolute address doesn't already have a table and pointer setup routine already... I won't be able to use indirect addressing for it. Hmm, that may force me to use more space for routines than I'd like to, but I'll start searching the disassembly to see if those base addresses I listed above (all six counters one after another) have any place in a table. Or I guess I could consider inserting a routine that does this, though at that point it comes down to whether it would save more space to do that than simply have 6x 11-byte routines (which would become a single 9-byte routine otherwise). So 66 bytes vs 9+X, X would have to be less than or equal to 57 bytes for it to be worth writing up a table and/or routine.

I haven't made a test patch yet, because while I'm confident that my mock-up would address the issue (as it's a pretty simple change), I'm hoping I can figure out some way to avoid eating up 66 bytes in our precious free space. After all, depending on where this project goes, I may need all the free space I can get.
« Last Edit: January 20, 2020, 07:05:17 pm by redmagejoe »

PresidentLeever

  • Hero Member
  • *****
  • Posts: 670
    • View Profile
    • Mini-Revver
Re: Final Fantasy II Restored
« Reply #115 on: January 20, 2020, 07:30:20 pm »
For improvements, would it be possible to group certain spells and weapons together to reduce grinding? This was done for Secret of Evermore for a reference.
Another method could be to check a char's skill with a school of magic and make a new spell start at a higher level based on that.

The other big thing would be the many trap rooms in dungeons, they suck.
Mini-reviews, retro sound chip tribute, romhacks and general listage at my site: Mini-Revver.

redmagejoe

  • Full Member
  • ***
  • Posts: 178
    • View Profile
Re: Final Fantasy II Restored
« Reply #116 on: January 20, 2020, 10:11:24 pm »
For improvements, would it be possible to group certain spells and weapons together to reduce grinding? This was done for Secret of Evermore for a reference.
Another method could be to check a char's skill with a school of magic and make a new spell start at a higher level based on that.

The other big thing would be the many trap rooms in dungeons, they suck.

While I'm not averse to the concept of the suggestion, I'd rather not depart from the original or remake's behaviors enough for it to come off as a brand new feature. This patch's purpose is to restore the game to what the devs likely intended in behavior, rather than to enhance. Having said that, your proposal may be more feasible as a result of this project and the progression of the existing disassembly.

abw

  • Sr. Member
  • ****
  • Posts: 344
    • View Profile
Re: Final Fantasy II Restored
« Reply #117 on: January 20, 2020, 11:52:02 pm »
It appears to work, though I wish I could figure out a way to squish it in.
Not bad, young padawan, but I think we can still do this without using any extra free space. Try the below, which also saves 3 bytes compared to the original:

Code: [Select]
org $3ACB3
base $ACA3

LDA $6210,Y ; Character #1 spell slot #1 level
CMP #$0F
BCS done ; already at least level 16 => no change
LDA $6211,Y ; Character #1 spell slot #1 experience
ADC #$02 ; +2 spell experience; we know carry is clear
CMP #$64
BCC up_exp ; new spell experience < 100 => just update spell experience
LDA $6210,Y ; Character #1 spell slot #1 level
ADC #$00 ; +1 spell level; we know carry is set
STA $6210,Y ; Character #1 spell slot #1 level
LDA #$00 ; reset spell experience to 0
up_exp:
STA $6211,Y ; Character #1 spell slot #1 experience
done:
RTS

@abw I belive 0x03AB18 may be the start of the routine for swapping the positions of spells around in the menu, if you're still labeling.
Thanks, I'll be sure to update that!

Current behavior is that the weapon you are wearing at the end of battle gets all the experience, regardless what you started with.
I like the idea of keeping track of each weapon type's usage separately, but I don't think our RAM map is fleshed out enough at the moment to be able to do this safely. Each spell slot already has its own counter, so I think we'd need an extra 7 weapon types * 4 characters = 28 bytes. That should be possible, it'll just take some more analysis work.

@abw: Please see my previous post and this one and give your input. I know it's a lot to read, but I feel like I'm thriving with your support. :)
Looks like you've got plenty of feedback on this one already - there's not much more for me to add ;).

PS:  Yes, this is Disch.  Computer problems made my several years long "automatically sign me in" thing drop, I have no idea what my password is, and I can't reset it because I created the account like a decade ago and have no clue what email I used with it or even if I still have that email, so I had to make a new account
I'm assuming you thought of this already, but just checking the obvious options: do you get email notices for PMs or posts on threads you're involved in? Otherwise Contact Staff seems like the next best alternative.


In other news...
  • The middle two characters, Guy and Maria, are biased by the enemy targetting algorithm, taking significantly more enemy focus than Firion or Character #4. Investigate cause and improve RNG, which is likely the cause.
Want to give this a shot? I'm not sure whether this is enough to completely fix the issue, but the RNG was definitely biased, so at the very least it's a necessary step in the right direction (assuming your goal is a "fair" RNG, anyway).

redmagejoe

  • Full Member
  • ***
  • Posts: 178
    • View Profile
Re: Final Fantasy II Restored
« Reply #118 on: January 21, 2020, 12:19:10 am »
Amazing as always, abw. I compared yours side by side with the original and against mine, and I see how you managed to get that space. Clever use of rearranging conditionals to clear allowed you to essentially compress the entire other subroutine into the one above it. Now I definitely have to list you as the main credit for this patch. It looks awesome though, plus then we don't have to eat into any of our free space chunks yet! Which is good, because I have a pretty ambitious intention for some of those...

So do you think that for my counter solution, the better choice is to use 6x 11 byte subroutines? I don't even know how to begin looking for where those table and pointer setup routines are in the disassembly, but I'm guessing that they simply don't exist for the addresses we want if they were using absolute addressing. Do you have an idea how many bytes it would take to create something like that for our counters, and at that point, is it better for me to just make 6 almost identical subroutines like my mockups in free space?

Looking at the RNG fix now. And yes, the idea was to make a fair RNG that gives all 4 characters as close to an equal chance of being targeted as possible. If this does indeed cause a more uniform distribution as opposed to what before could be described as 12.5% (0-63), 37.5% (64-127), 37.5% (128-191), 12.5% (192-255), then it should solve the problem outright. I can't imagine it will harm any other mechanics in the game, as RNG should aim to be uniform anyway in this game's combat systems. Would you like to keep this under review as per your notes about avoiding that free space, or do you want me to slap it on the first post?

Code: [Select]
Replace each INC $7xxx,X with a JSR to separate routines, or same routine if some clever addressing can be worked out.

0x031478|$0C:$9468:FE F3 7C INC $7CF3,X ; Character #1 physical attack counter
0x031E68|$0C:$9E58:FE F7 7C INC $7CF7,X ; Character #1 spell slot #1 battle use counter
0x031E75|$0C:$9E65:FE 3F 7D INC $7D3F,X ; Character #1 black magic use counter
0x031E7A|$0C:$9E6A:FE 43 7D INC $7D43,X ; Character #1 white magic use counter
0x03257B|$0C:$A56B:FE 37 7D INC $7D37,X ; Character #1 counter for times physically attacked by enemy
0x03261D|$0C:$A60D:FE 3B 7D INC $7D3B,X ; Character #1 counter for times magically attacked by enemy

BD F3 7C   LDA $7CF3,X   <<< this is the crux of whether we can use a single sub-routine or need 6
C9 08      CMP #$C8      <<< cap counter at 200 so overflow won't happen in any scenario
B0 03      BCS +3
FE F3 7C   INC $7CF3,X   <<< this too
60         RTS

66 for 6 11-byte sub-routines similar to the above. If indirect addressing can be used, it becomes 9, bringing us to 57 as the magic number. Based on Jiggers's statement about the way the data table would be laid out if we needed to make a new table, we'd be looking at 7C F3 7C F7 7D 37 7D 3B 7D 3F 7D 43... That's another 12, bringing us to 45. So if the code for indirect addressing can be fit into 45 bytes, then that would be the optimal way to approach this fix. I just have to find an example of how it works in the disassembly. Just searching the HEX in the ROM I already know no table already exists for these addresses, as 7C F3 in that order only occurred twice and they didn't seem relevant based on the surrounding bytes.
« Last Edit: January 21, 2020, 01:03:51 am by redmagejoe »

Cyneprepou4uk

  • Sr. Member
  • ****
  • Posts: 325
  • I am the baldest romhacker
    • View Profile
Re: Final Fantasy II Restored
« Reply #119 on: January 21, 2020, 08:24:01 am »
total 29 bytes for all players and all your 6 addresses

code = 23 bytes
A005B98888186500AABDF37CC9C8B003FEF37C8810EC60

table = 6 bytes
000444484C50

Code: [Select]
00:8100: A0 05     LDY #$05       ; loop counter
00:8102: B9 88 88  LDA $8888,Y    ; load base offset from table (put somewhere bytes 00 04 44 48 4C 50 and read them)
00:8105: 18        CLC            ; clear C for normal addition
00:8106: 65 00     ADC $00        ; add player id for offset (don't know the actual address)
00:8108: AA        TAX            ; put offset into X
00:8109: BD F3 7C  LDA $7CF3,X    ; load value
00:810C: C9 C8     CMP #$C8       ; compare to overflow
00:810E: B0 03     BCS $8113      ; skip increasing
00:8110: FE F3 7C  INC $7CF3,X    ; increase value
00:8113: 88        DEY            ; next check
00:8114: 10 EC     BPL $8102      ; repeat loop
00:8116: 60        RTS            ; if code is used as a subroutine
« Last Edit: January 21, 2020, 08:59:56 am by Cyneprepou4uk »
iromhacker.ru - NES ROM hacking tutorials for beginners. Please use Google Translate browser extension