data/claims/ff4-av.yamlFinal Fantasy IV battle graphics and sound (everything8215's disassembly)
Game: Final Fantasy IV.
On a town or dungeon map, a battle starts with sound effect $3F and 42 frames of mosaic before the field hands over to the battle program. The routine first leaves only BG1 and BG2 on the main screen, hiding the sprites, and then writes a 21-byte table to the mosaic register, each byte for 2 frames: the block size, on BG1 and BG2, rises and falls twice through 1, 3, 5, 7, 5 and 3 pixels square, then rises through 1, 3, 5 and 7 and on through 9, 11, 13 and 15 to 16. The same routine starts a battle that an event command or a scripted scene calls. Before it hands over, it reads the battle background from the map again, except for a battle a treasure chest starts. ff4-av/field-battle-mosaic
About: Final Fantasy IV Battle transition
Value: frames 42 · sfx 63 · frames per byte 2 · blocks 1, 3, 5, 7, 5, 3, 1, 3, 5, 7, 5, 3, 1, 3, 5, 7, 9, 11, 13, 15, 16
CodeFinal Fantasy IV Disassembly ·field/field.asm#L929-L956; [ battle (sub-map) ] BattleSub: @8918: lda #$3f jsr PlaySfx lda #$03 sta hTM ; bg1 and bg2 only (hide sprites) stz $79 @8924: jsr WaitVblankShort lda $79 lsr tax lda BattleMosaicTbl,x sta hMOSAIC inc $79 lda $79 cmp #$2a bne @8924 lda $c6 bne @8940 jsr UpdateBattleParams @8940: jsr ExecBattle rts ; screen pixelation data for battle blur -> $2106 BattleMosaicTbl: @8944: .byte $03,$23,$43,$63,$43,$23,$03,$23,$43,$63,$43,$23,$03,$23,$43,$63 .byte $83,$a3,$c3,$e3,$f3Final Fantasy IV Disassembly · field/field.asm#L302-L312jsr CheckBattle .endif lda $85 beq @814f ; branch if no battle jsr BattleSub jmp AfterBattle @814f: jsr CheckTreasureNPC lda $85 beq @815c ; branch if no battle jsr BattleSubFinal Fantasy IV Disassembly · field/field.asm#L1014-L1017 — after the battle a nonzero byte at $C6 becomes the item the battle gives, which the treasure menu then shows@89b0: lda $c6 beq @89bc sta $1804 stz $c6Final Fantasy IV Disassembly · field/event.asm#L588-L608; [ event command $ec: battle ] EventCmd_ec: … @e645: jsr BattleSubFinal Fantasy IV Disassembly · field/special.asm#L2390-L2399; [ special effect $04: rydia/titan battle and aftermath ] Special_04: … jsr BattleSubFinal Fantasy IV Disassembly · field/field.asm#L1817-L1824WaitVblankShort: @913c: lda #1 sta $7e @9140: lda $7e bne @9140A battle's background is picked on the field before the battle starts. On the world map, bits 0 to 2 of the tile under the party index a six-entry table of backgrounds ($00, $0A, $01, $09, $0F, $05); indexes 6 and 7 would read past the table, and this claim doesn't follow whether any tile gives them. On a town or dungeon map it is the map's own battle background, with bit 6 set for a cave with water when the tile's water bit is set, bit 5 set when the map asks for the alternate background palette, and the magnetized-cave flag cleared unless event switch $E1 is set. Battles $1B7 and $1B8 always get background $10, the final battle's. Code — what reading the background again doesFinal Fantasy IV Disassembly · field/trigger.asm#L263-L297 — a treasure chest's trigger that starts a battle sets the value the field tests before reading the background againjsr GetTreasureTiles lda #$30 jsr PlaySfx … lda $08 sta $c6 … sta $1800 ; set battle id … jsr GetBattleBGChecked by claude-agent, 2026-11-08 — read the sub-map battle routine and its 21-byte table, the sub-map main loop's two calls (a random battle, then a treasure-chest check), the event command and the scene that call the same routine, the after-battle use of $C6, and the vertical-blank wait the loop runs once a frame; block sizes computed as each byte's high nibble plus 1Revised after a check by a second AI model, 2026-11-08.The mosaic register's high nibble is the size n, drawing blocks n + 1 pixels square, and its low nibble picks the layers, here $3 for BG1 and BG2. The loop counter runs 0 to 41 and the table index is the counter halved, so each of the 21 bytes stays 2 frames. The loop never writes the brightness.
On the world map, a battle starts with the same sound effect, $3F, and 40 frames of Mode 7 zoom. The routine shows only BG1 on the main screen, hiding the sprites, and each frame turns HDMA off, takes the next of 40 zoom bytes and rebuilds the Mode 7 matrix from it at the map's current rotation. The world map's loader sets the zoom byte to $10; the battle's table rises from $10 to $16 and back twice, then falls to 0 over the last 16 frames. Each matrix entry is a sine value times the zoom byte over 256, so at $16 the entries are 1.375 times their size at $10 and at 0 they are 0 (computed): the map pulls back twice, then grows until one point of it fills the screen. The brightness stays as it is. ff4-av/world-battle-zoom
About: Final Fantasy IV Mode 7
Value
{
"frames": 40,
"sfx": 63,
"zoom": [
16,
17,
18,
19,
20,
21,
22,
21,
20,
19,
18,
17,
16,
17,
18,
19,
20,
21,
22,
21,
20,
19,
18,
17,
16,
15,
14,
13,
12,
11,
10,
8,
7,
6,
5,
4,
3,
2,
1,
0
],
"baseZoom": 16
}field/field.asm#L904-L925; [ battle (world map) ] BattleWorld: @88eb: lda #$3f jsr PlaySfx lda #$01 sta $212c ; main screen only (hide sprites) stz $79 @88f7: jsr WaitVblankShort stz hHDMAEN lda $79 tax lda f:BattleZoomTbl,x ; world map battle effect zoom values sta $ad jsr CalcMode7Rot jsr UpdateMode7Regs inc $79 lda $79 cmp #$28 bne @88f7 jsr ExecBattle rtsFinal Fantasy IV Disassembly · field/field_data.asm#L387-L391; world map battle effect zoom values BattleZoomTbl: @fb5e: .byte $10,$11,$12,$13,$14,$15,$16,$15,$14,$13,$12,$11,$10,$11,$12,$13 .byte $14,$15,$16,$15,$14,$13,$12,$11,$10,$0F,$0E,$0D,$0C,$0B,$0A,$08 .byte $07,$06,$05,$04,$03,$02,$01,$00Final Fantasy IV Disassembly · field/field.asm#L1734-L1798CalcMode7Rot: @90ba: lda #$40 ; zoom * cos(theta) jsr CalcSine sty $6e lda #$00 ; zoom * sin(theta) jsr CalcSine sty $70 lda #$80 ; -zoom * sin(theta) jsr CalcSine sty $72 rts … CalcSine: @90d0: clc adc $06fb ; rotation angle sta $06 and #$7f asl tax lda f:Mode7SineTbl,x ; sine table sta hM7A lda f:Mode7SineTbl+1,x sta hM7A lda $ad ; zoom level sta hM7B sta hM7B ldy hMPYM … UpdateMode7Regs: @9104: lda $6e sta hM7A … lda $6e sta hM7D lda $6f sta hM7D rtsFinal Fantasy IV Disassembly · field/field.asm#L582-L585lda #$10 sta $ad ; mode 7 zoom level ldx #0 stx $06fb ; mode 7 rotation angleFinal Fantasy IV Disassembly · field/field.asm#L259-L264jsr CheckBattle .endif lda $85 beq @80ef jsr BattleWorld jmp AfterBattleChecked by claude-agent, 2026-11-08 — read the world-map battle routine, its 40-byte table, the matrix routines it calls (the hardware multiply of a 16-bit sine by the 8-bit zoom, keeping the product's middle and high bytes), the loader's zoom and angle, and the world map loop's call; the 1.375 ratio is $16 / $10, computedChecked by a second AI model, 2026-11-08.The multiply unit's product read from its middle byte up is the product over 256. A Mode 7 matrix entry is the step through the map for one screen pixel, so a larger matrix shows more of the map in the same screen and a matrix near 0 magnifies one texel across it; that is the hardware's rule, not this routine's. The loop never writes the brightness register, so the screen goes from the zoom to the battle at whatever brightness the map had. $212C is the main screen designation; $01 is BG1.
A battle's background is picked on the field before the battle starts. On the world map, bits 0 to 2 of the tile under the party index a six-entry table of backgrounds ($00, $0A, $01, $09, $0F, $05); indexes 6 and 7 would read past the table, and this claim doesn't follow whether any tile gives them. On a town or dungeon map it is the map's own battle background, with bit 6 set for a cave with water when the tile's water bit is set, bit 5 set when the map asks for the alternate background palette, and the magnetized-cave flag cleared unless event switch $E1 is set. Battles $1B7 and $1B8 always get background $10, the final battle's. ff4-av/battle-background
Value: world table 0, 10, 1, 9, 15, 5 · final battle bg 16 · final battles 439, 440
CodeFinal Fantasy IV Disassembly ·field/rand_battle.asm#L193-L197lda $a2 and #$07 tay lda WorldBattleBGTbl,y ; world map battle backgrounds sta $1802Final Fantasy IV Disassembly · field/rand_battle.asm#L245-L247; world map battle backgrounds WorldBattleBGTbl: @8caf: .byte $00,$0a,$01,$09,$0f,$05Final Fantasy IV Disassembly · field/rand_battle.asm#L253-L286UpdateBattleParams: … jsr GetBattleBG lda #$e1 jsr CheckEventSwitch cmp #$00 bne @8cd3 lda $1802 ; disable magnetization and #$7f sta $1802 … GetBattleBG: @8cd4: lda $a2 ; use cave w/ water battle bg and #$20 asl sta $06 lda $0fdb ; use alternate battle bg palette and #$40 lsr ora $06 sta $06 lda $0fdb ; battle bg id and magnetization flag and #$8f ora $06 sta $1802 rtsFinal Fantasy IV Disassembly · field/field.asm#L965-L972ExecBattle: @8959: ldx $1800 cpx #$01b7 bcc @896b cpx #$01b9 bcs @896b lda #$10 ; use final battle bg sta $1802Final Fantasy IV Disassembly · notes/ff4j-sfc-ram-map.txt#L355-L361$01 ebcagbbb (world map) … c: through-tile / use cave w/ water bg … b: battle bg (world map, see 00/8A91)Final Fantasy IV Disassembly · notes/ff4j-sfc-ram-map.txt#L919-L922| $1802 | mwnbbbbb |
| | m: enable magnetization (cave magnes) |
| | w: use battle bg 7 (cave w/ water) |
| | n: use alternate battle bg palette |Checked by claude-agent, 2026-11-08 — read the world map's random-battle path and its table, the town and dungeon maps' background routine, the event-switch test, the final battle's override in the battle start, and the RAM map's layout of the tile byte and the background byteRevised after a check by a second AI model, 2026-11-08.The table has six entries, but the index has three bits; tiles whose bits give 6 or 7 would read the two bytes after the table, which are code, and this claim doesn't follow which tiles have them. On a town or dungeon map the background number is the low four bits of the map's property byte. The background byte's top bit is the magnetized-cave flag the RAM map names (the Cave of Magnes), which the battle program reads.
A battle's song comes from bits 2 and 3 of the seventh byte of its battle properties: values 0, 1 and 2 pick songs $27, $1A and $0B from a three-entry table (Battle 1, Battle 2 and The Dreadful Fight in the disassembly's battle editor), and 3 keeps the song that was playing. The battle program sends the song once the battle's data is loaded, before the battle graphics start, and repeats the request until the sound driver reports that song playing. ff4-av/battle-music
Value: songs 39, 26, 11 · keep 3
CodeFinal Fantasy IV Disassembly ·battle/battle.asm#L393-L403@8219: lda $38e5 ; battle song and #$0c lsr2 cmp #$03 beq @822d ; branch if no battle music tax lda f:BattleSongTbl,x jsl PlayBattleSong @822d: lda #$03 ; battle graphics $03: init battle graphics jsr ExecBtlGfxFinal Fantasy IV Disassembly · battle/battle_data.asm#L373-L375; battle songs BattleSongTbl: @ff0c: .byte $27,$1a,$0bFinal Fantasy IV Disassembly · battle/init.asm#L28-L29lda $29a2 sta $38e5Final Fantasy IV Disassembly · battle/battle.asm#L263-L269@80ec: lda $1800 sta $393d ; get pointer to battle properties lda #8 sta $393f jsr Mult16Final Fantasy IV Disassembly · battle/battle.asm#L1768-L1784PlayBattleSong: @ff12: sta $a9 … @ff1d: lda $a9 sta $1e01 lda #$01 sta $1e00 jsl ExecSound_ext lda $1e05 cmp $a9 bne @ff1d lda $1e04 cmp #$01 bne @ff1d rtlFinal Fantasy IV Disassembly · vanilla/ff4-en-rip.json#L1133-L1145"song": { "type": "property", "name": "Song", "begin": 6, "mask": "0x0C", "stringTable": { "string": { "0": "Battle 1", "1": "Battle 2", "2": "The Dreadful Fight", "3": "Continue Current Song"The song table has 70 entries, each a three-byte pointer to a song's script; this is the game's track listing, by song number, with where the game plays each, from every request found: the song byte of each of the 383 maps, 375 song commands in the event scripts, 49 song control codes in dialogue, 14 song commands in monster action scripts, the battle properties' song field, the world and vehicle song tables, and constants in the field, battle, menu, and cutscene code. 62 entries are requested; no request found reaches 56, 57, 58, 59, 60, 61, 64, 65. Entries 56, 57, 58, 59, 60, 61 point to the same script as entry 0. Code — the track listing counts the battles that request each songChecked by claude-agent, 2026-11-08 — read the song pick in the battle start, the table, where $38E5 is copied from (the eighth-byte load puts byte 6 at $29A2), the request loop, and the rip definition's names for the field's valuesRevised after a check by a second AI model, 2026-11-08.The battle start copies the battle's eight property bytes to $299C up, so $29A2 is byte 6 counted from 0, the seventh. The song names are the disassembly's rip definition, which labels the values in its battle editor; the code names none of them.
The battle screen opens from black over 16 frames, its brightness rising by one step a frame from 0 to full (15), while the battle background on BG2 comes out of a mosaic: blocks 16 pixels square at first, shrinking by one pixel every 2 frames to none after 32 frames. Meanwhile the monsters slide into place over 22 frames: their layer's scroll, set per line through HDMA, falls from 252 pixels to 0 along a sine curve (252, 240, 228, … 24, 12, 0; computed), horizontally for an ordinary battle and negated in a back attack, or vertically for flying monsters; the directions (from the left, from the right in a back attack, from above) and which flag means flying are the source's comments, not followed. Player input waits until the menu is open and both effects are done. ff4-av/battle-opening
About: Final Fantasy IV HDMA
Value: brightness frames 16 · mosaic frames 32 · entry frames 22 · entry offsets 252, 240, 228, 216, 204, 196, 184, 172, 160, 148, 136, 124, 112, 100, 88, 76, 64, 48, 36, 24, 12, 0
CodeFinal Fantasy IV Disassembly ·btlgfx/btlgfx.asm#L326-L394lda #$f2 sta $6cc2 … stz $f13d … lda #$16 ; monster entry takes 24 frames sta $f28e lda #$20 sta $f133 … ; start of frame loop @8205: jsr WaitVblank lda $f13d sta $6cc1 cmp #$0f beq @8215 inc $f13d @8215: lda $f133 beq @822d lda $f133 dec sta $f133 asl3 and #$f0 ora #$02 sta $6cc2 bra @8230 @822d: stz $6cc2 @8230: jsr ImmediateMenuUpdate lda $f28e beq @8251 lda $f28e dec sta $f28e pha tax lda f:AnimSineTbl,x ; sine table asl2 jsr UpdateMonsterEntry pla bne @8251 clr_a jsr SwapMonsterScreen @8251: lda $181e ; wait for menu to open ora $1820 ora $f28e ; wait for monster entry ora $f133 ; wait for screen mosaic bne @8205 inc $f44a ; enable player input rtsFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L577-L580lda $6cc1 sta f:hINIDISP ; set screen brightness lda $6cc2 sta f:hMOSAICFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L400-L429UpdateMonsterEntry: @8263: ldx #0 pha lda $ed4e and #$40 bne @8287 ; branch if monsters are flying lda $6cc0 beq @8279 ; back attack - enter from right pla eor #$ff inc bra @827a ; normal attack - enter from left @8279: pla @827a: sta $7612,x ; bg1 h-scroll inx4 cpx #$0260 bne @827a rts ; flying monsters - enter from above @8287: pla @8288: sta $7614,x ; bg1 v-scroll inx4 cpx #$0260 bne @8288 rtsFinal Fantasy IV Disassembly · btlgfx/magic.asm#L1622-L1631 — the battle background's shake writes the BG2 scroll table at $7992UpdateShakeBattleBG: @f4b2: pha lda $1802 ; battle bg id … @f4bc: pla ldx #0 @f4c0: sta $7992,xFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L4577-L4588lda #$0d ; bg1 scroll sta $4301 lda #$0f ; bg2 scroll sta $4311 lda #$11 ; bg3 scroll sta $4321Final Fantasy II (USA) (Rev 1) · 0xe7f00-0xe7f15 — the first 22 bytes of the animation sine table ($1C:FF00): 0, 3, 6, … 60, 63Checked by claude-agent, 2026-11-08 — read the battle graphics start's frame loop, the NMI's brightness and mosaic writes, the monster entry routine, the HDMA setup that points channels 0 to 2 at the three layers' scroll, and the battle background's shake that writes the BG2 table; read the sine bytes from the ROM; the offsets are the sine bytes times 4, computedRevised after a check by a second AI model, 2026-11-08.The mosaic byte's low nibble $2 is BG2, and its size is the frame counter halved, so 15, 15, 14, 14 … 0, 0 (computed); a size n draws blocks n + 1 pixels square. The source's comment says the monster entry takes 24 frames, but the counter starts at $16, 22. The entry offset is the sine byte times 4 in an 8-bit register, written as the low byte of each line's BG1 scroll. A back attack negates it (two's complement), so its offsets are 256 minus these. Which way the monsters move on screen comes from the source's comments; this claim doesn't trace the two halves of BG1's map the scroll's high byte picks between.
Every battle ends, won, lost or fled, with 15 frames (computed) in which the screen dims by one step a frame, from 15 to 1, while a mosaic on BG1, BG2 and on alternate frames BG4 grows from none to blocks 8 pixels square, one pixel every 2 frames (sizes 0 to 7, computed). Before it the battle program fades the music, a slow fade after a win or an escape and a plain one after a defeat, unless the battle kept the map's song, gave items, or was battle $1B7. ff4-av/battle-exit
About: Final Fantasy IV Screen fade
Value: frames 15 · slow fade 133 · fade 139 · max block 8
CodeFinal Fantasy IV Disassembly ·battle/battle.asm#L1112-L1115EndBattle: @869f: jsl ValidateDeadCharHP lda #$85 ; sound command $85: fade out music (slow) sta $35f3Final Fantasy IV Disassembly · battle/battle.asm#L1135-L1141; defeat @86ce: lda $38e5 and #$0c cmp #$0c beq @86dc lda #$8b ; sound command $8b: fade out music sta $35f3Final Fantasy IV Disassembly · battle/battle.asm#L1201-L1251@8766: clr_ax … ldx $1800 cpx #$01c0 bcc @8777 inc $ab @8777: ldx #7 @877a: lda $1804,x ; items gained from battle … lda $38e5 and #$0c cmp #$0c beq @87ab ; branch if no battle music lda $ab bne @87ab ; branch if any items were gained lda $1800 cmp #$b7 bne @87a1 lda $1801 bne @87ab ; branch if battle $01b7 (zeromus) @87a1: lda $35f3 ; sound command (either $85 or $8b) sta $1e00 jsl ExecSound_ext @87ab: lda #$ff sta $a9 lda #$10 ; screen brightness counter sta $aa @87b3: inc $a9 ; increment mosaic counter dec $aa ; decrement screen brightness lda $aa beq @87d1 lda $a9 jsr Asl_3 ora #$03 ; affect bg1 and bg2 sta $6cc2 lda $aa sta $6cc1 lda #$02 ; battle graphics $02: wait one frame jsr ExecBtlGfx bra @87b3 @87d1: lda #$00 sta f:hMOSAIC ; clear mosaic register rtsFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L577-L580lda $6cc1 sta f:hINIDISP ; set screen brightness lda $6cc2 sta f:hMOSAICChecked by claude-agent, 2026-11-08 — read the end of battle from the fade request through the defeat, escape and victory branches to the shared tail, and the NMI that writes the two bytes; the mosaic sizes and the alternate-frame BG4 bit are computed from the counter shifted left by 3Revised after a check by a second AI model, 2026-11-08.The counter shifted left 3 puts its bit 0 in the mosaic register's BG4 bit and its upper bits in the size, so the size is the counter halved, 0 to 7 (computed), and odd counts also set BG4. Battles $1C0 and up count as giving items too. Battle $1B7 is Zeromus in the source's comment.
A damage number is four 8-pixel digit sprites and a blank fifth, right-aligned: the value's low 14 bits are turned into five decimal digits, the ten-thousands digit is dropped, and up to three leading zeros are hidden, so the ones digit always shows. The digits start 16 pixels left of the target's position and 8 above it; in a back attack that x position becomes 223 minus it (its bits inverted, less 32; computed), which mirrors the number across the screen. Damage is drawn with the sprite attributes $31, sprite palette 0, which the source's comment calls white, and a value with bit 15 set, which the code's comment marks as not HP damage, with $3D, palette 6, green; a miss replaces the digits with the miss tiles (four in the English release). A target whose value is 0, flags included, shows nothing. One pass draws the numbers for all 13 combatants at once, 8 monsters and 5 characters, and the battle waits until it ends. ff4-av/damage-numerals
Value: digits 4 · sprites 5 · digit width 8 · value bits 14 · damage attr 49 · heal attr 61 · damage palette 0 · heal palette 6 · slots 13 · x offset -16 · y offset -8
CodeFinal Fantasy IV Disassembly ·btlgfx/btlgfx.asm#L2392-L2551ShowMsg_04: … @c9e9: clr_a sta $f0fa,x ; disable character/monster damage numerals lda #$31 ; use white text (palette 0) sta $f0ed,x inx cpx #13 … @c9fd: longa lda ($41),y ; damage value sta $00 and #$3fff tax shorta0 jsr HexToDec jsr NormalizeNum phy iny lda ($41),y and #$40 beq @ca2a ; branch if not miss .if LANG_EN lda #$6c sta $180c inc sta $180d inc sta $180e inc sta $180f .else … @ca2e: lda $180c,x ; number digit cmp #$ff beq @ca38 ; branch if no digit sec sbc #$10 @ca38: sta $dbe7,y ; copy 4 digits to buffer iny inx cpx #4 … lda ($41),y bne @ca5c ; branch if damage is nonzero tya lsr tax shorta0 inc $f0fa,x ; enable character/monster damage numerals … lda ($41),y and #$40 beq @ca6f ; branch if not miss … lda #$ff sta $f0fa,x @ca6f: lda ($41),y bpl @ca7c ; branch if hp damage … lda #$3d ; use green text (palette 6) sta $f0ed,x … stz $f108 ; clear frame counter … inc $f107 ; enable damage numeral animation @cad5: jsr WaitFrame lda $f107 bne @cad5 rtsFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L1135-L1197HexToDec: @86bf: stx $26 ldx #10000 stx $28 jsr Div16 lda $2a clc adc #$80 sta $180c … NormalizeNum: @8716: ldx #0 @8719: lda $180d,x ; shift out the top digit sta $180c,x inx cpx #5 bne @8719 ldx #0 @8728: lda $180c,x cmp #$80 bne @873a ; return if digit is not zero lda #$ff sta $180c,x ; hide digit inx cpx #3 ; don't hide ones digit bne @8728 @873a: rtsFinal Fantasy IV Disassembly · btlgfx/sprite.asm#L1480-L1587DrawDmgNumerals: … @e525: stz $14 lda $f0fa,y beq @e535 ; branch if no damage numerals cmp #$ff beq @e533 ; branch if miss jmp @e5c2 @e533: inc $14 @e535: lda $f0d3,y ; x position sec sbc #$10 sta $12 phx lda $f0e0,y ; y position sec sbc #$08 sta $0e … lda $6cc0 beq @e590 ; branch if not back attack lda $12 eor #$ff sec sbc #$20 sta $12 ; start of digit loop @e590: lda $12 sta $0340,x ; set sprite x position clc adc #$08 sta $12 … lda $dbe6,x ; set sprite tile id plx sta $0340,x inx lda $f0ed,y sta $0340,x ; set sprite flags … iny ; next character/monster cpy #13Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L2392-L2415 — 10 bytes of character values and 16 of monster values, two bytes each, so 5 characters and 8 monstersShowMsg_04: @c9a6: clr_ax @c9a8: lda $34d4,x ; copy character damage values to buffer sta $dbf6,x inx cpx #10 … @c9b6: lda $34de,x ; copy monster damage values to buffer sta $dbe6,x inx cpx #$0010 … @c9c6: lda $dbe6,x ; put monsters before charactersChecked by claude-agent, 2026-11-08 — read the damage-number message from the value word through the digit buffer, the colors and the miss and zero cases, the decimal conversion and the leading-zero step, and the per-frame drawing loop over 13 combatantsRevised after a check by a second AI model, 2026-11-08.The flag in $f0fa is 0 to draw, 1 to skip, $FF for a miss, the reverse of what the source's comments on it say: the routine sets it to 1 when the whole value word is 0. Each combatant gets five buffer bytes, the first left blank, and the draw loop reads five from the second, so the fifth sprite is the next combatant's blank byte. Sprite attribute bits 1 to 3 are the palette, so $31 is palette 0 and $3D palette 6. Values from 10,000 to 16,383 would lose their first digit on screen; this claim doesn't follow whether the battle program ever stores one.
A damage or healing number lives 41 frames (computed), and its digits bounce one after another, left to right: each digit follows a 65-byte table of heights, the leftmost 12 frames ahead of the blank fifth sprite and each digit to its right 3 frames behind the one before. A digit rests for its first 4 table steps, rises to 16 pixels by step 12, holds there to step 14, comes back down on step 22, bounces once to 5 pixels at steps 26 to 28, and lands on step 33. A miss's tiles all bounce together, on the rightmost digit's timing. ff4-av/damage-number-bounce
Value
{
"frames": 41,
"digitDelay": 3,
"heights": [
0,
0,
0,
0,
0,
3,
6,
9,
12,
14,
15,
15,
16,
16,
16,
15,
15,
14,
12,
9,
6,
3,
0,
2,
4,
4,
5,
5,
5,
4,
4,
2,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
"peak": 16,
"bounce": 5
}btlgfx/btlgfx_data.asm#L1137-L1143; damage numeral y-offsets (for bouncing) DmgNumeralBounceTbl: @fc8a: .byte $00,$00,$00,$00,$00,$fd,$fa,$f7,$f4,$f2,$f1,$f1,$f0,$f0,$f0,$f1 .byte $f1,$f2,$f4,$f7,$fa,$fd,$00,$fe,$fc,$fc,$fb,$fb,$fb,$fc,$fc,$fe .byte $ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 .byte $00Final Fantasy IV Disassembly · btlgfx/sprite.asm#L1480-L1561DrawDmgNumerals: @e505: lda $f107 beq @e51a ; return if damage numerals are disabled jsr ResetAnimSpritesSmall inc $f108 lda $f108 cmp #$2a bne @e51d stz $f107 ; disable damage numerals animation … lda $f108 tax lda $14 bne @e567 ; branch if miss @e552: lda f:DmgNumeralBounceTbl,x clc adc $0e sta a:$0013,y inx3 ; successive digits are delayed by 3 frames iny cpy #5 bne @e552 bra @e57c @e567: lda $f108 tax lda f:DmgNumeralBounceTbl,x clc adc $0e ; miss tiles bounce together sta $13 … @e57c: lda #5 ; loop through 5 digits sta $0e … @e590: lda $12 sta $0340,x ; set sprite x position clc adc #$08 sta $12 inx phx lda $0e dec tax lda $13,x plx sta $0340,x ; set sprite y positionFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L595-L597 — the battle NMI draws the numbers each framejsr UpdateAnimSprites jsr UpdateTargetPal jsr DrawDmgNumeralsChecked by claude-agent, 2026-11-08 — read the bounce table and the drawing routine: the frame counter, the five heights taken 3 table steps apart, the digit loop that gives the first sprite (the leftmost) the last height, the miss branch, and the NMI call; heights are the table's bytes negated (screen y grows downward), the 41 frames computed from the counter, which is 1 to 41 on the frames it drawsRevised after a check by a second AI model, 2026-11-08.The counter is incremented before it is read, and on the frame it reaches $2A (42) the numbers are switched off and not drawn, so they show for counts 1 to 41. Sprite k from the left takes the table at the count plus 3 × (4 − k), so the leftmost digit's height runs 12 steps ahead (computed). Every digit has finished its bounce by count 30 (the ones digit, at 3 steps ahead, reaches step 33 then), and the numbers then sit still to the end.
When ordinary monsters die, the dying monsters' tiles switch to background palette 7, which is loaded with a 16-color death palette (after a transparent color and a dark blue, six shades of violet from red 22, green 16, blue 31 down to 4, 0, 13), and the death sound plays: $10 for an ordinary kill. For 48 frames every color of that palette loses one step of red, green and blue (of 31), stopping at black, so the figures are black from the 31st frame, and each frame an HDMA table picks, line by line, which of two copies of the monster layer shows: one drawn with the dying monsters, one without. Within each 32-line band the lines still showing the dying monsters fall from 14 of every 16 to 2 (computed), in a pattern that shifts each frame and mirrors about the band's middle; at the end every line shows the copy without them. ff4-av/monster-death
About: Final Fantasy IV HDMA
Value
{
"frames": 48,
"sfx": 16,
"palette": 7,
"colors": [
[
0,
0,
0
],
[
0,
0,
11
],
[
22,
16,
31
],
[
22,
10,
31
],
[
22,
0,
31
],
[
16,
0,
25
],
[
10,
0,
19
],
[
4,
0,
13
],
[
0,
0,
0
],
[
0,
0,
17
],
[
31,
31,
31
],
[
26,
26,
31
],
[
20,
20,
31
],
[
14,
14,
31
],
[
8,
8,
31
],
[
0,
0,
23
]
],
"linesShown": [
14,
14,
14,
14,
14,
13,
13,
13,
14,
14,
13,
13,
13,
13,
12,
12,
13,
12,
12,
11,
11,
11,
10,
10,
9,
8,
8,
7,
7,
6,
6,
5,
4,
4,
4,
4,
3,
3,
3,
2,
2,
2,
2,
3,
2,
2,
2,
2
]
}btlgfx/monster_death.asm#L97-L123@e969: clr_ax @e96b: lda $29b5,x cmp $f12b,x beq @e981 phx txa sta $f109 lda #$07 sta $f10a jsr _028a89 plx @e981: inx cpx #8 bne @e96b jsr ModifyBG1Tiles_near jsr _02e8e2 jsr TfrLeftMonsterTiles jsr LoadMonsterDeathPal_near lda #1 jsr SwapMonsterScreen jsr LoadMonsterTiles jsr TfrRightMonsterTiles jsr MonsterDeathAnim clr_a jsr SwapMonsterScreen @e9a5: stz $f483 rtsFinal Fantasy IV Disassembly · btlgfx/ppu.asm#L91-L100 — the monster's tilemap entries get palette 7 in their attribute bits@8af0: lda $f10a asl2 sta $06 @8af7: ldy #$0001 lda $00 tax @8afd: lda ($04),y and #$e3 ora $06 sta ($04),yFinal Fantasy IV Disassembly · btlgfx/monster_death.asm#L129-L159MonsterDeathAnim: @e9a9: lda $f483 bne @e9be lda #$80 sta $f485 lda $38e6 tax lda f:MonsterDeathSfxTbl,x jsr PlaySfx @e9be: stz $f483 jsr WaitFrame clr_ax @e9c6: lda $ee30,x sta $00 lda $ee31,x sta $01 lda #1 jsr DecColor … inx2 cpx #$0020 bne @e9c6 jsl UpdateMonsterDeathAnim inc $4e lda $4e cmp #$30 bne @e9be rtsFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3281-L3290LoadMonsterDeathPal: @f541: stz $4e clr_axy @f546: lda f:AnimPal+5*16,x sta $ee30,y inx iny cpy #$0020 bne @f546 rtlFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L4614-L4622 — the palette buffer starts at $ED50, so $EE30 is color 112, palette 7's firstTfrPal: @febe: phb clr_a pha plb sta hCGADD ldx #$2202 stx $4340 ldx #$ed50Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L1760-L1785DecColor: @bd43: sta $26 stz $27 longa lda $26 asl5 sta $28 asl5 sta $2a ; red lda $00 and #$001f beq @bd74 cmp $26 bcc @bd6d lda $00 sec sbc $26 sta $00 bra @bd74 ; set red component to zero @bd6d: lda $00 and #$7fe0 sta $00Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L4121-L4170UpdateMonsterDeathHDMA: @fae5: clr_ay lda $4e asl tax @faeb: lda f:MonsterDeathHDMATbl,x sta $01 lda f:MonsterDeathHDMATbl+1,x sta $00 inx2 longa asl $00 rol $02 shorta0 lda $02 and #$01 eor #$01 sta $7613,y sta $7693,y sta $7713,y sta $7793,y sta $7813,y sta $7893,y iny4 cpy #$0040 bne @faeb ldy #$0040 … sta $764f,y sta $76cf,y sta $774f,y sta $77cf,y sta $784f,y dey4 bne @fb2a rtsFinal Fantasy IV Disassembly · btlgfx/btlgfx_data.asm#L795-L807MonsterDeathHDMATbl: @f99d: .word $0101,$0202,$0404,$0808,$1010,$2120,$4240,$8480Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L1673-L1679SwapMonsterScreen: @bcc2: ldx #0 @bcc5: sta $7613,x ; set bg1 h-scroll hdma data inx4 cpx #$0230 bne @bcc5 rtsFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3556-L3557MonsterDeathSfxTbl: @f713: .byte $10,$59,$59,$59,$21,$10Final Fantasy IV Disassembly · battle/battle.asm#L495-L496 — BG1's map is 64 by 64 tiles, two 256-pixel halves side by sidelda #$63 sta hBG1SCFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L384-L387 — the monster entry ends with every line at 0pla bne @8251 clr_a jsr SwapMonsterScreenFinal Fantasy IV Disassembly · battle/cmd.asm#L60-L64; [ battle command $24: run away ]
Cmd_24:
@e1b5: lda #$04
sta $38e6Final Fantasy IV Disassembly · battle/magic.asm#L1289-L1293; [ magic effect $0a: life ]
MagicEffect_0a:
@d64b: lda #$02
sta $38e6Final Fantasy IV Disassembly · battle/magic.asm#L2209-L2213; [ magic effect $25: explode/fission ]
MagicEffect_25:
@dd1d: lda #$05
sta $38e6Final Fantasy IV Disassembly · battle/magic.asm#L2229-L2233; [ magic effect $26: reaction ]
MagicEffect_26:
@dd32: lda #$05
sta $38e6Final Fantasy IV Disassembly · battle/magic.asm#L2293-L2296; [ magic effect $29: alert/call ]
MagicEffect_29:
@dd9b: inc $38e6Final Fantasy IV Disassembly · battle/magic.asm#L2360-L2364; [ magic effect $30: revive monster ]
MagicEffect_30:
@dde1: lda #$02
sta $38e6Final Fantasy IV Disassembly · battle/magic.asm#L2401-L2405; [ magic effect $31: change form ]
MagicEffect_31:
@de34: lda #$03 ; monster death type
sta $38e6Final Fantasy IV Disassembly · battle/battle.asm#L1027stz $38e6Final Fantasy II (USA) (Rev 1) · 0x74b50-0x74b6f — the death palette, animation palettes 5 and 6 ($0E:CB50)Checked by claude-agent, 2026-11-08 — read the death routine's ordinary branch, the palette-7 retiling, the palette load and its buffer's place in color memory, the per-color darkening, the HDMA update and its table, the screen swap, and the death sound table; decoded the 32 palette bytes from the ROM; the lines-shown counts are computed from the table's words (each line takes bit 7 of a word's first byte, inverted)Checked by a second AI model, 2026-11-08.Each per-line value is the high byte of BG1's horizontal scroll, which picks one of the two 256-pixel halves of BG1's 512-pixel map. Before the animation the routine draws the monsters with the dying ones recolored into one half and shows it everywhere (value 1), then draws the survivors into the other half; it ends with every line at 0, and the battle shows 0 after the monster entry too, so 1 is the copy with the dying monsters. Line k of each band takes table entry (frame + k) for k = 0 to 15, and lines 16 to 31 mirror lines 15 to 0. The death sound is indexed by the death type the battle program sets: 0 for an ordinary kill, 2 for the Life effect and a monster's revival, 1 from the call for help, 4 for a monster running away, 5 for Explode and the reaction effect; type 3, a change of form, skips the animation. Only types 0 and 5 play $10. The two halves of the death palette are animation palettes 5 and 6; the dying monsters' tiles show the colors their pixels select.
In the 9 battles flagged for it (of 512; computed), a monster killed in the ordinary way dies by a boss's effect instead: sound effect $5E plays, and after 16 frames the monster layer is erased line by line from the top, one more line every 4 frames, 128 lines over 512 frames (computed), while it shakes sideways each frame by 1, 2, 4, 2, 1, 0, 2 and 2 pixels in turn. Below the erased lines runs a 32-line band in which 11 lines are erased too, every other line next to the edge and fewer farther down, so the edge is dithered. Then it requests sound effect 0, which plays nothing; since every sound request first keys off the two sound-effect channels, this ends the boss's sound. The palette is left alone. ff4-av/boss-death
Value: battles 9 · of 512 · sfx 94 · wait frames 16 · lines 128 · frames per line 4 · frames 512 · band 32 · band erased 0, 2, 4, 6, 8, 10, 12, 15, 18, 22, 27 · shake 1, 2, 4, 2, 1, 0, 2, 2
CodeFinal Fantasy IV Disassembly ·btlgfx/monster_death.asm#L73-L96@e928: lda $29a4 and #$10 beq @e940 lda $38e6 bne @e940 jsr _02c46d jsr _02e8dd jsl _03f418 bra @e960 … @e960: jsr _02e8e2 clr_a jsr PlaySfx bra @e9a5Final Fantasy IV Disassembly · btlgfx/cmd_anim.asm#L592-L597_02c46d: @c46d: phx jsr ClearTileBuf jsr TfrLeftMonsterTiles plx rtsFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3136-L3214_03f418: @f418: lda #$5e jsl PlaySfx_far clr_ax stx $f406 stx $f408 stx $f173 ldx #$0010 jsr WaitX_near clr_axy stx $f4a2 longa @f437: lda f:_03f479,x asl and #$0100 sta $dbe6,y inx iny2 cpy #$0040 bne @f437 shorta0 @f44d: jsl WaitFrame_far inc $f173 lda $f173 and #$07 tax lda f:AnimShakeTbl,x tay sty $f406 jsr _03f49a lda $f173 and #$03 bne @f44d ldx $f4a2 inx stx $f4a2 cpx #$0080 bne @f44d rtl … _03f479: @f479: .byte $80,$00,$80,$00,$80,$00,$80,$00,$80,$00,$80,$00,$80,$00,$00,$80 .byte $00,$00,$80,$00,$00,$00,$80,$00,$00,$00,$00,$80,$00,$00,$00,$00 .byte $00 … _03f49a: @f49a: phx longa lda $f4a2 sta $02 lda #$0100 sta $00 clr_ayx @f4aa: lda $00 sta $7612,y dec $02 bpl @f4ca lda $f406 sta $00 cpx #$0040 beq @f4ca lda $dbe6,x sta $04 lda $00 eor $04 sta $00 inx2 @f4ca: iny4 cpy #$0230 bne @f4aa shorta0 plx rtsFinal Fantasy IV Disassembly · btlgfx/btlgfx_data.asm#L595-L597; screen shake offsets for animations AnimShakeTbl: @ffdd: .byte $01,$02,$04,$02,$01,$00,$02,$02Final Fantasy IV Disassembly · battle/battle.asm#L271-L278@8103: lda f:BattleProp,x sta $299c,y ; load battle properties … lda $299c sta $29a4Final Fantasy IV Disassembly · vanilla/ff4-en-rip.json#L1147-L1158"flags1": { "type": "property", "name": "Flags", "begin": 0, "mask": "0x18", "flag": true, "stringTable": { "string": { "0": "Force Back Attack", "1": "Boss Death Animation"Final Fantasy II (USA) (Rev 1) · 0x70000-0x70fff — the 512 battles' properties, 8 bytes each; bit 4 of the first byte is set in battles $DC, $E3, $E5, $EA, $F3, $FF, $1A1, $1A2 and $1B3When ordinary monsters die, the dying monsters' tiles switch to background palette 7, which is loaded with a 16-color death palette (after a transparent color and a dark blue, six shades of violet from red 22, green 16, blue 31 down to 4, 0, 13), and the death sound plays: $10 for an ordinary kill. For 48 frames every color of that palette loses one step of red, green and blue (of 31), stopping at black, so the figures are black from the 31st frame, and each frame an HDMA table picks, line by line, which of two copies of the monster layer shows: one drawn with the dying monsters, one without. Within each 32-line band the lines still showing the dying monsters fall from 14 of every 16 to 2 (computed), in a pattern that shifts each frame and mirrors about the band's middle; at the end every line shows the copy without them. Code — the ordinary death sets which half of BG1 each line shows; 1 is the half this routine clearsFinal Fantasy IV Disassembly · notes/ff4-spc.asm#L1943-L194515CD: E8 C0 MOV A, #$C0 ; channel 6 and 7
15CF: 8D 5C MOV Y, #$5C ; key off
15D1: 3F E9 10 CALL $10E9 ; set dsp registerBattle sounds are requested by number, with a stereo position: an attack's animation looks its sound up in a 256-entry table, and the battle code gives it pan $C0 or $40 by bit 7 of the attack, which side each means not followed, or the center, $80, for sounds $31 and $33, reversing the pan in a back attack. The sound program finds the number in a table of 128 entries, each two script pointers, and plays one script on each of the last two of its 8 channels, cutting off whatever they held; 99 of the 128 use both channels, 27 one, and 2, numbers 0 and 79, none (all computed). Menu sounds such as the cursor and the error buzz are a separate set of 16 numbers with their own table. Code — entries 0 and 79 play on no channelChecked by claude-agent, 2026-11-08 — read the death routine's boss branch, the clear of the half it erases into, the boss routine with its band mask and per-line scroll writer, the shake table, the property byte's load and the rip's name for its bit; counted the flagged battles in the ROM's property table; the band's erased lines and the timing are computed from the mask and the countersRevised after a check by a second AI model, 2026-11-08.Each of the 140 lines' BG1 scroll gets $0100, the half the routine has cleared, if it lies at or above the edge; the shake offset with the mask's $0100 added (exclusive or) in the 32-line band below; and the shake offset alone farther down. The edge line is the counter, which starts at 0 and stops after 127, so its last frame leaves lines 0 to 127 erased and the band's first 12 lines below it. The flag is bit 4 of the battle's first property byte, which the disassembly's rip definition names Boss Death Animation; a kill by another death type takes the ordinary death in these battles too.
A spell's animation runs from a 4-byte entry in a table of 192 attack animations: its palette, its graphics, an animation script and one of 48 hand-written animation routines; $FF in either the script byte or the routine byte skips the whole animation. The routine runs the script in its own way: routine 0 runs it on the targets, then again for a reflected spell. A script opens with two bytes. The first gives one of 16 frame sizes, from 1 by 1 to 16 columns by 9 rows of 16-pixel tiles, and how many times the script repeats, 1 to 16. The second gives the speed, a step every 8, 4, 2 or 1 frames, a bit that turns off mirroring, and in bits 0 to 2 which block of 128 animation frames the script's frame numbers count from. Then comes a byte stream with five kinds of byte: $00 to $7F shows that frame and ends the step, $FF ends the script, $88 and a parameter byte starts or stops a palette effect, other bytes with bit 3 set start a flashing palette on the targets, and the rest move the animation 1 to 8 pixels in one of 8 directions. Commands take no time. ff4-av/anim-script-format
Value: entries 192 · routines 48 · scripts 79 · frame sizes 16 · repeats 1, 16 · frames per step 8, 4, 2, 1 · move distances 1, 8 · directions 8
CodeFinal Fantasy IV Disassembly ·btlgfx/magic.asm#L174-L212@eafe: dec longa asl2 tax shorta0 lda f:AnimProp,x phx ldx #$000f sta $f457 jsr LoadAnimPal plx lda f:AnimProp+1,x phx jsr LoadMagicGfx … lda f:AnimProp+2,x cmp #$ff beq @eb51 sta $f397 … lda f:AnimProp+3,x cmp #$ff beq @eb51 asl tax lda f:MagicAnimTbl,x ; magic animation jump table … @eb57: stz $f42b jmp ($0000)Final Fantasy IV Disassembly · btlgfx/magic.asm#L235-L284; magic animation jump table MagicAnimTbl: @eb79: .addr MagicAnim_00 … .addr MagicAnim_2fFinal Fantasy IV Disassembly · vanilla/ff4-en-rip.json#L252-L258"attackGraphicsProperties": { "type": "array", "name": "Attack Graphics Properties", "range": "0x0FA050-0x0FA34F", "arrayLength": 192, "itemLength": 4, "asmSymbol": "AnimProp",Final Fantasy IV Disassembly · btlgfx/magic.asm#L2019-L2276ExecAnimScript: @f6bc: asl tax … lda f:AnimScriptPtrs,x … @f70f: ldx $02 inx2 stx $f27c lda [$02] and #$f0 ; frame size id lsr3 tax lda f:AnimFrameSizeTbl,x sta $f24d lda f:AnimFrameSizeTbl+1,x sta $f24e lda [$02] and #$0f inc sta $f27b ; repeat count … ldy #1 lda [$02],y ; animation speed id and #$18 lsr3 tax lda f:AnimSpeedTbl,x sta $f29f ldy #1 lda [$02],y bpl @f77c stz $f261 … ; start of frame loop @f7a3: jsr WaitFrame … @f81f: inc $f27e @f822: … lda [$02],y ; get script command cmp #$ff bne @f839 jmp @f8c8 @f839: lda [$02],y ; get script command again bmi @f840 jmp @f8aa ; $88: enable animation palette effect @f840: sta $06 cmp #$88 bne @f855 inc $f27f iny lda [$02],y ; palette effect parameter inc $f44e jsr EnableAnimPalEffect jmp @f878 ; $8c: disable target palette effect @f855: and #$08 beq @f87e and #$04 beq @f863 jsr DisableTargetPalEffect jmp @f878 ; $8f: enable target palette effect @f863: lda $06 pha lsr4 and #$07 jsr _02f524 pla and #$03 sta $f281 jsr EnableTargetPalEffect @f878: inc $f27f jmp @f822 ; move animation sprite @f87e: lda $06 lsr4 and #$07 asl tax lda $06 ; direction and #$07 inc sta $08 ; distance jsr MoveAnim … jmp @f822 ; show animation frame @f8aa: and #$7f sta $f248 … lda $f27e and $f29f bne @f8c5 inc $f27f ; go to next animation frame @f8c5: jmp @f7a3 ; end of script @f8c8: stz $f27f … dec $f27b ; decrement repeat counter beq @f8db jmp @f7a3Final Fantasy IV Disassembly · btlgfx/btlgfx_data.asm#L572-L574AnimFrameSizeTbl: @ff3e: .byte $01,$01,$03,$03,$02,$02,$05,$09,$0c,$09,$10,$09,$04,$04,$04,$09 .byte $03,$04,$0c,$01,$02,$03,$06,$09,$01,$02,$04,$09,$06,$09,$06,$06Final Fantasy IV Disassembly · btlgfx/btlgfx_data.asm#L592-L593AnimSpeedTbl: @ffd9: .byte $07,$03,$01,$00Final Fantasy IV Disassembly · btlgfx/magic.asm#L2511-L2544MoveAnim: @fa78: lda $f261 beq @fa82 txa clc adc #$10 tax @fa82: lda f:MoveAnimTbl,x … MoveAnimTbl: @fa91: .addr MoveAnimDownBack .addr MoveAnimDown .addr MoveAnimDownForward .addr MoveAnimBack .addr MoveAnimForward .addr MoveAnimUpBack .addr MoveAnimUp .addr MoveAnimUpForward ; h-flip animationFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3220-L3255 — with mirroring on, each frame tile is drawn flipped horizontally_03f4d8: … lda $f261 beq @f51a lda $ebe9,y eor #$40 sta $ebe9,yFinal Fantasy IV Disassembly · btlgfx/btlgfx_data.asm#L708-L709AnimScriptPtrs: make_ptr_tbl_rel AnimScript, 79, .bankbyte(*)<<16Final Fantasy IV Disassembly · btlgfx/magic.asm#L191-L209lda f:AnimProp+2,x cmp #$ff beq @eb51 … lda f:AnimProp+3,x cmp #$ff beq @eb51 … jsr @eb57 @eb51: stz $f2d0 jmp ResetAnimSpritesLargeFinal Fantasy IV Disassembly · btlgfx/magic.asm#L1385-L1422MagicAnim_00: @f30d: stz $f2a0 _02f310: @f310: jsr MagicAnim_1c jmp ReflectMagicAnim … MagicAnim_1c: @f316: lda $34c4 ; character/monster target … DefaultMagicAnim: @f322: lda $3522 ; targets reflected off of sta $02 lda $f397 ; animation script jmp ExecAnimScript … ReflectMagicAnim: … lda $3523 ; targets reflected onto sta $01 stz $02 lda $f397 ; animation script jmp ExecAnimScriptFinal Fantasy IV Disassembly · btlgfx/magic.asm#L2203-L2207 — after `and; $8c: disable target palette effect
@f855: and #$08
beq @f87e
and #$04
beq @f863Final Fantasy IV Disassembly · btlgfx/magic.asm#L2103-L2105@f77c: lda [$02],y
and #$07
sta $f249Final Fantasy IV Disassembly · btlgfx/magic.asm#L2931-L2956lda $f24d asl3 sta $12 lda $f24e asl3 sta $13 … lda $f248 tax stx $1c lda $f249 longa asl7 clc adc $1c asl tax shorta0 lda f:AnimFramePtrs,xFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3256-L3271 — the first size byte counts tiles across a row, 16 pixels each, the second the rows@f51e: lda $12 clc adc #$10 sta $12 dec $10 bne @f53f lda $14 sta $12 lda $13 clc adc #$10 sta $13 lda $f24d sta $10 dec $11Checked by claude-agent, 2026-11-08 — read the animation start from the property entry to the routine's jump, the 48-entry routine table, the rip's size for the property table, the script interpreter from its header through every byte kind to the repeat, the frame-size and speed tables, the move dispatch with its mirrored half, the frame drawer's flip, and the 79-entry pointer tableRevised after a check by a second AI model, 2026-11-08.A frame byte advances the script only on frames where the frame counter masked by the speed byte ($07, $03, $01 or $00) is 0, so each frame shows for that many frames plus one. The frame sizes are the table's pairs of columns and rows; the drawer steps 16 pixels per tile and centers the frame by half of each. The block byte times 128 plus the frame number indexes the frame pointer table, which has 361 entries. The parameter of $88 is a palette swap when its bit 3 is set and a color flash otherwise (ff4-av/color-flash). Mirroring is on unless the header's bit 7 is set; it flips the frames and swaps forward and back. The bit-3 bytes have a stop branch, which the disassembly's comment calls $8C, but the test before it can't send a byte there; the one such byte in the ROM's scripts, $8F, starts a flash.
The 79 animation scripts hold 865 frame bytes and 407 moves, and the commands spread unevenly: 53 scripts flash a palette on their targets, 37 start a palette swap, 33 move the animation, and 15 start a color flash. 49 run at a step every 2 frames, 16 every 4, 9 every frame and 5 every 8, and 43 play once while the rest repeat, up to 9 times. 55 turn mirroring off (all computed). ff4-av/anim-scripts-use
Value
{
"scripts": 79,
"frameBytes": 865,
"moves": 407,
"targetFlash": 53,
"paletteSwap": 37,
"moving": 33,
"colorFlash": 15,
"speeds": {
"1": 9,
"2": 49,
"4": 16,
"8": 5
},
"once": 43,
"maxRepeat": 9,
"mirrorOff": 55
}0x7d5e0-0x7dd08 — the 79 script pointers ($0F:D5E0) and the scripts they point to, through the last one's end byte at $0F:DD08Checked by claude-agent, 2026-11-08 — parsed all 79 scripts from the ROM with the interpreter's rules (header, frame bytes below $80, $88 with its parameter, bit 3 for the target palette, the rest moves, $FF ending); every script ends inside the blockChecked by a second AI model, 2026-11-08.The palette-swap and color-flash counts split $88's parameter by its bit 3, as the interpreter does; a parameter of $08, which stops the swap, isn't counted. A script can use several kinds.
Spells flash the screen with color math: a fixed color, any mix of red, green and blue, is added to BG1 and BG2, the monsters and the battle background, by the color math HDMA table, which covers the top 140 lines (computed) and turns color math off below them, over the menu. Five kinds of flash exist: on 2 frames and off 2 until stopped, a pulse rising from 0 to full (31) and back a step a frame until stopped, a single frame, a rise to a set level that holds, and a fall to 0 that ends it. Scripts start one with command $88; routines call it by color. The routine the source names Black Hole darkens the background and flashes white for 32 frames; Big Bang flashes green, red, yellow, blue and white for 8 frames each, four times over. ff4-av/color-flash
About: Final Fantasy IV Color math
Value: kinds 5 · lines 140 · full 31 · black hole frames 32 · big bang frames 8 · big bang rounds 4 · big bang colors green, red, yellow, blue, white
CodeFinal Fantasy IV Disassembly ·btlgfx/btlgfx.asm#L4298-L4398UpdateColorMath: @fc3e: lda $ef87 ; color flash type bne @fc46 jmp @fd24 @fc46: lda $f49b beq @fc50 lda $f49b bra @fc52 @fc50: lda #$03 @fc52: jsr SetColorMath lda #$e0 sta f:hCOLDATA … ; 5: decrease @fc76: lda $f435 beq @fc86 dec $f435 … @fc86: stz $f49b stz $ef87 bra @fcdb ; 4: increase @fc8e: lda $f435 cmp $f436 beq @fc9f inc $f435 … ; 2: gradual, 6: ??? @fca1: lda $ef8a bne @fcbf lda $f435 cmp #$1f beq @fcb8 inc $f435 … @fcb8: lda #$01 sta $ef8a bra @fcdb @fcbf: lda $f435 beq @fccf dec $f435 … @fccf: stz $ef8a bra @fcdb ; 1: every other frame, 3: once @fcd4: lda $ef89 and #$02 bne @fd20 @fcdb: lda $ef88 and #$80 beq @fceb lda $f433 ora #$80 sta f:hCOLDATA … @fd0b: lda $ef87 cmp #$03 bne @fd20 inc $ef89 lda $ef89 and #$02 bne @fd1f stz $ef87 @fd1f: rtl @fd20: inc $ef89 rtlFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L4418-L4421SetColorMath: @fd48: sta f:hCGADSUB ; set register directly sta $f43d ; set value in hdma table sta $f43fFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L4560-L4604InitHDMA: @fe46: lda #$64 sta $f43c lda #$28 sta $f43e lda #$01 sta $f440 stz $f441 stz $f442 … lda #$00 sta $4370 lda #$31 ; color math sta $4371 ldx #$f43c stx $4372Final Fantasy IV Disassembly · btlgfx/special_anim.asm#L139-L174FlashScreenMagenta: @eead: lda #$a0 ; blue + red … FlashScreenWhite: @eec5: lda #$e0 ; white _eec7: sta $ef88 ; set flash color components lda #$1f sta $f433 sta $f435 sta $f434 lda #$01 sta $ef87Final Fantasy IV Disassembly · btlgfx/magic.asm#L1953-L1978; bgr-0ttt color flash ; b: flash blue ; g: flash green ; r: flash red ; t: flash type ; 0: disabled ; 1: every other frame ; 2: gradual ; 3: once ; 4: increase only ; 5: decrease only @f673: pla pha and #$e0 sta $ef88 … pla and #$07 sta $ef87Final Fantasy IV Disassembly · btlgfx/magic.asm#L288-L297; [ magic animation $2f: black hole ] MagicAnim_2f: @ebd9: jsr PlayMagicSfx jsr DarkenBattleBG jsl FlashScreenWhite ldx #32 jsr WaitX stz $ef87 jmp LightenBattleBGFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3771-L3799; [ big bang ] BigBang: @f86f: jsr FinalBGScrollBack … ldx #4 ; repeat 4 times @f88f: phx jsl FlashScreenGreen jsr Wait8 jsl FlashScreenRed jsr Wait8 jsl FlashScreenYellow jsr Wait8 jsl FlashScreenBlue jsr Wait8 jsl FlashScreenWhite jsr Wait8 plx dex bne @f88f stz $ef87Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3760-L3767Wait8: @f864: ldx #8 ; repeat 8 times WaitX_near: @f867: jsl WaitFrame_far dex bne @f867 rtsChecked by claude-agent, 2026-11-08 — read the color math update with each flash type, the register-and-table setter, the HDMA channel 7 setup and its three-entry table, the screen-flash entry points, the $88 parameter's flash branch, and the Black Hole and Big Bang routines with their waitsRevised after a check by a second AI model, 2026-11-08.The flash types are the routine's: 1 tests bit 1 of a counter that rises each frame, so 2 frames on and 2 off; 2 climbs to 31 and back and turns round at 0 again; 3 stops after one frame; 4 climbs to a target value; 5 falls and then turns the flash off. The screen-flash entry points start type 1 at full strength, and a routine stops it by clearing the type. The color math value $03 adds to BG1 and BG2; a routine can set another in its place. The HDMA table writes the color math control once for 100 lines and again for 40, then 0, so 140 lines (computed). Type 6 behaves like type 2.
In the 2 battles flagged for transparent monsters (of 512; computed), the monsters show at half strength over the battle background: color math puts BG2 on the sub screen and adds it to BG1, the monsters' layer, halving the sum. Odin's summon sets the same color math partway through its attack loop, from the 17th of its 64 frames (computed). ff4-av/transparent-monsters
About: Final Fantasy IV Color math
Value: battles 2 · of 512 · cgadsub 65 · sub screen 2
CodeFinal Fantasy IV Disassembly ·btlgfx/btlgfx.asm#L4399-L4407@fd24: lda $ed4e and #$80 beq @fd3b ; branch if monsters are not transparent lda #$02 sta f:hCGSWSEL sta f:hTS lda #$41 jsr SetColorMath rtlFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L1596-L1604lda $1800 ; battle id … lda f:BattleProp+6,x sta $ed4eFinal Fantasy IV Disassembly · vanilla/ff4-en-rip.json#L1160-L1172"flags2": { "type": "property", "begin": 6, "mask": "0xF3", … "7": "Transparent Monsters"Final Fantasy IV Disassembly · btlgfx/summon.asm#L1346-L1386OdinSummonAnim: … lda #$62 sta f:hBG1SC clr_a sta $f133 … @fac9: jsr WaitFrame_near lda $f133 cmp #$10 bcc @fae7 lda #$02 sta f:hCGSWSEL sta f:hTS lda #$41 sta f:hCGADSUB … inc $f133 inc $f134 lda $f133 cmp #$40 bne @fac9Final Fantasy II (USA) (Rev 1) · 0x70000-0x70fff — bit 7 of the seventh property byte is set in battles $1A1 and $1A2The battle screen opens from black over 16 frames, its brightness rising by one step a frame from 0 to full (15), while the battle background on BG2 comes out of a mosaic: blocks 16 pixels square at first, shrinking by one pixel every 2 frames to none after 32 frames. Meanwhile the monsters slide into place over 22 frames: their layer's scroll, set per line through HDMA, falls from 252 pixels to 0 along a sine curve (252, 240, 228, … 24, 12, 0; computed), horizontally for an ordinary battle and negated in a back attack, or vertically for flying monsters; the directions (from the left, from the right in a back attack, from above) and which flag means flying are the source's comments, not followed. Player input waits until the menu is open and both effects are done. Code — BG2 is the battle background, BG1 the monstersChecked by claude-agent, 2026-11-08 — read the color math update's no-flash branch, where the flag byte is loaded, the rip's name for the bit, and Odin's summon loop; counted the flagged battles in the ROM's property tableRevised after a check by a second AI model, 2026-11-08.$41 in the color math control is half-add (bit 6) on BG1 (bit 0); $02 in the sub screen designation is BG2, and the same $02 in the window control selects the sub screen as the color source. While a flash runs, its own setting replaces this one. Odin's counter starts at 0 and rises one a frame, the transparency is set once it reaches 16, the 17th frame, and the loop ends when it reaches 64 (both computed). Odin's loop first points BG1 at another tile map, so what turns transparent there is Odin's layer; this claim doesn't follow what it holds.
Battles run in BG mode 1, with no Mode 7: the monsters are on BG1 and the battle background on BG2. Four HDMA channels run through a battle, three setting BG1, BG2 and BG3's horizontal and vertical scroll line by line, and one setting the color math control; these per-line tables carry the shakes, the monster deaths, the final battle's moving background and the flashes. ff4-av/battle-layers
About: Final Fantasy IV HDMA
Value: bg mode 1 · hdma channels 4
CodeFinal Fantasy IV Disassembly ·battle/battle.asm#L485-L486lda #$09
sta hBGMODEFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L4573-L4606lda #$43 sta $4300 sta $4310 sta $4320 lda #$0d ; bg1 scroll sta $4301 lda #$0f ; bg2 scroll sta $4311 lda #$11 ; bg3 scroll sta $4321 … lda #$31 ; color math sta $4371 … lda #$87 ; enable hdma sta hHDMAENThe battle screen opens from black over 16 frames, its brightness rising by one step a frame from 0 to full (15), while the battle background on BG2 comes out of a mosaic: blocks 16 pixels square at first, shrinking by one pixel every 2 frames to none after 32 frames. Meanwhile the monsters slide into place over 22 frames: their layer's scroll, set per line through HDMA, falls from 252 pixels to 0 along a sine curve (252, 240, 228, … 24, 12, 0; computed), horizontally for an ordinary battle and negated in a back attack, or vertically for flying monsters; the directions (from the left, from the right in a back attack, from above) and which flag means flying are the source's comments, not followed. Player input waits until the menu is open and both effects are done. Code — BG1 holds the monsters, BG2 the backgroundWhen ordinary monsters die, the dying monsters' tiles switch to background palette 7, which is loaded with a 16-color death palette (after a transparent color and a dark blue, six shades of violet from red 22, green 16, blue 31 down to 4, 0, 13), and the death sound plays: $10 for an ordinary kill. For 48 frames every color of that palette loses one step of red, green and blue (of 31), stopping at black, so the figures are black from the 31st frame, and each frame an HDMA table picks, line by line, which of two copies of the monster layer shows: one drawn with the dying monsters, one without. Within each 32-line band the lines still showing the dying monsters fall from 14 of every 16 to 2 (computed), in a pattern that shifts each frame and mirrors about the band's middle; at the end every line shows the copy without them. Code — deaths switch BG1's tile map line by lineChecked by claude-agent, 2026-11-08 — read the battle's PPU setup and the HDMA setup; searched battle/ and btlgfx/ for the BG mode and Mode 7 registers by name and address: the mode is written once, to 1, and no Mode 7 register is writtenChecked by a second AI model, 2026-11-08.$87 enables channels 0, 1, 2 and 7. Mode $43 writes two registers twice a line through a pointer table, a scroll register's low and high bytes for horizontal and vertical. The field's world map zoom into a battle is Mode 7 (ff4-av/world-battle-zoom); the battle itself isn't.
Quake shakes the screen by repeating an 8-frame pattern of offsets, 1, 2, 4, 2, 1, 0, 2 and 2 pixels, written to the battle background's scroll table line by line and to the monsters' or to some characters' positions. The final battle's background is never shaken this way. ff4-av/battle-shake
About: Final Fantasy IV Screen shake
Value: pattern 1, 2, 4, 2, 1, 0, 2, 2 · frames 8
CodeFinal Fantasy IV Disassembly ·btlgfx/btlgfx_data.asm#L596-L597AnimShakeTbl:
@ffdd: .byte $01,$02,$04,$02,$01,$00,$02,$02Final Fantasy IV Disassembly · btlgfx/magic.asm#L2152-L2171@f7ed: lda $f2d0 beq @f81f cmp #$01 bne @f817 lda $f27e and #$07 tax lda $f279 bne @f811 ; shake battle bg and characters lda f:AnimShakeTbl,x sta $f268 jsr UpdateShakeBattleBG jsr UpdateShakeChars jmp @f81f ; shake battle bg and monsters @f811: jsr UpdateShakeMonstersAndBGFinal Fantasy IV Disassembly · btlgfx/magic.asm#L920-L932; [ magic animation $07: quake ] MagicAnim_07: @ef91: lda $34c5 beq @efaa ldx #$80b0 stx $f289 ; sprite $30 is highest priority lda #$01 sta $f2d0 jsr MagicAnim_00Final Fantasy IV Disassembly · btlgfx/magic.asm#L1605-L1638UpdateShakeMonsters: @f497: pha ldx #0 @f49b: sta $7612,x … UpdateShakeBattleBG: @f4b2: pha lda $1802 ; battle bg id cmp #$10 bne @f4bc ; return if final battle bg pla rts @f4bc: pla ldx #0 @f4c0: sta $7992,xFinal Fantasy IV Disassembly · btlgfx/magic.asm#L2490-L2505UpdateShakeChars: @fa58: ldx #0 sta $00 lda $f284 sta $02 @fa62: asl $02 bcc @fa71 … sta $efc7,y @fa71: inx cpx #5Final Fantasy IV Disassembly · btlgfx/magic.asm#L498-L502_02ed0d: @ed0d: pha lda $34c4 and #$80 sta $f279Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3863-L3870lda $7992,y ; add to bg2 hdma h-scroll valueFinal Fantasy IV Disassembly · btlgfx/magic.asm#L1816-L1821UpdateShakeMonstersAndBG:
@f5cd: lda f:AnimShakeTbl,x
DisableShakeBG:
@f5d1: jsr UpdateShakeMonsters
jmp UpdateShakeBattleBGChecked by claude-agent, 2026-11-08 — read the script interpreter's shake step, Quake's routine that turns it on, the three shake writers, the 8-entry table, where the side flag is set, and the final background's scroll update naming the BG2 tableRevised after a check by a second AI model, 2026-11-08.The pattern is indexed by the script's frame counter masked to 0 to 7. Which side shakes comes from bit 7 of an attack byte: clear, the background shakes with the characters marked in a 5-bit mask; set, with the monster layer. That this bit marks the side the attack hits is a reading, not followed here. The background table is BG2's horizontal scroll, by the comment on the final background's update, so the shake is sideways. Values other than 1 in the shake switch pick other shake routines, not followed.
The final battle's background scrolls sideways in 18 strips, 17 of 8 lines and one of 4 (140 lines, computed), each moving its own number of pixels a frame: 5, 4, 3, 2 and 1 in the top five strips, none in the sixth, then 1 to 12 down to the bottom, all the same way. Big Bang first slows the rates by one a frame until every strip runs backward three times as fast (−15 to −36), flashes, and then brings them back the same way; the bottom strip, the last to arrive, takes 48 frames each way (computed). ff4-av/final-battle-background
About: Final Fantasy IV HDMA
Value: strips 18 · lines 140 · forward 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 · back -15, -12, -9, -6, -3, 0, -3, -6, -9, -12, -15, -18, -21, -24, -27, -30, -33, -36 · ramp frames 48
CodeFinal Fantasy IV Disassembly ·btlgfx/btlgfx.asm#L3847-L3881UpdateFinalBGScroll: @f902: phx phy lda $f4a9 bne @f947 lda $1802 cmp #$10 bne @f947 ; branch if not final battle bg clr_ax @f912: lda f:FinalBGScrollHDMAStripTbl,x tay sty $0e stz $11 lda $f488,x ; scroll rate for this strip sta $10 bpl @f924 ; convert to 16-bit dec $11 @f924: longa txa asl5 tay lda $7992,y ; add to bg2 hdma h-scroll value clc adc $10 @f933: sta $7992,y iny4 dec $0e bne @f933 shorta0 inx cpx #$0012 bne @f912Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3885-L3897; forward scroll rate (1x) FinalBGScrollForwardTbl: @f94a: .byte 5,4,3,2,1,0,1,2,3,4,5,6,7,8,9,10,11,12 ; backward scroll rate (3x) FinalBGScrollBackTbl: @f95c: .lobytes -15,-12,-9,-6,-3,0,-3,-6,-9,-12,-15,-18,-21,-24,-27,-30,-33,-36 ; hdma strip sizes (pixels) FinalBGScrollHDMAStripTbl: @f96e: .byte 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,4Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3807-L3841FinalBGScrollBack: @f8be: jsl WaitFrame_far stz $00 ldx #0 @f8c7: lda $f488,x cmp f:FinalBGScrollBackTbl,x beq @f8d5 dec $f488,x ; decrement to backward scroll rate inc $00 … FinalBGScrollForward: @f8e0: jsl WaitFrame_far … inc $f488,x ; increment to forward scroll rateFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L5835-L5839@eba3: lda f:FinalBGScrollForwardTbl,x
sta $f488,x
inx
cpx #$0012
bne @eba3Final Fantasy IV Disassembly · btlgfx/summon.asm#L36-L38UpdateSummon:
@f112: jsr DrawSummon
jsl UpdateFinalBGScrollSpells flash the screen with color math: a fixed color, any mix of red, green and blue, is added to BG1 and BG2, the monsters and the battle background, by the color math HDMA table, which covers the top 140 lines (computed) and turns color math off below them, over the menu. Five kinds of flash exist: on 2 frames and off 2 until stopped, a pulse rising from 0 to full (31) and back a step a frame until stopped, a single frame, a rise to a set level that holds, and a fall to 0 that ends it. Scripts start one with command $88; routines call it by color. The routine the source names Black Hole darkens the background and flashes white for 32 frames; Big Bang flashes green, red, yellow, blue and white for 8 frames each, four times over. Code — Big Bang's flashes between the two rampsA battle's background is picked on the field before the battle starts. On the world map, bits 0 to 2 of the tile under the party index a six-entry table of backgrounds ($00, $0A, $01, $09, $0F, $05); indexes 6 and 7 would read past the table, and this claim doesn't follow whether any tile gives them. On a town or dungeon map it is the map's own battle background, with bit 6 set for a cave with water when the tile's water bit is set, bit 5 set when the map asks for the alternate background palette, and the magnetized-cave flag cleared unless event switch $E1 is set. Battles $1B7 and $1B8 always get background $10, the final battle's. Code — background $10 is the final battle'sFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3771-L3800; [ big bang ] BigBang: @f86f: jsr FinalBGScrollBack … stz $ef87 jsr FinalBGScrollForwardChecked by claude-agent, 2026-11-08 — read the per-frame scroll update, its three 18-byte tables, the two ramps, the battle start copying the forward rates in, and the vblank routine that calls the updateRevised after a check by a second AI model, 2026-11-08.Each strip's rate is added to its lines' horizontal scroll every frame, from the vblank routine. Rates in the forward table are all zero or positive, so every strip moves the same way at a speed that grows with its distance from the sixth strip (lines 40 to 47, computed). Each ramp step waits a frame and moves every strip still short of its target by one; the bottom strip goes from 12 to −36, 48 steps. Big Bang's shake value 7 is set before the ramp.
When a battle is won, song 8, the fanfare, is requested, unless the battle kept the field's music instead of playing its own. Seven battles, listed by number, get the fanfare even then. Next, every character who is alive and not stone takes the victory pose, except in nine listed battles. ff4-av/victory-fanfare
Value: song 8 · fanfare list 220, 221, 225, 231, 423, 431, 438 · no pose list 235, 245, 240, 250, 425, 254, 243, 436, 439
CodeFinal Fantasy IV Disassembly ·battle/battle.asm#L1176-L1199@8728: jsr CheckFanfare lda $a9 bne @8738 lda $38e5 and #$0c cmp #$0c beq @8740 ; branch if no battle music @8738: lda #$08 ; victory fanfare sta $38be inc $38bd @8740: jsr ClearWinStatus … jsr CheckWinAnim lda $a9 bne @8754 lda #$12 ; battle graphics $12: victory animation jsr ExecBtlGfxFinal Fantasy IV Disassembly · battle/battle.asm#L1257-L1296CheckFanfare: @87d8: ldx #.loword(NoFanfareTbl) … CheckBattleList: @87e4: stz $a9 clr_ay @87e8: lda [$ab],y cmp #$ff beq @8802 cmp $1800 bne @87fe iny lda [$ab],y cmp $1801 bne @87ff inc $a9 rts … CheckWinAnim: @8803: ldx #.loword(NoWinAnimTbl)Final Fantasy IV Disassembly · battle/battle_data.asm#L257-L267; battles with no victory fanfare
NoFanfareTbl:
@fe67: .word $00dc,$00dd,$00e1,$00e7,$01a7,$01af,$01b6
.byte $ff
; ------------------------------------------------------------------------------
; battles with no victory animation
NoWinAnimTbl:
@fe76: .word $00eb,$00f5,$00f0,$00fa,$01a9,$00fe,$00f3,$01b4,$01b7
.byte $ffFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L228-L247WinAnim: @812d: inc $f472 jsr WaitMenuBattleEnd clr_axy @8136: tya tax jsr GetObjPtr lda $2003,x and #$c0 bne @814c ; skip if dead or stone lda #$08 sta $f099,yFinal Fantasy IV Disassembly · btlgfx/summon.asm#L49-L56@f133: lda $38bd
beq @f14a ; branch if no song change
lda $38be ; song id
sta $1e01
lda #$01 ; play song
sta $1e00
jsl ExecSound_ext
stz $38bdFinal Fantasy IV Disassembly · vanilla/ff4-en-rip.json#L7316"8": "Victory Fanfare",A battle's song comes from bits 2 and 3 of the seventh byte of its battle properties: values 0, 1 and 2 pick songs $27, $1A and $0B from a three-entry table (Battle 1, Battle 2 and The Dreadful Fight in the disassembly's battle editor), and 3 keeps the song that was playing. The battle program sends the song once the battle's data is loaded, before the battle graphics start, and repeats the request until the sound driver reports that song playing. Code — the song field whose value 3 keeps the field's musicThe song table has 70 entries, each a three-byte pointer to a song's script; this is the game's track listing, by song number, with where the game plays each, from every request found: the song byte of each of the 383 maps, 375 song commands in the event scripts, 49 song control codes in dialogue, 14 song commands in monster action scripts, the battle properties' song field, the world and vehicle song tables, and constants in the field, battle, menu, and cutscene code. 62 entries are requested; no request found reaches 56, 57, 58, 59, 60, 61, 64, 65. Entries 56, 57, 58, 59, 60, 61 point to the same script as entry 0. Code — song 8's requestsChecked by claude-agent, 2026-11-08 — read the victory branch, the list search with both bytes of the battle number, the two lists, the victory animation, the vblank routine that sends the song request, and the rip's name for song 8Revised after a check by a second AI model, 2026-11-08.The first list's label calls it the battles with no fanfare, but a match jumps straight to the request, so these battles play it where the music field alone would skip it; the label reads the list the wrong way round. The test for the field's music is the song field's value 3 (bits 2 and 3 both set). Pose 8 is the one the victory animation gives; the pose itself is a sprite frame not followed here.
After a win the gil and experience are added, each capped at 9,999,999, and the experience is shared out among the party members who are alive and not stone, at least 1 each when there is any. One window then shows "Received {n} GP." and "Received {n} Exp.", the experience being each member's share, either line left out when it is zero. Then, character by character, while one has enough experience for the next level and is below level 99, it gains the level, and the window shows "{name} gained a level." followed by one "Learned {spell}" for each spell that level teaches, so a character who gains two levels gets the pair twice. ff4-av/victory-messages
Value: cap 9999999 · max level 99 · party slots 5
CodeFinal Fantasy IV Disassembly ·battle/win.asm#L128-L151@ed73: clc lda $16a0 adc $3594 sta $16a0 … sec lda $16a0 sbc #$7f lda $16a1 sbc #$96 lda $16a2 sbc #$98 bcc @edb0 lda #$7f sta $16a0 lda #$96 sta $16a1 lda #$98 sta $16a2Final Fantasy IV Disassembly · battle/win.asm#L152-L243@edb0: clr_ax … @edc7: ldx $a9 cpx #$0005 bcs @edd3 lda $3540,x bne @edef @edd3: ldx $ad lda $1000,x and #$1f beq @edef lda $1003,x and #$c0 bne @edef ldx $a9 inc $405f,x cpx #$0005 bcs @edef inc $ab … lda $3591 ora $3592 ora $3593 beq @ee40 … @ee28: jsl DivXP @ee2c: lda $ad ora $ae ora $af bne @ee36 inc $ad @ee36: ldx $ad stx $3591 … clr_ax lda $3594 ora $3595 beq @ee7a lda #$1f sta $34ca inx @ee7a: lda $3591 ora $3592 beq @ee88 lda #$20 sta $34ca,x inx @ee88: txa beq @ee90 lda #$05 ; battle graphics $05: graphics script jsr ExecBtlGfxFinal Fantasy IV Disassembly · battle/win.asm#L249-L276@ee9b: ldx $a9 lda $405f,x beq @eee1 ldx $ab clc lda $1037,x adc $3591 sta $1037,x … sec lda $1037,x sbc #$7f lda $1038,x sbc #$96 lda $1039,x sbc #$98 bcc @eee1Final Fantasy IV Disassembly · battle/win.asm#L290-L294@eefc: ldx $98
lda $405f,x
bne @ef06
jmp @eff6
@ef06: ldx $a6Final Fantasy IV Disassembly · battle/win.asm#L360-L401sec lda $1037,x sbc $ad lda $1038,x sbc $ae lda $1039,x sbc $af bcc @eff6 … lda $1002,x cmp #$63 bcs @eff6 inc $1002,x jsr LevelUp lda $98 cmp #$05 bcs @eff3 sta $359a lda #$21 sta $34ca lda #$05 ; battle graphics $05: graphics script jsr ExecBtlGfx lda #$23 sta $34ca clr_ax stx $af @efde: ldx $af lda $291c,x cmp #$ff beq @eff3 sta $359a lda #$05 ; battle graphics $05: graphics script jsr ExecBtlGfx inc $af bra @efde @eff3: jmp @ef06Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L2706-L2736ShowMsg_03: … lda f:BattleMsgPtrs,x ; pointers to battle messagesFinal Fantasy IV Disassembly · vanilla/ff4-en-rip.json#L985-L990"battleMessage": { "type": "array", "name": "Battle Message", "range": "0x0FB076-0x0FB33C", "arrayLength": 59, "asmSymbol": "BattleMsg",Final Fantasy II (USA) (Rev 1) · 0x7b076-0x7b33c — the 59 battle messages; $1F, $20, $21 and $23 read, with the rip's character table, "Received {0} GP.", "Received {1} Exp.", "{2} gained a level." and "Learned {4}"Checked by claude-agent, 2026-11-08 — read the gil add and cap, the count of party members sharing the experience, the at-least-1 step, the window's two lines, each character's add and cap, the level check, the level-99 test, the two messages per level and the loop back for another level, and the battle message reader; decoded messages $1F to $24 from the ROM block with the rip's character tableChecked by a second AI model, 2026-11-08.The divisor counts the first five slots whose character exists and is neither dead nor stone, and that the battle marks ($3540 is checked only for those five); the code also pays the five slots after them, which this claim doesn't follow. A slot past the fifth gains levels without a message. The braces stand for the message's variable codes, which the window fills with the amounts, the name and the spell. Message $24 reads "Perished……".
Battle sounds are requested by number, with a stereo position: an attack's animation looks its sound up in a 256-entry table, and the battle code gives it pan $C0 or $40 by bit 7 of the attack, which side each means not followed, or the center, $80, for sounds $31 and $33, reversing the pan in a back attack. The sound program finds the number in a table of 128 entries, each two script pointers, and plays one script on each of the last two of its 8 channels, cutting off whatever they held; 99 of the 128 use both channels, 27 one, and 2, numbers 0 and 79, none (all computed). Menu sounds such as the cursor and the error buzz are a separate set of 16 numbers with their own table. ff4-av/sound-effects
About: Final Fantasy IV Sound effects
Value
{
"attackTable": 256,
"sounds": 128,
"twoChannels": 99,
"oneChannel": 27,
"silent": [
0,
79
],
"channels": [
6,
7
],
"pan": {
"center": 128,
"monsters": 192,
"characters": 64
},
"systemSounds": 16
}btlgfx/magic.asm#L1852-L1884PlayMagicSfx: @f5e7: phx phy lda $f47f cmp #$ff beq _f623 tax lda f:AttackSfx,x ; attack sound effect _f5f5: pha cmp #$31 beq @f5fe cmp #$33 bne @f602 @f5fe: lda #$80 ; pan center bra @f60d @f602: lda $f485 bmi @f60b lda #$c0 ; pan left (toward monsters) bra @f60d @f60b: lda #$40 ; pan right (toward characters) @f60d: sta $f414 ; sound effect pan pla sta $f413 ; sound effect id lda #$ff sta $f415 lda #$02 ; play sound effect sta $f412Final Fantasy IV Disassembly · btlgfx/magic.asm#L186-L189phx
jsr LoadMagicGfx
lda $34c4
sta $f485Final Fantasy IV Disassembly · vanilla/ff4-en-rip.json#L460-L466"attackSoundEffect": { "type": "array", "name": "Attack Sound Effect", "range": "0x0FA350-0x0FA44F", "arrayLength": 256, "itemLength": 1, "asmSymbol": "AttackSfx",Final Fantasy IV Disassembly · btlgfx/summon.asm#L57-L72@f14a: lda $f416 beq @f159 ; branch if no system sound effect … @f159: lda $f412 beq @f17e ; branch if no game sound effect ldx $f412 stx $1e00 ldx $f414 stx $1e02 lda $6cc0 beq @f177 lda $1e02 eor #$ff ; reverse pan for back attackFinal Fantasy IV Disassembly · sound/sound.asm#L168-L225ExecSound: … lda $00 beq @815a cmp #$01 bne @812b jsr PlaySong bra @815a @812b: cmp #$02 bne @8134 jsr PlayGameSfx … @8146: cmp #$10 bcc @8153 cmp #$1f bcs @8153 jsr PlaySystemSfxFinal Fantasy IV Disassembly · sound/sound.asm#L767-L784PlayGameSfx: @85e1: shorta lda $03 sta hAPUIO3 lda $02 sta hAPUIO2 lda $01 sta hAPUIO1 lda $00 sta hAPUIO0Final Fantasy IV Disassembly · notes/ff4-spc.asm#L1927-L2008; [ interrupt $02: play game sound effect ] … 15BE: 8F 00 22 MOV $22, #$00 ; $FD00 (pointers to game sound effects) 15C1: 8F FD 23 MOV $23, #$FD 15C4: E4 F5 MOV A, $F5 ; sound effect id 15C6: 8D 04 MOV Y, #$04 15C8: CF MUL YA … 15CD: E8 C0 MOV A, #$C0 ; channel 6 and 7 15CF: 8D 5C MOV Y, #$5C ; key off … 15D7: CD 1C MOV X, #$1C ; channel 6 … 15E0: F7 22 MOV A, ($22)+Y ; load 1st channel script pointer … 15EB: F7 22 MOV A, ($22)+Y ; load 2nd channel script pointer … 15F3: 5A 28 CMPW YA, $28 15F5: EE POP Y 15F6: F0 04 BEQ $15FC ; branch if both script pointers are equal 15F8: E4 29 MOV A, $29 15FA: D0 08 BNE $1604 ; channel is not usedFinal Fantasy IV Disassembly · notes/ff4-spc.asm#L2259-L2285; pointers to system sound effects 18E0: 1900 1912 1924 1938 194A 0000 0000 0000 18F0: 195C 1983 0000 0000 0000 0000 0000 0000 … ; $11: cursor … ; $12: errorFinal Fantasy IV Disassembly · sound/sound.asm#L42-L110 — the sound program and its data go over as blocks, each a size, an address and the bytes, until a size of 0InitSound: … lda SPCData+1,x xba lda SPCData,x longa tay shorta inx2 lda SPCData,x sta hAPUIO2 lda SPCData+1,x sta hAPUIO3 … @8067: lda SPCData,x sta hAPUIO1 … longa lda SPCData,x inx2 tay beq @80b0Final Fantasy II (USA) (Rev 1) · 0x239cc-0x23bcb — the block the loader puts at $FD00 in the sound chip's memory, $200 bytes, the 128 entriesFinal Fantasy II (USA) (Rev 1) · 0x7a350-0x7a44f — the attack sound table; 152 of the 256 entries are nonzero, 67 different sounds (computed)Checked by claude-agent, 2026-11-08 — read the battle's sound request with its pan choice, where the side bit is copied, the vblank send with the back-attack reversal, the CPU side's command dispatch and its send, the sound program's game-sound interrupt from the lookup to the per-channel test, the system sound table, and the block loader; walked the loader's blocks in the ROM to find the $FD00 block and counted each entry's channels by the interrupt's testRevised after a check by a second AI model, 2026-11-08.A channel is skipped when its pointer equals the next one or its high byte is 0, so an entry whose two pointers match plays on one channel. The table holds sound numbers 0 to 127; the attack table's largest is 114 (computed). Which side each pan value means is the source's comment, not checked against the sound program's pan direction. The battle requests system sounds through the same vblank routine; the CPU side passes $10 to $1E, while the sound program's table has 16 entries, 7 of them filled. The sound scripts themselves aren't followed here, and no source found names the 128 sounds.
Final Fantasy IV and VI both have two ways into a battle: a mosaic on town and dungeon maps, a Mode 7 zoom on the world map, each after one battle sound. VI feeds the mosaic through an HDMA table where IV writes the register directly, and its zoom also dims the screen nearly to black, where IV's leaves the brightness alone. The battle screen then opens differently: IV brightens the whole screen and clears a mosaic from the background while the monsters slide in; VI opens a band from the middle of the battlefield with color math. ff4-av/changes-ff6-transition
Both games build a damage number from digit tiles out of the value's low 14 bits, with no ten-thousands place, white for damage and green for healing, and drive its motion by a table of heights. IV right-aligns four digits and bounces them one after another from left to right; VI centers the number and moves it as a whole. ff4-av/changes-ff6-numbers
Both games kill an ordinary monster the same way at heart: its picture switches to a death palette whose colors lose one step of red, green and blue a frame until black, with the death sound. IV's monsters are background tiles, so it removes them line by line with an HDMA table that switches between two copies of the monster layer; VI's are sprites, and it adds them to the background with color math and then drops them from the shown set. IV's death palette is violet, VI's magenta. In IV's flagged boss battles the boss is wiped from the top down over a shake, where VI runs a death script of its own that the boss's AI asks for. ff4-av/changes-ff6-death
Both games' animation scripts open with two header bytes, run a byte stream in which a byte below $80 shows a frame and ends the step and $FF ends the script, and run their commands at once. IV's commands are a handful of kinds, palette effects and moves, and a hand-written routine for the spell can run the script with steps of its own; VI turns the bytes from $80 up into a command set with a second page behind $80 and runs a script per layer, many threads side by side. ff4-av/changes-ff6-script
Both games request a victory fanfare after a win unless the battle kept the map's music, and both share the experience among the living. IV forces the fanfare for a short list of battles, VI turns it off by a battle flag. IV shows a level-up message for every level gained, each followed by its spells; VI shows one however many levels come, and adds more lines (espers' magic points, Lores, items dropped). ff4-av/changes-ff6-victory
Both sound programs keep a table that gives each sound effect two channel scripts, and both battles queue a sound with a pan and send it once a frame. IV pans by a bit of the attack, three positions in all; VI pans by the animation's target position across the screen. ff4-av/changes-ff6-sound
About: Final Fantasy IV Sound effects
CodeBattle sounds are requested by number, with a stereo position: an attack's animation looks its sound up in a 256-entry table, and the battle code gives it pan $C0 or $40 by bit 7 of the attack, which side each means not followed, or the center, $80, for sounds $31 and $33, reversing the pan in a back attack. The sound program finds the number in a table of 128 entries, each two script pointers, and plays one script on each of the last two of its 8 channels, cutting off whatever they held; 99 of the 128 use both channels, 27 one, and 2, numbers 0 and 79, none (all computed). Menu sounds such as the cursor and the error buzz are a separate set of 16 numbers with their own table. CodeFinal Fantasy VI's sound effects are numbered $00 to $FC in the disassembly's list, 253 names (18 of them a bare number, unnamed), in a driver table of 256 slots that each point at two channel scripts. The effects have eight samples of their own, apart from the instruments'. In battle a sound is queued and sent once a frame from the NMI as sound driver command $18 with the effect's number and a pan value; the animation scripts' sound command pans by the animation's target position across the screen, and a monster death by the last dying monster's, in formation order. CodeChecked by claude-agent, 2026-11-08 — set the two claims side by side; every clause is in one of themRevised after a check by a second AI model, 2026-11-08.Battle $1B7, one of the two the final battle's background is set for, ends with a death of its own. It requests song $42 and plays one of four sounds at each flash, picked by the low two bits of a value this claim doesn't follow. The screen flashes white, yellow and red a frame each, waits 64 frames, flashes yellow and waits 32 more, flashes the three colors again, and then the monster layer shakes by the 8-frame shake pattern while it sinks one pixel every 16 frames, 160 pixels in all, with three more three-color flashes in every 256 frames, 30 in all; the sinking takes about 2,618 frames, some 43.6 seconds (all computed). A last sound plays when it has sunk. ff4-av/final-boss-death
Value: battle 439 · song 66 · sounds 14, 27, 46, 87 · sink 160 · frames per pixel 16 · end sound 32
CodeFinal Fantasy IV Disassembly ·btlgfx/monster_death.asm#L84-L92ldy $1800
cpy #$01b7
bne @e969
lda $38e6
bne @e969
inc $f4a1
jsr LoadMonsterTiles
jsr TfrRightMonsterTiles
jsl _03f591Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3324-L3336_03f57f: @f57f: .byte $0e,$1b,$2e,$57 … _03f583: @f583: lda $13 and #$03 tax lda f:_03f57f,x jsl PlaySfx_farFinal Fantasy IV Disassembly · btlgfx/btlgfx.asm#L3343-L3407_03f591: @f591: lda #$42 sta $38be inc $38bd jsr _03f583 jsr _03f609 ldx #$0040 jsr WaitX_near jsr _03f583 jsl FlashScreenYellow ldx #$0020 jsr WaitX_near jsr _03f609 … @f5c1: jsl WaitFrame_far lda $f133 and #$07 tax lda f:AnimShakeTbl,x tay sty $f406 lda $f133 and #$0f bne @f5e1 ldy $f408 dey sty $f408 @f5e1: lda $f133 cmp #$8c beq @f5f0 cmp #$10 beq @f5f0 cmp #$20 bne @f5f3 @f5f0: jsr _03f609 @f5f3: inc $f133 jsl SetMonsterScroll ldy $f408 cpy #$ff60 bne @f5c1 lda #$20 jsl PlaySfx_far rtl … _03f609: @f609: jsr _03f583 jsl FlashScreenWhite lda #$03 sta $ef87 jsl WaitFrame_far jsl FlashScreenYellow lda #$03 sta $ef87 jsl WaitFrame_far jsl FlashScreenRed lda #$03 sta $ef87Final Fantasy IV Disassembly · btlgfx/btlgfx.asm#L6049-L6078SetMonsterScroll: @ed70: phx longa lda $f408 sta $02 _ed78: lda $f406 sta $00 clr_ax @ed7f: lda $00 ; h-scroll sta $7612,x … lda $02 ; v-scroll sta $7614,xA battle's background is picked on the field before the battle starts. On the world map, bits 0 to 2 of the tile under the party index a six-entry table of backgrounds ($00, $0A, $01, $09, $0F, $05); indexes 6 and 7 would read past the table, and this claim doesn't follow whether any tile gives them. On a town or dungeon map it is the map's own battle background, with bit 6 set for a cave with water when the tile's water bit is set, bit 5 set when the map asks for the alternate background palette, and the magnetized-cave flag cleared unless event switch $E1 is set. Battles $1B7 and $1B8 always get background $10, the final battle's. Code — battles $1B7 and $1B8 get the final backgroundQuake shakes the screen by repeating an 8-frame pattern of offsets, 1, 2, 4, 2, 1, 0, 2 and 2 pixels, written to the battle background's scroll table line by line and to the monsters' or to some characters' positions. The final battle's background is never shaken this way. Code — the shake patternSpells flash the screen with color math: a fixed color, any mix of red, green and blue, is added to BG1 and BG2, the monsters and the battle background, by the color math HDMA table, which covers the top 140 lines (computed) and turns color math off below them, over the menu. Five kinds of flash exist: on 2 frames and off 2 until stopped, a pulse rising from 0 to full (31) and back a step a frame until stopped, a single frame, a rise to a set level that holds, and a fall to 0 that ends it. Scripts start one with command $88; routines call it by color. The routine the source names Black Hole darkens the background and flashes white for 32 frames; Big Bang flashes green, red, yellow, blue and white for 8 frames each, four times over. Code — flash type 3 lasts one frameChecked by claude-agent, 2026-11-08 — read the death branch's battle test, the four-sound picker, the whole sequence with its flashes, shake and sink loop, the three-color flash, and the monster scroll writer with its horizontal and vertical columnsRevised after a check by a second AI model, 2026-11-08.The vertical scroll falls from 0 to −160 ($FF60), and a lower scroll value moves the layer down, so the monster sinks. The loop's one-byte counter is stored from the accumulator, which holds 3 from the three-color flash run before it, so the first step comes on the 14th pass and the 160th on pass 2,558 (14 + 159 × 16). The flashes in the loop come at counter values $10, $20 and $8C, ten times each as the counter wraps, and each flash adds the two frames it waits, 60 in all; 2,558 + 60 = 2,618 frames (all computed). A flag at $38E6 skips the sequence; it isn't followed.