A cast, end to end
One Seru spell crosses four pieces of the battle code before its damage lands: the command flow that asks what to do, the action queue that records the answer, the action state machine that plays it out, and a small cast module paged off the disc for the summon itself. Each has its own page. This one follows a single cast through all four, so the hand-offs between them are in one place.
At a glance
- Asks
- Command flow
FUN_801D0748, state bytectx[+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 bytectx[+0x07](action state machine) - Choreographs
- One of 64 cast modules, PROT
0903..=0966, paged into slot B at0x801F69D8, phase bytectx[+0x279](cast modules) - Ends
- The Done band: the HP-bar settle gate in
0x51, then the end-of-action gate0x5A(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.
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 to0x28, except a low-class, low-id spell record (class byte below0x14and id below0x65), which takes the applier band0x3Cand is charged there just the same; the player Seru block0x81..=0x8Balways 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 a0x14-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+3byte, halved or quartered by the MP-saving passives. A capture-class spell leaves for0x6Eat 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 to0x32, and this is where a party summon's module is paged: loader argumentid - 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
0x32invokeWaits for the drive while the framing keeps walking, then stages clip9on the caster.0x33fade inThe summon close-up camera; on the clip's first effect record, the white flash-in and cue0x63.0x34freezeHides the party and the living monsters and calls the summon dispatchFUN_801F1ED4, whose phase 0 seats the creature in seat 7.0x35sustainDrains 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's0x78frames.0x36returnHolds while the module returns busy, un-hides the actors, and runs the Seru-magic level check.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.