Title: Adaptive Dialog Engine Author: SilentEnigma Version: 1.0 Release Date: 2021-06-01 Applies to: Final Fantasy III (v1.0) (U) Contents: AdaptiveDialogEngine_H.ips = the patch for headered ROMs AdaptiveDialogEngine_NH.ips = the patch for unheadered ROMs examples.txt = examples doc covering common use cases in FF3usME export format readme.txt = this file ROM Addresses: C0/8072 - C0/8075, C0/80B0 - C0/80B3, C0/80C6 - C0/80D1, C0/80D9 - C0/80DB C0/8284 - C0/828C, C0/829D - C0/82B1, C0/82B3 Free space used: 320 bytes C0/D8E0 - C0/DA1F RAM used: direct page $40 - $42 (unused field RAM) -------------------------------------------------- TABLE OF CONTENTS 0. Description 1. Feature Details 2. Relevant Offsets & Disassembly 3. Credits 4. Revision History 5. Legal ________________________________________________________________________________ 0. DESCRIPTION ________________________________________________________________________________ The Adaptive Dialog Engine extends the functionality of the field dialog script interpreter in FF3us. Most importantly, it allows script hackers to make context-sensitive customizations to the text within individual captions. The following new capabilities are added: 1. Actor Name insertion, by: > Player character > Sprite at specified field map location > Actor ID in RAM 2. Conditional branching, by: > Player character > Status of bits in RAM > Number of characters in the active party 3. RAM Bitwise OR (for setting bits in memory) 4. RAM Bitwise AND (for resetting bits in memory) The new functions and their IDs have been defined for somewhat user-friendly integration with the FF3us Multi Editor (FF3usME) Town Dialog editor. ________________________________________________________________________________ 1. FEATURE DETAILS ________________________________________________________________________________ Contents: a. New Symbol Definitions b. New Command Definitions i. Actor Name Insertion ii. Conditional Branching iii. RAM Bitwise OR iv. RAM Bitwise AND c. Control Function Definitions i. _get_party_member ii. _ram_byte_divide iii. _ram_byte_bitwise_and iv. _get_party_size ================================================== a. New Symbol Definitions ================================================== The following field dialog commands are now defined: FF3usME Hex Symbol Identifier ---- ------- ------------------------------ #$76 $ _INSERT( ( ... ) ) #$79 _OR_BITS( ..., ... ) #$7A _AND_BITS( ..., ... ) #$7B { _SWITCH( ( ... ), ... ) #$7C } _ENDSWITCH #$7D ` _BREAK #$7E | _ENDARGS _INSERT and _SWITCH commands must be invoked with a "Control" modifier. The following Control functions are defined: FF3usME Hex Symbol Identifier ---- ------- ------------------------------ #$54 0 _get_party_member( ... ) #$55 1 _ram_byte_divide( ..., ... ) #$56 2 _ram_byte_bitwise_and( ..., ... ) #$57 3 _get_party_size() Some of the Control functions take parameters in the form of encoded hex values. Hex digit encoding is defined by the following: Encoded character value = #$54 + n, where n is the intended hex digit value. Using FF3usME dialog editor encoding, this yields: Hex value: 0 1 2 3 4 5 6 7 8 9 A B C D E F Symbol: 0 1 2 3 4 5 6 7 8 9 ! ? / : ] ' ================================================== b. New Command Definitions ================================================== Contents: i. Actor Name Insertion ii. Conditional Branching iii. RAM Bitwise OR iv. RAM Bitwise AND ------------------------------- i. Actor Name Insertion ------------------------------- Command Identifier: _INSERT( ( ... ) ) Syntax: $x| Syntax Breakdown: $ _INSERT symbol x ( ... ) | _ENDARGS symbol Description: Inserts a context-sensitive Actor Name. The _INSERT command is processed as follows: 1) The given Control function is evaluated, yielding result N. 2) N is interpreted as an Actor I.D. and converted to the corresponding Actor symbol. (Actor symbol value = N + 2) 3) The Actor symbol is read into the raw caption data. Example(s): "a. Insert name of party leader" "b. Insert name of actor on field map" "e. Specialize text for second party member" ------------------------------- ii. Conditional Branching ------------------------------- Command Identifier: _SWITCH( ( ... ), ... ) Syntax: {w|x|y`z} Syntax breakdown: { _SWITCH symbol w ( ... ) | _ENDARGS symbol x Branch Map (arbitrary length n) | _ENDARGS symbol y branch-text 0, 1, ..., n-2 ` _BREAK symbol at end of branch(es) 0, 1, ..., n-2 z branch-text n-1 } _ENDSWITCH symbol Description: Branches text with a pseudo-switch statement. The _SWITCH command is processed as follows: 1) The given Control function is evaluated, yielding result M. 2) M is used as an intermediate index for retrieving branch text index N from the given "Branch Map": - The Branch Map is an array of indices linking each anticipated Control result to its corresponding text branch. - Each element of the map must be a single character-encoded hex digit. (See encoding explanation in section 1.a.) - The same value can appear more than once in a map; in other words, multiple Control result values can be mapped to the same text branch. - The map is terminated with the _ENDARGS symbol. - If Control result M is greater than the number of entries in the Branch Map, the last entry of the map is used as the text index (N). 3) The caption readout jumps to the text branch indexed by N. - Branches are terminated with the _BREAK symbol. - A _BREAK symbol is not required for the last branch. 4) After the appropriate branch of text has been read, the caption skips to the end of the switch statement. - Switch statements are terminated with the _ENDSWITCH symbol. For example, if the Control function evaluates to 0, then the first entry in the Branch Map is retrieved. If the entry retrieved from the Branch Map is 2, then the third text branch is displayed in the caption. Nested _SWITCH blocks are supported. Example(s): "c. Specialize text based on party size" "d. Specialize text based on the party leader" "e. Specialize text for second party member" "f. Set flag if TERRA is in the active party" "g. Start new line on current page, if possible" ------------------------------- iii. RAM Bitwise OR ------------------------------- Command Identifier: _OR_BITS( ..., ... ) Syntax: <$79>xxxxyy| Syntax breakdown: <$79> _OR_BITS symbol xxxx encoded 16-bit RAM address (Big-endian) yy encoded 8-bit operand | _ENDARGS symbol Description: Performs a bitwise OR operation on a byte in RAM with the given operand. Command may be used to set individual RAM bits or event flags. The 16-bit RAM address and the 8-bit operand are given as character-encoded hex digits. (See encoding explanation in section 1.a.) Example(s): "f. Set flag if TERRA is in the active party" ------------------------------- iv. RAM Bitwise AND ------------------------------- Command Identifier: _AND_BITS( ..., ... ) Syntax: <$7A>xxxxyy| Syntax Breakdown: <$7A> _AND_BITS symbol xxxx encoded 16-bit RAM address (Big-endian) yy encoded 8-bit operand | _ENDARGS symbol Description: Performs a bitwise AND operation on a byte in RAM with the given operand. Command may be used to reset individual RAM bits or event flags. The 16-bit RAM address and the 8-bit operand are given as character-encoded hex digits. (See encoding explanation 1.a.) Example(s): "f. Set flag if TERRA is in the active party" ================================================== c. Control Function Definitions ================================================== Contents: i. _get_party_member ii. _ram_byte_divide iii. _ram_byte_bitwise_and iv. _get_party_size ------------------------------- i. _get_party_member ------------------------------- Syntax: 0x Syntax Breakdown: 0 _get_party_member symbol x party member rank (0, 1, 2, or 3) Description: Returns the Actor I.D. for the party member of the given rank. - Rank is specified using a single character-encoded hex digit. (See explanation in 1.a.) - Rank 0 indicates the lead party member. - Return value is undefined if the given rank is greater than the active party size minus one (if x > party_size - 1). Example(s): "a. Insert name of party leader" "d. Specialize text based on the party leader" "e. Specialize text for second party member" ------------------------------- ii. _ram_byte_divide ------------------------------- Syntax: 1xxxxyy Syntax Breakdown: 1 _ram_byte_divide symbol xxxx encoded 16-bit RAM address (Big-endian) yy encoded 8-bit divisor for the value read from RAM Description: Reads a byte at the given RAM address $0000 - $FFFF. As long as the RAM byte is not equal to #$FF, the function returns this value divided by the given divisor. RAM byte values equal to #$FF are considered invalid. - Multiple address-divisor pairs may be listed in sequence, with the latter pairs designated as fallbacks in case the RAM byte is invalid: 1xxxxyyxxxxyyxxxxyy - If the _ENDARGS symbol (or any symbol of value >= #$64) is reached before a valid RAM byte has been found, then the value of that symbol is returned. - To avoid the insertion of the _ENDARGS symbol, it is recommended to define the list of address-divisor pairs such that at least one of the pairs is guaranteed to yield a valid RAM byte. Address-divisor pairs are specified using character-encoded hex digits. (See explanation in 1.a.) The RAM address can range from $0000 to $FFFF. The divisor can range from #$01 to #$FF. Example(s): "b. Insert name of actor on field map" "g. Start new line on current page, if possible" ------------------------------- iii. _ram_byte_bitwise_and ------------------------------- Syntax: 2xxxxyy Syntax Breakdown: 2 _ram_byte_bitwise_and symbol xxxx encoded 2-byte RAM address (Big-endian) yy encoded 1-byte operand Description: Returns the bitwise AND of the byte at the given RAM address with the given operand. Example(s): "f. Set flag if TERRA is in the active party" ------------------------------- iv. _get_party_size ------------------------------- Syntax: 3 Syntax Breakdown: 3 _get_party_size symbol Description: Returns the size of the active party. Example(s): "c. Specialize text based on party size" "e. Specialize text for second party member" ________________________________________________________________________________ 2. RELEVANT OFFSETS & DISASSEMBLY ________________________________________________________________________________ Contents: a. Code Modifications b. New Subroutines ================================================== a. Code Modifications ================================================== Updating dialog script base address Original: C0/80D9: 85 CF STA $CF C0/80DB: 60 RTS Modified: C0/80D9: 4C CB 80 JMP $80CB ------------------------------- Incrementing dialog script base address Original: C0/80C6: E6 C9 INC $C9 C0/80C8: D0 A8 BNE $8072 C0/80CA: E6 CA INC $CA C0/80CC: D0 A4 BNE $8072 C0/80CE: E6 CB INC $CB C0/80D0: 80 A0 BRA $8072 Modified: C0/80C6: 20 07 DA JSR $DA07 ; INC_BASE_ADDRESS() C0/80C9: 80 A7 BRA $8072 ; op originally at C0/80D0: C0/80CB: 85 CF STA $CF C0/80CD: 64 40 STZ $40 ; Unused RAM byte. Reset last-char cache C0/80CF: 60 RTS C0/80D0: EA C0/80D1: EA ------------------------------- Incrementing dialog script base address (+1 or +2) Original: C0/829D: A9 01 LDA #$01 ; from C0/8281 C0/829F: 80 02 BRA $82A3 C0/82A1: A9 02 LDA #$02 C0/82A3: 18 CLC ;(from C0/829F C0/82A4: 65 C9 ADC $C9 C0/82A6: 85 C9 STA $C9 C0/82A8: A5 CA LDA $CA C0/82AA: 69 00 ADC #$00 C0/82AC: 85 CA STA $CA C0/82AE: A5 CB LDA $CB C0/82B0: 69 00 ADC #$00 C0/82B2: 85 CB STA $CB C0/82B4: 60 RTS Modified: C0/829D: 4C 07 DA JMP $DA07 ; entry point. INC_BASE_ADDRESS() C0/82A0: EA NOP C0/82A1: 20 07 DA JSR $DA07 ; entry point. INC_BASE_ADDRESS() C0/82A4: 4C 07 DA JMP $DA07 ; INC_BASE_ADDRESS() A_READ_CHAR: C0/82A7: A4 41 LDY $41 ; 2 unused RAM bytes C0/82A9: A5 40 LDA $40 ; Unused RAM byte C0/82AB: D0 07 BNE $82B4 C0/82AD: 20 97 D9 JSR $D997 ; Y_FIX_A_READ_CHAR() C0/82B0: 84 41 STY $41 ; 2 unused RAM bytes C0/82B2: 85 40 STA $40 ; Unused RAM byte C0/82B4: 60 RTS ------------------------------- Original: C0/8072: A4 00 LDY $00 C0/8074: B7 C9 LDA [$C9],Y Modified: C0/8072: EA NOP C0/8073: 20 A7 82 JSR $82A7 ; A_READ_CHAR() ------------------------------- Original: C0/80B0: A4 00 LDY $00 C0/80B2: B7 C9 LDA [$C9],Y Modified: C0/80B0: EA NOP C0/80B1: 20 A7 82 JSR $82A7 ; A_READ_CHAR() ------------------------------- Original: C0/8284: A4 00 LDY $00 ; from C0/8261 C0/8286: B7 C9 LDA [$C9],Y C0/8288: 85 BD STA $BD C0/828A: C8 INY C0/828B: B7 C9 LDA [$C9],Y Modified: C0/8284: 20 A7 82 JSR $82A7 ; A_READ_CHAR() C0/8287: 85 BD STA $BD C0/8289: 20 99 D9 JSR $D999 ; Y_INC_A_READ_CHAR() C0/828C: EA NOP ================================================== b. New Subroutines ================================================== Y_SEEK_TO_ENDARGS: ; Load _ENDARGS char into A, then drop into next subroutine. C0/D8E0: A9 7E LDA #$7E ; _ENDARGS Y_SEEK_TO_CHAR_A: ; Increase Y until current char-to-read equals A, ; or following character equals , ; skipping over any segments bookended by a _SWITCH - _ENDSWITCH pair. C0/D8E2: C8 INY C0/D8E3: D7 C9 CMP [$C9],y ; C0/D8E5: F0 14 BEQ $D8FB C0/D8E7: 48 PHA C0/D8E8: A9 7B LDA #$7B ; _SWITCH C0/D8EA: D7 C9 CMP [$C9],y C0/D8EC: D0 04 BNE $D8F2 C0/D8EE: 1A INC A ; _ENDSWITCH C0/D8EF: 20 E2 D8 JSR $D8E2 ; Y_SEEK_TO_CHAR_A(_ENDSWITCH) C0/D8F2: B7 C9 LDA [$C9],y C0/D8F4: F0 03 BEQ $D8F9 ; branch if char C0/D8F6: 68 PLA C0/D8F7: 80 E9 BRA $D8E2 C0/D8F9: 68 PLA C0/D8FA: 88 DEY C0/D8FB: 60 RTS Y_INC_X_PARSE_TYPE: ; Parse next encoded digit as enumerated subroutine I.D. (0, 1, 2, 3); ; Call corresponding subroutine. (Affects X.) C0/D8FC: C8 INY C0/D8FD: B7 C9 LDA [$C9],y C0/D8FF: 38 SEC C0/D900: E9 54 SBC #$54 ; _get_party_member ("0") C0/D902: 0A ASL A C0/D903: AA TAX C0/D904: FC 0A D9 JSR ($D90A,x) C0/D907: AA TAX C0/D908: 80 D8 BRA $D8E0 ; Y_SEEK_TO_CHAR_ENDARGS C0/D90A: 12 D9 ; $D912, A_GET_PARTY_MEMBER C0/D90C: 58 D9 ; $D958, A_RAM_BYTE_DIVIDE C0/D90E: 70 D9 ; $D970, A_RAM_BYTE_BITWISE_AND C0/D910: 78 D9 ; $D978, A_GET_PARTY_SIZE A_GET_PARTY_MEMBER: ; Compute Actor I.D. of the active party leader, store in A. ; (Drops into following subroutine, affects X.) C0/D912: C8 INY C0/D913: B7 C9 LDA [$C9],y C0/D915: 38 SEC C0/D916: E9 54 SBC #$54 ; '0' char C0/D918: 0A ASL A C0/D919: 5A PHY C0/D91A: A8 TAY ; 2 bytes C0/D91B: BE 03 08 LDX [$0803],y C0/D91E: 7A PLY C0/D91F: A9 29 LDA #$29 DIVIDE_X_BY_A: ; Divide X by A; store result in A. C0/D921: 8E 04 42 STX $4204 C0/D924: 8D 06 42 STA $4206 C0/D927: EA NOP C0/D928: EA NOP C0/D929: EA NOP C0/D92A: EA NOP C0/D92B: EA NOP C0/D92C: EA NOP C0/D92D: EA NOP C0/D92E: AD 14 42 LDA $4214 C0/D931: 60 RTS Y_INC_A_X_PARSE_ARGS: ; Increment Y, then drop into following subroutine C0/D932: C8 INY ; first encoded hex address digit C0/D933: B7 C9 LDA [$C9],y A_X_PARSE_ARGS: ; Read next 4 chars as encoded 16-bit value, store in X; ; Read following 2 chars as encoded 8-bit value, store in A. C0/D935: 38 SEC C0/D936: E9 54 SBC #$54 ; ('0' char) C0/D938: A2 05 00 LDX #$0005 C0/D93B: 0A ASL A C0/D93C: 0A ASL A C0/D93D: 0A ASL A C0/D93E: 0A ASL A C0/D93F: C8 INY ; next encoded hex address digit C0/D940: E2 20 SEP #$20 ; (8 bit accum./memory) C0/D942: 18 CLC C0/D943: 77 C9 ADC [$C9],y ; may overflow, but it doesn't matter C0/D945: 38 SEC C0/D946: E9 54 SBC #$54 ; ('0' char) C0/D948: C2 20 REP #$20 ; 16 bit accum./memory C0/D94A: E0 03 00 CPX #$0003 C0/D94D: D0 02 BNE $D951 C0/D94F: 48 PHA C0/D950: 7B TDC C0/D951: CA DEX C0/D952: D0 E7 BNE $D93B C0/D954: FA PLX C0/D955: E2 20 SEP #$20 ; (8 bit accum./memory) C0/D957: 60 RTS A_RAM_BYTE_DIVIDE: ; Read next 4+2 chars as arguments X and A; ; Read byte value at RAM address X, store in X; ; If X equals #$FF, retry with the following 4+2 chars until _ENDARGS char; ; Else, divide X by A, store in A. C0/D958: C8 INY ; first char of encoded RAM address C0/D959: B7 C9 LDA [$C9],y C0/D95B: C9 64 CMP #$64 ; #$63==(char '0' + #$0F) C0/D95D: B0 9B BCS $D8FA ; conventionally, if _ENDARGS C0/D95F: 20 35 D9 JSR $D935 ; A_X_PARSE_ARGS C0/D962: 48 PHA C0/D963: BF 00 00 7E LDA $7E0000,X C0/D967: AA TAX C0/D968: 68 PLA C0/D969: E0 FF 00 CPX #$00FF ; #$FF == no one's home C0/D96C: F0 EA BEQ D958 ; A_RAM_BYTE_DIVIDE C0/D96E: 80 B1 BRA $D921 ; DIVIDE_X_BY_A A_RAM_BYTE_BITWISE_AND: ; Read next 4+2 chars as arguments X and A ; Bitwise AND A with byte value at RAM address X C0/D970: 20 32 D9 JSR $D932 ; Y_INC_A_X_PARSE_ARGS C0/D973: 3F 00 00 7E AND $7E0000,X C0/D977: 60 RTS A_GET_PARTY_SIZE: ; Compute the size of the active party, store in A (affects X) C0/D978: 5A PHY C0/D979: A4 00 LDY $00 ; Y=0 C0/D97B: BB TYX C0/D97C: BD 50 18 LDA $1850,x C0/D97F: 29 40 AND #$40 C0/D981: F0 0B BEQ $D98E C0/D983: BD 50 18 LDA $1850,x C0/D986: 29 07 AND #$07 C0/D988: CD 6D 1A CMP $1A6D C0/D98B: D0 01 BNE $D98E C0/D98D: C8 INY C0/D98E: E8 INX C0/D98F: E0 10 00 CPX #$0010 C0/D992: D0 E8 BNE $D97C C0/D994: 98 TYA C0/D995: 7A PLY C0/D996: 60 RTS Y_FIX_A_READ_CHAR: ; Based on current dialog script base address $C9, Y==0, ; Adjust Y to produce a valid char-to-read; ; Process any data manipulation commands or switch blocks found while seeking; ; Store the resulting char-to-read in A. ; (Drops into following subroutine Y_INC_Y_FIX_A_READ_CHAR.) C0/D997: A4 02 LDY $02 ; Y=#$FFFF Y_INC_Y_FIX_A_READ_CHAR: ; Based on current valid char-to-read, ; Increment and adjust Y to produce the next valid char-to-read; ; Process any data manipulation commands or switch blocks found while seeking; ; Store the resulting char-to-read in A C0/D999: 08 PHP C0/D99A: DA PHX C0/D99B: C8 INY C0/D99C: B7 C9 LDA [$C9],y C0/D99E: C9 7B CMP #$7B ; _SWITCH C0/D9A0: D0 26 BNE $D9C8 C0/D9A2: DA PHX C0/D9A3: 20 FC D8 JSR $D8FC ; Y_INC_X_PARSE_TYPE() C0/D9A6: C8 INY C0/D9A7: B7 C9 LDA [$C9],y ; branch index for switch block (prelim.) C0/D9A9: C9 7E CMP #$7E ; _ENDARGS C0/D9AB: F0 04 BEQ $D9B1 C0/D9BD: CA DEX C0/D9BE: 10 F6 BPL $D9A6 C0/D9C0: C8 INY ; if branch index found, seek Y one too far C0/D9B1: 88 DEY ; adjust Y to last valid branch index C0/D9B2: B7 C9 LDA [$C9],y ; branch index for switch block C0/D9B4: 38 SEC C0/D9B5: E9 54 SBC #$54 ; '0' char C0/D9B7: AA TAX C0/D9B8: 20 E0 D8 JSR $D8E0 ; Y_SEEK_TO_ENDARGS() C0/D9BB: A9 7D LDA #$7D ; _BREAK C0/D9BD: CA DEX C0/D9BE: 30 05 BMI $D9C5 ; Seek to indexed branch C0/D9C0: 20 E2 D8 JSR $D8E2 ; Y_SEEK_TO_CHAR_A(_BREAK) C0/D9C3: 80 F8 BRA $D9BD C0/D9C5: FA PLX C0/D9C6: 80 D3 BRA $D99B ; continue C0/D9C8: C9 7C CMP #$7C ; _ENDSWITCH C0/D9CA: F0 CF BEQ $D99B ; continue C0/D9CC: C9 7D CMP #$7D ; _BREAK C0/D9CE: D0 07 BNE $D9D7 C0/D9D0: A9 7C LDA #$7C ; _ENDSWITCH C0/D9D2: 20 E2 D8 JSR $D8E2 ; Y_SEEK_TO_CHAR_A(_ENDSWITCH) C0/D9D5: 80 C4 BRA $D99B ; continue C0/D9D7: C9 7A CMP #$7A ; _AND C0/D9D9: D0 06 BNE $D9E1 C0/D9DB: DA PHX C0/D9DC: 20 70 D9 JSR $D970 ; A_RAM_BYTE_BITWISE_AND() C0/D9DF: 80 0C BRA $D9ED C0/D9E1: C9 79 CMP #$79 ; _OR C0/D9E3: D0 12 BNE $D9F7 C0/D9E5: DA PHX C0/D9E6: 20 32 D9 JSR $D932 ; Y_INC_A_X_PARSE_ARGS() C0/D9E7: 1F 00 00 7E ORA $7E0000,X C0/D9ED: 9F 00 00 7E STA $7E0000,X C0/D9F1: FA PLX C0/D9F2: 20 E0 D8 JSR $D8E0 ; Y_SEEK_TO_ENDARGS() C0/D9F5: 80 A4 BRA $D99B ; continue C0/D9F7: C9 76 CMP #$76 ; _INSERT C0/D9F9: D0 07 BNE $DA02 C0/D9FB: 20 FC D8 JSR $D8FC ; Y_INC_X_PARSE_TYPE() C0/D9FE: 8A TXA C0/D9FF: 18 CLC C0/DA00: 69 02 ADC #$02 ; Convert actor I.D. to actor char C0/DA02: FA PLX C0/DA03: 28 PLP C0/DA04: 48 PHA ; set flags based on A C0/DA05: 68 PLA C0/DA06: 60 RTS INC_BASE_ADDRESS: ; Compute and store the next valid dialog script base address in $C9; ; Process any data manipulation commands or switch blocks found while seeking; ; Preserve A and Y; Reset last-char cache. C0/DA07: 48 PHA C0/DA08: 5A PHY C0/DA09: 20 A7 82 JSR $82A7 ; A_READ_CHAR() C0/DA0C: C2 20 REP #$20 ; 16 bit accum./memory C0/DA0E: 98 TYA C0/DA0F: 38 SEC ; Force additional +1 C0/DA10: 65 C9 ADC $C9 C0/DA12: 85 C9 STA $C9 C0/DA14: 7B TDC ; Reset B C0/DA15: E2 20 SEP #$20 ; (8 bit accum./memory) C0/DA17: 90 02 BCC $DA1B C0/DA19: E6 CB INC $CB C0/DA1B: 64 40 STZ $40 ; Unused RAM byte C0/DA1D: 7A PLY C0/DA1E: 68 PLA C0/DA1F: 60 RTS ________________________________________________________________________________ 3. CREDITS ________________________________________________________________________________ Thanks to Imzogelmo for his disassembly of bank C0. FF3usME coded by Lord J. http://www.angelfire.com/pq/jumparound/ FINAL FANTASY is a registered trademark of Square Enix Holdings Co., Ltd. FINAL FANTASY VI (C) 1994, 2006, 2014 SQUARE ENIX CO., LTD. ________________________________________________________________________________ 4. REVISION HISTORY ________________________________________________________________________________ 2021-06-01 : Version 1.0 released. ________________________________________________________________________________ 5. LEGAL ________________________________________________________________________________ Copyright (C) 2021 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.