Tower of Bab Il Docs Spell Code - GameHackingDotOrg/FF4Hacking GitHub Wiki


Updates:
9/3/2000 Header added, a few new spell/item effect subroutines added,
elemental damage subroutines added.

The most recent copy of this document can be obtained from the Tower of Bab-Il
(http://minitrue.weyland-yutani.net/tower/index.html).

The disassembled code reproduced here is only the code that I believe I fully
understand at the moment. Other disassembled code will be added later once I
can fully comment it.

The subroutine to look up a spell effect subroutine address in a table and
jump to it is disassembled here:

03:D297   ASL  A            ;A register holds the subroutine #
03:D298   TAX
03:D299   LDA  $03E07C,X
03:D29D   STA  D,$80
03:D29F   LDA  $03E07D,X
03:D2A3   STA  D,$81
03:D2A5   LDA  #$03
03:D2A7   STA  D,$82
03:D2A9   JML  ($0080)

So this tells us that the table of spell effect subroutines is located at
1E27C (in the ROM with header).

The table is reproduced here with commentary:

E# CPUa Used by (not a complete list)
-- ---- -----------------------------
00 D378
01 D3B2
02 D40C Cure1, Cure2, Cure3, Cure4, Asura
03 D443 Storm, Weak
04 D466 Drain
05 D488 Psych
06 D505
07 D5A6 Hold, Sleep, Pin, Tongue, Entangle, Gas
08 D613 Blink, Image
09 D61C Wall
0A D64B Life1, Life2
0B D6E8 Heal
0C D81A Piggy, Size, Toad
0D D83F Armor
0E D863 Shell
0F D887 Slow, Fast, DullSong
10 D8A0
11 D8DE
12 D917 Peep
13 D972
14 D990
15 D9EC Ether1, Ether2
16 DA0C Elixir
17 DA1E
18 DA66
19 DA7F
1A DAA1
1B DB52
1C DBA8
1D DBA8
1E DBD8
1F DC1E
20 DC83
21 DCA3
22 DCBE
23 DCE3
24 DCEE
25 DD05
26 DD1A
27 DD39
28 DD7D
29 DD83
2A DD99
2B DDB1
2C DD4D
2D DDC4
2E DDC1
2F DDC5
30 DDC9
31 DE1C
32 DFBA
33 DFC0
34 DFD3
35 DFE8

At the time this subroutine is called, the target's data has been copied to
2700-277F in RAM. The caster's data has been copied to 2680-26FF. The
subroutines modify this data. The relevant spell/item table entry has been
copied to 289C-28A1. 28A2-28A4 contain the elemental/status table entry for
the spell/item. Locations CD and CE in the zero page designate the caster and
target; if the MSB is set, it is the number of a monster slot, otherwise it is
a character slot. After the subroutine returns, A2-A3 is damage done to the
caster (MSB/bit 0 set for HP recovery, bit 1 set for MP damage which should
only be displayed by following code as the actual damage is handled by the
effect subroutine) and A4-A5 is damage done to the target (ditto).

Here is the spell effect subroutine 00 (used by Fire/Ice/Lit 1-3 and too many
other spells to list):

03:D378   JSR  $E0E8    ;adjust elemental damage for elemental defenses
03:D37B   LDA  $38FE
03:D37E   BPL  $D388    ;if the elemental damage is positive, go on to check
                        ;for weaknesses
03:D380   AND  #$7F
03:D382   STA  $38FE    ;otherwise, make it positive
03:D385   JMP  $D416    ;and treat it as a cure spell (but skipping the undead
                        ;check)
03:D388   JSR  $E11B    ;adjust elemental damage for elemental weaknesses
03:D38B   LDA  $2704
03:D38E   AND  #$40     ;check to see if the target is floating
03:D390   BEQ  $D3AE    ;if not, skip the checks for earthquake attacks
03:D392   LDA  $352A
03:D395   BNE  $D3A7    ;if an item is being used, go and check to see if it
                        ;is the Earth Drum
03:D397   LDA  $26D2
03:D39A   CMP  #$28     ;check to see if the spell is the black magic Quake
03:D39C   BEQ  $D3B1    ;if it is, return
03:D39E   CMP  #$55     ;check to see if the spell is the call magic Titan
03:D3A0   BEQ  $D3B1    ;if it is, return
03:D3A2   CMP  #$A1     ;check to see if the spell is the enemy spell Quake
03:D3A4   BNE  $D3AE    ;if it isn't, go ahead and determine damage
03:D3A6   RTS           ;otherwise, return
03:D3A7   LDA  $26D2
03:D3AA   CMP  #$C7     ;check to see if the item is the Earth Drum
03:D3AC   BEQ  $D3B1    ;if it is, return
03:D3AE   JSR  $C99F    ;call damage determination subroutine
03:D3B1   RTS

Here is the spell effect subroutine 02 (used by all Cure spells and potions):

03:D40C   LDA  $2740
03:D40F   AND  #$80     ;check to see if target is undead
03:D411   BEQ  $D416
03:D413   JMP  $D3AE    ;if so, do a straightforward damage determination
03:D416   LDA  $352A
03:D419   BNE  $D439    ;if an item is being used, do a normal cure
03:D41B   LDA  $26D2
03:D41E   CMP  #$11     ;check to see if the spell is the white magic Cure4
03:D420   BNE  $D439    ;if it isn't, do a normal cure
03:D422   LDA  $3906
03:D425   CMP  #$01
03:D427   BNE  $D439    ;if the number of targets isn't one, do a normal cure
                        ;otherwise, restore all lost HP...
03:D429   REP  #$20     ;16 bit A register
03:D42B   SEC
03:D42C   LDA  $2709
03:D42F   SBC  $2707
03:D432   STA  D,$A4    ;damage done to target = target's max HP - target's
                        ;current HP
03:D434   TDC
03:D435   SEP  #$20     ;8 bit A register
03:D437   BRA  $D43C    ;and skip normal damage determination
                        ;for normal cures, use normal damage determination
03:D439   JSR  $C99F    ;call damage determination subroutine
03:D43C   LDA  D,$A5
03:D43E   ORA  #$80
03:D440   STA  D,$A5    ;set cure bit in damage
03:D442   RTS

Here is the spell effect subroutine 04 (used by Drain):

03:D466   LDA  D,$CD
03:D468   CMP  D,$CE
03:D46A   BEQ  $D487    ;check to see if user is the target, if so do nothing
03:D46C   JSR  $C99F    ;determine damage
03:D46F   LDX  D,$A4
03:D471   STX  D,$A2    ;assign damage to caster equal to damage to target
03:D473   LDA  $2740
03:D476   AND  #$80     ;check to see if target is undead
03:D478   BEQ  $D481
03:D47A   LDA  D,$A5
03:D47C   ORA  #$80
03:D47E   STA  D,$A5    ;if undead, set highest bit in target damage (cure)
03:D480   RTS
03:D481   LDA  D,$A3
03:D483   ORA  #$80
03:D485   STA  D,$A3    ;if not undead, set highest bit in caster damage
                        ;(cure)
03:D487   RTS

Here is the spell effect subroutine 05 (used by Psych):

03:D488   LDA  D,$CD
03:D48A   CMP  D,$CE
03:D48C   BNE  $D48F    ;check to see that user is not the target target
03:D48E   RTS
03:D48F   JSR  $C99F    ;determine damage
03:D492   LDX  D,$A4
03:D494   STX  D,$A2    ;assign damage to caster equal to damage to target
03:D496   LDA  $2740
03:D499   AND  #$80     ;check to see if target is undead
03:D49B   BEQ  $D49F
03:D49D   BRA  $D4DC
03:D49F   REP  #$20     ;16 bit A register
03:D4A1   SEC
03:D4A2   LDA  $270B
03:D4A5   SBC  D,$A4    ;if target is not undead subtract damage from target's
                        ;MP
03:D4A7   BCS  $D4B5
03:D4A9   LDA  $270B
03:D4AC   STA  D,$A4
03:D4AE   STA  D,$A2    ;if the damage was greater than target's MP, lower
                        ;damage to target's MP
03:D4B0   STZ  $270B    ;target's MP = 0
03:D4B3   BRA  $D4B8
03:D4B5   STA  $270B    ;if it wasn't greater than target's MP, store the
                        ;result
03:D4B8   CLC
03:D4B9   LDA  $268B
03:D4BC   ADC  D,$A4
03:D4BE   STA  $268B    ;add damage to caster's MP
03:D4C1   LDA  $268D
03:D4C4   CMP  $268B
03:D4C7   BCS  $D4CC
03:D4C9   STA  $268B    ;if MP is greater than maximum, set it to maximum
03:D4CC   TDC           ;A register = 0
03:D4CD   SEP  #$20     ;8 bit A register
03:D4CF   LDA  D,$A5
03:D4D1   ORA  #$40
03:D4D3   STA  D,$A5    ;set MP damage bit in target's damage
03:D4D5   LDA  D,$A3
03:D4D7   ORA  #$C0
03:D4D9   STA  D,$A3    ;set cure and MP damage bit in caster's damage
03:D4DB   RTS
03:D4DC   REP  #$20     ;16 bit A register
03:D4DE   SEC
03:D4DF   LDA  $268B
03:D4E2   SBC  D,$A4    ;if target is undead subtract damage from caster's MP
03:D4E4   BCS  $D4F2
03:D4E6   LDA  $268B
03:D4E9   STA  D,$A4
03:D4EB   STA  D,$A2    ;if damage was greater than caster's MP lower damage
                        ;to caster's MP
03:D4ED   STZ  $268B    ;caster's MP = 0
03:D4F0   BRA  $D4F5
03:D4F2   STA  $268B    ;if it wasn't greater than caster's MP, store result
03:D4F5   TDC           ;A register = 0
03:D4F6   SEP  #$20     ;8 bit A register
03:D4F8   LDA  D,$A3
03:D4FA   ORA  #$40
03:D4FC   STA  D,$A3    ;set MP damage bit in caster's damage
03:D4FE   LDA  D,$A5
03:D500   ORA  #$C0
03:D502   STA  D,$A5    ;set cure and MP damage bit in target's damage
03:D504   RTS

Here is the spell effect subroutine 08 (used by Blink and Image):

03:D613   LDA  $2706
03:D616   ORA  #$0C
03:D618   STA  $2706    ;set the image bits in the target status to both 1
                        ;(three images)
03:D61B   RTS

Here is the spell effect subroutine 0F (used by Fast, Slow, and DullSong):

03:D887   CLC
03:D888   LDA  $273B
03:D88B   ADC  $289D    ;add byte 01 of spell data to target speed modifier
03:D88E   CMP  #$20
03:D890   BCS  $D89A    ;if speed modifier >= #20, set it to #20 and return
03:D892   CMP  #$0C
03:D894   BCS  $D89C    ;else if speed modifier < #0C set it and return
03:D896   LDA  #$0C
03:D898   BRA  $D89C    ;if it's less than #0C, set it to #0C and return
03:D89A   LDA  #$20
03:D89C   STA  $273B
03:D89F   RTS

Here is the spell effect subroutine 15 (used by Ether1 and Ether2):

03:D9EC   JSR  $C99F    ;call damage determination subroutine
03:D9EF   REP  #$20     ;16 bit A register
03:D9F1   CLC
03:D9F2   LDA  D,$A4
03:D9F4   ADC  $270B    ;add the "damage" to the target's mp
03:D9F7   CMP  $270D
03:D9FA   BCC  $D9FF
03:D9FC   LDA  $270D
03:D9FF   STA  $270B    ;if mp is greater than maximum, set it to maximum
03:DA02   TDC           ;A register = 0
03:DA03   SEP  #$20     ;8 bit A register
03:DA05   LDA  D,$A5
03:DA07   ORA  #$C0
03:DA09   STA  D,$A5    ;set cure, mp bits in damage value
03:DA0B   RTS

Here is the spell effect subroutine 16 (used by Elixir):

03:DA0C   REP  #$20     ;16 bit A register
03:DA0E   LDA  $2709
03:DA11   STA  $2707    ;target's hp = target's max hp
03:DA14   LDA  $270D
03:DA17   STA  $270B    ;target's mp = target's max mp
03:DA1A   TDC           ;A register = 0
03:DA1B   SEP  #$20     ;8 bit A register
03:DA1D   RTS

This subroutine adjusts the elemental damage modifier for elemental defenses:

03:E0E8   LDA  $2726    ;get target elemental immunities
03:E0EB   AND  $28A2    ;check to see if target is immune to any elements used
                        ;in the attack
03:E0EE   BEQ  $E101
03:E0F0   TDC           ;A register = 0
03:E0F1   STA  $38FE    ;if target was immune to any attack elements, set
                        ;elemental damage multiplier to 0
03:E0F4   LDA  $2726
03:E0F7   AND  #$40     ;test to see if target is both immune and absorbs
03:E0F9   BEQ  $E11A    ;if not, return
03:E0FB   LDA  #$84
03:E0FD   STA  $38FE
03:E100   RTS           ;otherwise, elemental damage is negative (healing)
                        ;and twice normal!
03:E101   LDA  $2725
03:E104   AND  $28A2    ;check to see if target resists/absorbs any elements
                        ;used in the attack
03:E107   BEQ  $E11A    ;if not return with elemental damage multiplier intact
03:E109   LDA  #$01
03:E10B   STA  $38FE    ;set elemental damage multiplier to 1 (half damage)
03:E10E   LDA  $2725
03:E111   AND  #$40     ;check to see if blocked elements are absorbed
03:E113   BEQ  $E11A    ;if not, return (with half elemental damage)
03:E115   LDA  #$82
03:E117   STA  $38FE    ;otherwise, elemental damage is negative (healing)
03:E11A   RTS

This subroutine adjusts the elemental damage modifier for weaknesses:

03:E11B   LDA  $38FE    ;check the elemental damage multiplier
03:E11E   CMP  #$02
03:E120   BNE  $E13D    ;if it isn't normal damage (there was an elemental
                        ;defense involved), return
03:E122   LDA  $2721
03:E125   AND  $28A2    ;check to see if target is very weak vs. any of the
                        ;attack elements
03:E128   BEQ  $E130    ;if not, go check for normal weakness
03:E12A   LDA  #$08
03:E12C   STA  $38FE    ;if so, set elemental damage to four times normal
03:E12F   RTS
03:E130   LDA  $2720
03:E133   AND  $28A2    ;check to see if target is weak vs. any of the attack
                        ;elements
03:E136   BEQ  $E13D    ;if not, return
03:E138   LDA  #$04
03:E13A   STA  $38FE    ;if so, set elemental damage to twice normal
03:E13D   RTS
⚠️ **GitHub.com Fallback** ⚠️