At a glance

Asks
Command flow FUN_801D0748, state byte ctx[+0x06] (command flow)
Records
The acting seat's action bytes: category +0x1DE, spell id +0x1DF, target +0x1DD (action queue)
Plays out
Action state machine FUN_801E295C, state byte ctx[+0x07] (action state machine)
Choreographs
One of 64 cast modules, PROT 0903..=0966, paged into slot B at 0x801F69D8, phase byte ctx[+0x279] (cast modules)
Ends
The Done band: the HP-bar settle gate in 0x51, then the end-of-action gate 0x5A (exit gates)
Port
engine-battle-menus (command flow), engine-battle-vm::battle_action (one enum value per retail state byte), engine-cast (module ticks, cameras, the cast-arm kernels)

The path

Three bands leave the cast's opening states. Which one a cast takes is decided by the spell, not by who casts it: a party Seru spell carries the summon flag and enters the summon band, a capture-class spell (its record's first byte is 'c') enters the capture band, and every other cast - most monster spells - runs the plain effect-driver band. The two module bands are the only states that hand control to code outside the battle overlay.

command flow ctx[+0x06] Ring: magic armstate 0x28 Magic windowstate 0x46 Target sub-cursor0x64..0x67 Commit + confirmwrites the bytes +0x1DE = 2 (magic), +0x1DF = spell id, +0x1DD = target action state machine ctx[+0x07] 0x0C Action seeddispatch on +0x1DE 0x28 Cast beginface target, debit MP 0x29 Pre-cast waitparty cast trigger, first clip 0x2A..0x2Eeffect-driver castclip chain, hit frames 0x32..0x38 Summon bandinvoke, fade in, freeze,sustain, return, verify 0x6E..0x71 Capture bandload, fade, run,finalize no summon flag +0x1E0 == 9 'c' spell, from 0x28 Cast module in slot B 0x801F69D8 (one of PROT 0903..0966) phase ctx[+0x279] · camera · spawns · CD-XA voice · damage rolls ticked every frame ticked every frame 0x50 Done cleanuprevive, re-order, pose 0x51 Fade-downHP-bar settle gate 0x5A End-of-action gatenext actor, or round 0xFF returns 0
Only the summon and capture bands leave the battle overlay: the module ticks every frame and the band moves on when it returns zero. A Seru absorbed by the killing blow detours 0x51 through the 0x52 banner hold. Not drawn: the applier band 0x3C..0x40, which items and the low elemental spell records take from the seed, and Spirit (0x46..0x48).

1. Asking: the command flow

The round prompt and the D-pad command ring are one state byte, ctx[+0x06]. Right on the ring is the Ra-Seru magic arm, which opens the magic window; the window commits through its own target sub-cursor, and the commit log at the screen's edge records the choice until the last party member has one and the player confirms. Nothing has been cast at this point. What the flow leaves behind is three bytes on the acting seat: the action category +0x1DE (2 for magic), the spell id +0x1DF and the target +0x1DD, where 8 and 9 are group codes (the whole party, every monster).

A monster reaches the same three bytes from its AI pick instead of a menu, which is why everything below is shared: the action state machine never asks who filled the bytes in.

2. Opening the cast: 0x0C, 0x28, 0x29

  • 0x0C, the seed, dispatches on the category. A magic action goes to 0x28, except a low-class, low-id spell record (class byte below 0x14 and id below 0x65), which takes the applier band 0x3C and is charged there just the same; the player Seru block 0x81..=0x8B always fails the id test. An attack goes to the strike band, an item to the applier band, Spirit and Run to their own.
  • 0x28, cast begin, turns the caster to face its target and sets a 0x14-frame timer. A group target degenerates onto the last one standing (the census's sole-survivor latches). A monster caster raises its spell-name label. MP comes off here, from the spell record's +3 byte, halved or quartered by the MP-saving passives. A capture-class spell leaves for 0x6E at once and queues its module's load.
  • 0x29, pre-cast, counts the timer down, then runs the party cast trigger, which stages the caster's animation stream (not the outcome). A summon flag (+0x1E0 == 9) sends it to 0x32, and this is where a party summon's module is paged: loader argument id - 0x79, so Gimard (0x81) is PROT 0903.

3. The three bands

Effect-driver cast, 0x2A..0x2E

The ordinary monster spell. The action machine walks (clip, shot) pairs off the queue, runs the cast-effect driver until the clip latch counts down, lands hits on the clip's hit frames, waits for any live effect child and snaps a pitched camera back. Its effects are effect-VM spawns resolved from UI-element ids; nothing is paged from the disc.

Summon band, 0x32..0x38

  1. 0x32 invokeWaits for the drive while the framing keeps walking, then stages clip 9 on the caster.
  2. 0x33 fade inThe summon close-up camera; on the clip's first effect record, the white flash-in and cue 0x63.
  3. 0x34 freezeHides the party and the living monsters and calls the summon dispatch FUN_801F1ED4, whose phase 0 seats the creature in seat 7.
  4. 0x35 sustainDrains a band word, ducks the music to 75% (50% for the high spells), and calls the module every frame. It re-reads the word after the call, so a module that keeps re-seeding it holds the band: Theeder, Nighto and Zenoir leave on an arm of their own, not after the word's 0x78 frames.
  5. 0x36 returnHolds while the module returns busy, un-hides the actors, and runs the Seru-magic level check.
  6. 0x37, 0x38Wait for every actor to settle, write the fade sentinel, kill the fade primitive.

Capture band, 0x6E..0x71

Seru capture, the capture item and the boss cinematic specials. 0x6E waits for the drive and pages the module, 0x6F ducks the audio and waits for the load, 0x70 ticks the module every frame until it returns zero, and 0x71 waits for the targeted seat to settle. The dispatch is FUN_801F2160, its row the spell record's sub-id.

4. Inside the module

A cast module is a library with two entry points, a tick and a stager, both named by tables in the battle overlay. The tick is a dispatcher over the phase byte ctx[+0x279]: its arms stage clips, spawn effect parts through the stager (move-VM opcode 0x20), move the camera, roll damage and write stats, and it returns busy until a terminal arm clears the result. A phase past the last arm returns busy forever, which is why a port that steps one arm too far parks the battle.

  • The camera is the module's. No framing case of the action machine runs inside the bands; the module builds shots and drifts on the battle camera's own pose globals.
  • The voice waits on the drive. A spoken line is CD-XA streamed off the disc, so a seek arm points the drive at it and a poll arm waits for the seek before the head cue starts the line - after the seek the battle had already queued for the caster's fanfare.
  • Damage is baked in. Each hit calls a roll wrapper with a power constant from the module image, then clamps against the victim's HP and accumulates into the damage-popup word as it writes HP. A cast never reads the move-power table.

The phase table, the clamp shapes, the seat-0 hardcode in the boss cinematics and the colour wash are on cast modules.

5. Done: 0x50, 0x51, 0x5A

Every band ends in the same three states. 0x50 runs the final Heal revive, re-orders the round and stands the caster in its end pose. 0x51 restores the audio level and waits for every HP readout to reach the HP it shows, with no timeout: the module's paired write of HP and the popup word is what lets the readout close, and a write that skips the pair is one of the two retail softlock classes (exit gates). A killing blow that absorbed a Seru holds the “acquired the power of” banner in 0x52. 0x5A clears the per-actor flags, tests for a wipe, and moves the turn cursor to the next actor or to the round boundary 0xFF, which hands the battle back to the command flow.

How the port is held to it

A cast lasts hundreds of vsyncs and a save state holds one, so a cast is measured as a film: the cast-sequence oracle records one cast on both sides and pairs frames on the action state, switching to the module phase ctx[+0x279] inside the module bands - the same two clocks this page follows. Its sound is scored against the emulator's own output by the real-PCM cast channel, and the routines it calls without a module - the item applier, the MP-cost leaf, the melee resolver - by the retail kernel differential. Measuring against retail puts the instruments side by side.

See also