Title: SwdTech Suspend Author: SilentEnigma Version: 1.0 Release Date: 2023-04-23 Applies to: Final Fantasy III (v1.0) (U) Archive Contents ------------------------------- readme.txt = this file SwdTechSuspend_H.ips = patch for headered ROMs SwdTechSuspend_H_Anti.ips = anti-patch for headered ROMs SwdTechSuspend_NH.ips = patch for unheadered ROMs SwdTechSuspend_NH_Anti.ips = anti-patch for unheadered ROMs ROM Addresses ------------------------------- C1/0253 - C1/0256, C1/561B - C1/561D, C1/5A45 - C1/5A49, C1/7D41 - C1/7D44, C1/7D56 - C1/7D5D, C2/258D - C2/258F Free Space Used ------------------------------- C2/A6A8 - C2/A735, C2/A784 - C2/A7B5 (192 bytes) Additional RAM used ------------------------------- $7EECFC - $7EECFF (4 bytes) TABLE OF CONTENTS ------------------------------- 0. Description 1. Relevant Offsets & Disassembly 2. Credits 3. Revision History 4. Legal ________________________________________________________________________________ 0. DESCRIPTION ________________________________________________________________________________ This patch modifies the SwdTech charging mechanic in FF3us (FF6) for SNES. SwdTech is often criticized for its slow charge time and the inability for other characters to receive commands while charging. Modern rereleases of FF6 address this by allowing the gauge to charge in the background. This patch is intended to improve the mechanic in the SNES version by adding character switching support to the SwdTech gauge window. While the SwdTech gauge is charging for a character, the player may now press the X or Y button to switch between characters. The gauge position is held in the background while other characters receive commands. Once input returns to the charging character, the SwdTech gauge window automatically reopens. The gauge will resume from the next tier number past its previous position. This allows the player to give other characters commands without losing progress on any charging SwdTech gauge(s). The feature is disabled if there are no other characters ready for input. A character's SwdTech gauge resets if the player cancels the gauge window or the character becomes incapacitated. This patch uses 4 additional bytes of RAM to hold the value of each character's SwdTech gauge progress. ________________________________________________________________________________ 1. RELEVANT OFFSETS & DISASSEMBLY ________________________________________________________________________________ Incapacitated character check ------------------------------- Original: C1/0253: 9D 01 40 STA $4001,X ; battle menu order for ally at index x C1/0256: 6B RTL Modified: C1/0253: 5C A8 A6 C2 JML $C2A6A8 ; new subroutine C2/A6A8 New Subroutine C2/A6A8: C2/A6A8: 9D 01 40 STA $4001,X ; original C1/0253 C2/A6AB: 9E FC EC STZ $ECFC,X ; UNUSED RAM for ally's held SwdTech timer C2/A6AE: 6B RTL SwdTech command ------------------------------- Original: C1/561B: 9C 82 7B STZ $7B82 ; (from C1/5529, X = 35) reset SwdTech gauge Modified: C1/561B: EA NOP ; typical reset moved to C2/A72A C1/561C: EA NOP C1/561D: EA NOP Top menu - button check ------------------------------- Original: C1/5A44: D0 03 BNE $5A49 C1/5A46: 4C 4A 5A JMP $5A4A C1/5A49: 60 RTS Modified: C1/5A44: D0 FA BNE $5A40 ; to RTS at C1/5A40 C1/5A46: 5C 09 A7 C2 JML $C2A709 ; new subroutine C2/A709 New Subroutine C2/A709: ; X holds ally ID (0 - 3) C2/A709: AD C3 7B LDA $7BC3 ; pending menu state C2/A70C: C9 05 CMP #$05 ; top menu? C2/A70E: D0 22 BNE $A732 ; Branch if not C2/A710: AD CB 7B LDA $7BCB ; should be closing menu? C2/A713: D0 1D BEQ $A732 ; If so, branch C2/A715: AE CA 62 LDX $62CA ; current character C2/A718: BD FC EC LDA $ECFC,X ; UNUSED RAM held SwdTech gauge value C2/A71B: F0 12 BEQ $A72F ; branch if zero C2/A71D: 29 E0 AND #$E0 ; snap counter to start of current number C2/A71F: 18 CLC C2/A720: 69 20 ADC #$20 ; gauge bonus C2/A722: 8D 82 7B STA $7B82 ; SwdTech counter C2/A725: 8A TXA ; current character C2/A726: 85 22 STA $22 ; multiplier function input C2/A728: A9 0C LDA #$0C ; cmd info size per ally (4 cmds x 3 bytes) C2/A72A: 85 24 STA $24 ; multiplier function input C2/A72C: 4C 84 A7 JMP $A784 ; new subroutine C2/A784 C2/A72F: 9C 82 7B STZ $7B82 ; reset SwdTech gauge from original C1/561B C2/A732: 5C 4A 5A C1 JML $C15A4A ; replaces original C1/5A46 New Subroutine C2/A784: C2/A784: DA PHX C2/A785: 22 D5 18 C1 JSL $C118D5 ; multiplier function C2/A789: A6 26 LDX $26 ; product C2/A78B: A4 00 LDY $00 ; loop init C2/A78D: BD 2E 20 LDA $2E20,X ; command ID for this ally & command C2/A790: C9 07 CMP #$07 ; SwdTech? C2/A792: F0 0C BEQ $A7A0 ; if so, break C2/A794: E8 INX ; 3 info bytes per command C2/A795: E8 INX C2/A796: E8 INX C2/A797: C8 INY c2/A798: C0 04 00 CPY #$0004 ; 4 commands C2/A79B: D0 F0 BNE $A78D ; loop management C2/A79D: 4C 2F A7 JMP $A72F ; something went wrong - abort C2/A7A0: 98 TYA ; SwdTech command position for current ally C2/A7A1: EE 41 2F INC $2F41 ; flag - Command has been selected C2/A7A4: FA PLX C2/A7A5: 9D 0F 89 STA $890F,X ; cursor position for this character C2/A7A8: 5C C8 7C C1 JML $C17CC8 ; C1/7CC9 - top menu cmd has been selected Upon confirming SwdTech number ------------------------------- Original: C1/7D41: 4A LSR A C1/7D42: 4A LSR A C1/7D43: 4A LSR A C1/7D44: 4A LSR A Modified: C1/7D41: 22 F8 A6 C2 JSL $C2A6F8 ; new subroutine C2/A6F8 New Subroutine C2/A6EF: ; Reset held gauge value for current ally C2/A6EF: DA PHX C2/A6F0: AE 01 02 LDX $0201 ; current ally ID (0 - 3) C2/A6F3: 9E FC EC STZ $ECFC,X ; UNUSED RAM reset held SwdTech gauge value C2/A6F6: FA PLX C2/A6F7: 60 RTS New Subroutine C2/A6F8: ; Reset held gauge, etc. C2/A6F8: 20 EF A6 JSR $A6EF ; new subroutine C2/A6EF - reset held gauge C2/A6FB: 4A LSR A ; original C1/7D41 C2/A6FC: 4A LSR A ; original C1/7D42 C2/A6FD: 4A LSR A ; original C1/7D43 C2/A6FE: 4A LSR A ; original C1/7D44 C2/A6FF: 6B RTL In SwdTech gauge - button check ------------------------------- Original: C1/7D56: A5 09 LDA $09 ; Pressed mapped B this frame (& not last)? C1/7D58: 10 05 BPL $7D5F ; Branch if not Modified: C1/7D56: 5C B9 A6 C2 JML $C2A6B9 ; new subroutine C2/A6B9 New Subroutine C2/A6AF: ; hold current gauge value C2/A6AF: AE 01 02 LDX $0201 ; current ally ID (0 - 3) C2/A6B2: AD 82 7B LDA $7B82 ; SwdTech counter C2/A6B5: 9D FC EC STA $ECFC,X ; UNUSED RAM hold SwdTech counter value C2/A6B8: 60 RTS New Subroutine C2/A7A3: ; check for available allies & X/Y button press C2/A6B9: A5 09 LDA $09 ; original C1/7D56 C2/A6BB: 10 04 BPL $A7AB ; replaces original C1/7D58 C2/A6BD: 5C 5A 7D C1 JML $C17D5A ; Player has canceled SwdTech gauge C2/A6C1: DA PHX C2/A6C2: A2 03 00 LDX #$0003 ; loop init C2/A6C5: BD 01 40 LDA $4001,X ; character order C2/A6C8: 3A DEA ; Is ally current (00) or inactive (FF)? C2/A6C9: 10 05 BPL $A6D0 ; If not, branch to button check C2/A6CB: CA DEX C2/A6CC: 10 F7 BPL $A6C5 ; loop management C2/A6CE: 80 1A BRA $A6EA ; No ally available; branch to abort C2/A6D0: A5 09 LDA $09 ; buttons just pressed this frame (incl. Y) C2/A6D2: 0A ASL A ; Y pressed this frame (but not last)? C2/A6D3: 10 08 BPL $A6DD ; Branch if not C2/A6D5: 20 AF A6 JSR $A6AF ; new subroutine C2/A6AF - hold gauge value C2/A6D8: FA PLX C2/A6D9: 5C C0 7A C1 JML $C17AC0 ; C1/7AC0 - Y press at top menu C2/A6DD: A5 08 LDA $08 ; buttons just pressed this frame (incl. X) C2/A6DF: 0A ASL A ; X pressed this frame (but not last)? C2/A6E0: 10 08 BPL $A6EA ; Branch if not C2/A6E2: 20 AF A6 JSR $A6AF ; new subroutine C2/A6AF - Hold gauge value C2/A6E5: FA PLX C2/A6E6: 5C AF 7A C1 JML $C17AB1 ; C1/7AB1 - X press at top menu C2/A6EA: FA PLX C2/A6EB: 5C 5F 7D C1 JML $C17D5F ; C1/7D5F - abort Upon canceling SwdTech gauge ------------------------------- Original: C1/7D5A: E6 96 INC $96 ; player has canceled SwdTech gauge C1/7D5C: 4C 28 56 JMP $5628 ; close SwdTech gauge window Modified: C1/7D5A: 5C 00 A7 C2 JML $C2A700 ; new subroutine C2/A700 New Subroutine C2/A700: C2/A700: 20 EF A6 JSR $A6EF ; new subroutine C2/A6EF - reset held gauge C2/A703: E6 96 INC $96 ; original C1/7D5A C2/A705: 5C 28 56 C1 JML $C15628 ; replaces original C1/7D5C Battle initialization ------------------------------- Original: C2/258D: A9 FF 03 LDA #$03FF ; 10 bits set, 10 possible entities in battle Modified: C2/258D: 20 AC A7 JSR $A7AC ; new subroutine C2/A7AC New Subroutine C2/A7AC: C2/A7AC: 9C FC EC STZ #$ECFC ; UNUSED RAM held SwdTech gauge (slot 1 & 2) C2/A7AF: 9C FE EC STZ #$ECFE ; UNUSED RAM held SwdTech gauge (slot 3 & 4) C2/A7B2: A9 FF 03 LDA #$03FF ; original C2/258D C2/A7B5: 60 RTS ________________________________________________________________________________ 2. CREDITS ________________________________________________________________________________ Special Thanks: Imzogelmo, for his disassembly of bank C1 http://www.angelfire.com/al2/imzogelmo/patches.html C-Dude, for pointing out a plethora of unused battle RAM ________________________________________________________________________________ 3. REVISION HISTORY ________________________________________________________________________________ 2023-04-23 : Version 1.0 released ________________________________________________________________________________ 4. LEGAL ________________________________________________________________________________ Copyright (C) 2023 David R. Thompson (SilentEnigma). The copyright holder ("author") permits the free use of the attributed work referenced by this document exclusively for non-commercial purposes, provided that the following conditions are met: 1. The author and all contributors credited in this readme document shall be given credit for their respective contributions wherever the attributed work is reused, redistributed, or modified. 2. This readme document shall accompany any of the files comprising the attributed work wherever they are redistributed in unmodified form. The work(s) and file(s) distributed with this document are provided "AS-IS", WITHOUT ANY WARRANTY. The author shall not be held responsible for any damages related to the use of work(s) and file(s) distributed with this document.