How to read this page
The causes are read from the game's code and the disc's data. The key numbers are also measured in a running game (PCSX-Redux, retail USA disc): the Mystic Shield hit counts and damage, Lapis's stats, the Lapis Wave MP wipe, the MP check and Jedo's kill. None of those measurements is a played fight at the boss; how the captures were made says exactly what was forced or written by hand, and every result below is labelled where one of those applies. Where the page still predicts something, it says so. All addresses are USA SCUS_942.54 runtime addresses.

Shared vocabulary

Each fighter in a battle has an actor record. Pointers to them sit in a table at 0x801C9370: party in slots 0-2, enemies in slots 3-6, a summoned creature in slot 7. The battle's shared state is the context at *0x8007BD24. The battle logic is PROT entry 0898 (loaded at 0x801CE818); each spell and summon is its own small module, PROT entries 0903-0966, all loaded at 0x801F69D8 (see battle and battle action).

Actor offsetField
+0x14C / +0x14EHP, current / maximum
+0x150 / +0x152MP, current / maximum
+0x1D9 / +0x1DAAnimation clip now playing / clip staged to play next
+0x1DE / +0x1DFChosen action category / action id
+0x1EF..+0x1F2Reaction clips: flinch, alternate flinch, knockdown, get-up

How the captures were made

No save in front of Lapis or Cort was available, so the fights were built from a nearby one. Each measurement on this page has this pedigree:

  • Forced formation. Every fight starts from a retail save state at the Queen Bee fight in Rim Elm - a scripted battle about to begin, with Vahn alone. At the instruction that reads the first byte of that battle's formation row (0x801DA600) the row is overwritten in memory: 01 00 00 01 B4 for Cort, and 00 00 00 01 A7 00 00 00 for Lapis, the exact bytes of the disc's own row. Everything after that read is the game's code on the game's data: the flag, the monster load, the stat boost, the boss's turn logic, Cort casting and losing his own Mystic Shield. What is not the real fight: the party (Vahn alone, at opening-chapter stats), the stage (Rim Elm) and the frame load, which is lighter than with three characters on screen.
  • Injected casts. Vahn owns none of these spells. When his turn is set up, his queued action is rewritten to the spell; the game then loads and runs the spell's module itself.
  • Poked values. Vahn's HP is held at 9000 so he survives, and his MP is topped up. For the summon measurements his magic level for the spell is set to 1 - at the default the damage roll saturates at 9999 and hides the shield's halving. For the "no shield" baseline Cort's HP is written to 25000 and the game breaks the shield itself on the next frame. For the Lapis MP check, Vahn's current MP is written to 0 once. "Captured on a poked state" below means one of these.
  • Which runs are cited. A first batch of these runs held Vahn's HP with a write that began before the battle existed, and so also wrote field memory for the first few seconds. Every experiment was rerun with that write confined to the battle. The reruns gave the same counts, and they are the ones cited; the single exception is marked where it is used.

Lapis Wave checks current MP

Reported as
Lapis Wave tests the party's current MP where it was meant to test maximum MP, so Lapis can be killed outright with magic the party is not supposed to have (community report)
Cause
Lapis decides whether to cast by counting party members with current MP above zero; leaving a battle saves current MP only, so a returning party reads 0 of its full maximum and is never targeted again
Where
Lapis's turn logic at 0x801EBC38 in PROT entry 0898; the wave itself at 0x801F6A04 in PROT entry 0943
Affects
The Lapis fight only
Status
explained - from disassembly and disc data; the wave, the check and the Jedo kill each captured in a forced fight, the flee-and-return route not played through

Lapis Wave is not a damage attack. It sets both the current and the maximum MP of every party member to zero for the rest of that battle. That is why MP items do nothing afterwards: there is no maximum left to fill. In the captured fight Lapis casts it before the party can enter a command, and Vahn's MP goes from 27 / 27 to 0 / 0.

The check the report describes is in Lapis's turn logic, not in the attack. Each time Lapis acts it counts the party members whose current MP is above zero and casts the wave only if it finds one. A party at zero current MP is left alone, whatever its maximum is: with Vahn's current MP written to 0 and his maximum left at 27, Lapis attacked physically on both of its turns and never cast the wave (captured on a poked state).

Leaving a battle writes each member's current MP back to their permanent record and never writes the maximum. So a party that takes one wave and flees walks away at 0 MP with its real maximum untouched. On the return visit Lapis sees nobody with MP and never casts the wave; an MP item works again; and a caster who gets a turn before Lapis's next one can cast.

The spell that ends the fight is Jedo. Its module has two outcomes: against a fight flagged as scripted it deals normal rolled damage, against an unflagged one it sets every living enemy's HP to zero with no roll. The Lapis fight is unflagged (next section), so the kill is certain: an injected Jedo took Lapis from 64800 HP to 0 in a single write. A check on maximum MP would have fired on the returning party and closed the route; whether the designers meant that field is an inference, not something the code states.

These pieces were captured one at a time. The wave, the check at zero MP and the Jedo kill are each observed; the flee, the return visit and the MP item between them are read from the code and have not been played through as one route.

First visit MP 27 / 27 +0x150 / +0x152 +0x150 != 0 check fires Lapis Wave both fields zeroed MP 0 / 0 items do nothing flee: only current MP is saved, the record keeps maximum 27 Return visit MP 0 / 27 +0x150 / +0x152 +0x150 == 0 check does not fire MP item maximum is still 27 Jedo enemy HP set to 0 The check reads +0x150 (current). Reading +0x152 (maximum) would fire on the return visit too.
What Lapis's check sees on each visit, with the numbers of the captured fight (Vahn alone, 27 MP). Captured: the top row, the check not firing at 0 / 27 (current MP written by hand) and the Jedo kill (injected cast). From the code: the flee and the MP item.
  1. Forced ambushBattle setup compares the first enemy id with 0xA7 (Lapis) and forces a back attack, so Lapis acts before the party can enter a command. Captured: the first action of the fight is Lapis's. 0x80051FC4
  2. The checkLapis's turn counts party actors with +0x150 (current MP) non-zero. None: an ordinary attack. One or more, and Lapis holds 255 MP: Lapis Wave. 0x801EBC68
  3. The waveFor every party seat: current MP and maximum MP both written to zero. Captured: 27 / 27 to 0 / 0, and the cast costs Lapis 255 of its 4000 MP. 0x801F6D08, 0x801F6D1C
  4. The exitFleeing copies current MP to the character record. Maximum MP is not copied. 0x801E7C78
  5. The returnThe battle reloads MP from the record: 0 current, full maximum. The check finds nobody and never fires.
  6. The killJedo on an unflagged fight sets each living enemy's HP to 0. Captured: 64800 to 0. 0x801F8814
Register-level walkthrough

Ambush, FUN_80051D84 in SCUS_942.54:

80051fc0  lbu  v1,-0x42f4(v1)     ; first formation id, 0x8007BD0C
80051fc4  li   v0,0xa7
80051fc8  bne  v1,v0,0x80052068
80051fd0  li   v0,0x1
80051fe0  sb   v0,0x290(v1)       ; ctx[+0x290] = 1, back attack

The check, FUN_801E9FD4 in PROT entry 0898. The routine switches on the enemy id through a jump table at 0x801CF1CC (index id - 4); the entry at 0x801CF458 sends Lapis to 0x801EBC38:

801ebc60  lw    v0,0x0(v1)        ; party actor i
801ebc68  lhu   v0,0x150(v0)      ; current MP
801ebc70  beq   v0,zero,<next>
801ebc78  addiu s1,s1,0x1         ; count members with MP
801ebc90  beq   v0,zero,0x801ebdac  ; nobody: generic pick
801ebc98  lhu   v0,0x150(s4)      ; Lapis's own MP
801ebca0  sltiu v0,v0,0xff
801ebca4  bne   v0,zero,0x801ebdac  ; needs 255
801ebcac  sb    v0,0x1de(s4)      ; category 2, magic
801ebcb4  li    v0,0xb5           ; Lapis Wave -> +0x1DF

The wave, FUN_801F6A04 in PROT entry 0943, third arm, once per party seat. The module calls no damage routine anywhere:

801f6cf4  lhu v0,0x150(v1)
801f6cfc  sh  v0,0x178(v1)        ; MP readout drains from the old value
801f6d08  sh  zero,0x150(v0)      ; current MP = 0
801f6d1c  sh  zero,0x152(v1)      ; maximum MP = 0

The exit, FUN_801E791C in PROT entry 0898: actor +0x150 is stored to the character record's current MP (+0x10A) at 0x801E7C78..0x801E7C80. The routine has no store of +0x152.

Jedo, FUN_801F6A58 in PROT entry 0931:

801f87d4  lbu v0,0x287(v1)        ; scripted-fight flag
801f87dc  bne v0,zero,0x801f8844
801f8804  lhu v0,0x14c(a0)        ; each enemy seat 3..6
801f8814  sh  zero,0x14c(a0)      ; HP = 0
801f8820  sb  a2,0x21c(v0)        ; defeat fade

Nighto (PROT entry 0907) also reaches its kill on this fight, as a roll: the resist test is skipped when the flag is clear (0x801F6BF0), the resist roll is rand % (0x13 - magic_level) < 9 (0x801F6C28), and the kill is rand % 8 == 0 (0x801F6B50, store at 0x801F7E58). At magic level 9 that is about 11% per cast.

Addresses

AddressImageWhatEvidence
0x80051FC0..0x80051FE0SCUS_942.54, FUN_80051D84Enemy id 0xA7 forces the back attack, ctx[+0x290] = 1disassembly, capture
0x801CF1CC, word at 0x801CF458PROT 0898Turn-logic jump table; Lapis's arm is 0x801EBC38disc data
0x801EBC38..0x801EBCB4PROT 0898, FUN_801E9FD4Count party +0x150, require own MP of 255, queue action 0xB5disassembly, capture
0x801F6A04PROT 0943Lapis Wave bodydisassembly
0x801F6CFC, 0x801F6D08, 0x801F6D1CPROT 0943Stores to +0x178, +0x150, +0x152 for each party seatdisassembly, capture
0x801E7C78..0x801E7C80PROT 0898, FUN_801E791CFlee: current MP saved to record +0x10Adisassembly
0x80084708 + n * 0x414RAMCharacter record; +0x108 maximum MP, +0x10A current MPrepo doc
0x801F87D4..0x801F8820PROT 0931, FUN_801F6A58Jedo, flag clear: every living enemy's HP set to 0disassembly, capture
0x801F833C, 0x801F8438PROT 0931Jedo, flag set: rolled damage insteaddisassembly
0x801F6BF0, 0x801F6C28, 0x801F6B50, 0x801F7E58PROT 0907, FUN_801F69E8Nighto: resist gate, resist roll, kill roll, kill storedisassembly
Spell record 0xB5SCUS_942.54 spell tableLapis Wave: cost 255, targets the whole partydisc data
Monster record 167, +0x20PROT 0867Lapis's resist byte is 1disc data

Scope

  • The code does not require the escape. Any party that walks in with every member at zero current MP is never waved. Captured on a poked state: with Vahn's current MP written to 0 before Lapis's first pick and the maximum left at 27, Lapis opened with a physical attack, attacked physically again on its next turn and kept all 4000 MP. Those two turns are all the capture covers. The reported route is flee and return, which works because the wave itself produces the zero.
  • One member holding a single point of MP re-arms the wave for everyone. The count has no "is this member alive" test, so a fallen member's MP counts.
  • Lapis needs 255 MP of its own for each cast.
  • That MP items do nothing at zero maximum is a community report consistent with the code; the item routine's cap was not re-read for this page.

Verifying it

Observed in the forced Lapis fight (forced formation, Vahn alone with 27 MP; see how the captures were made):

  • Lapis acts first, with no command prompt before it, and the action is Lapis Wave: the cast loads PROT entry 0943 and costs Lapis 255 MP.
  • Vahn's +0x150 and +0x152 both go from 27 to 0 during the wave. On its next turn, with the party at 0 / 0, Lapis attacks physically.
  • With +0x150 written to 0 and +0x152 left at 27, no wave is cast. The maximum being non-zero when the check runs is what tells the two fields apart.
  • Jedo, injected: ctx[+0x287] reads 0 at 0x801F87DC, the write at 0x801F8814 zeroes Lapis's 64800 HP, and the damage roll at 0x801F8438 never runs.

The run that shows the wave also carries two unrelated one-byte writes to Vahn's character record (a summon level stash and his Ra-Seru item byte), made for a different question.

Still to check in a played game:

  • After a real escape, read the character record: +0x108 unchanged, +0x10A zero.
  • On the return visit, break at 0x801EBC68: +0x150 reads 0, +0x152 the real maximum, and the branch at 0x801EBC90 is taken.

Open questions

  • Whether the check was meant to read maximum MP. The code is equally consistent with a check written for the in-battle case, where both fields are zero after a wave.
  • Whether the restored caster reliably acts before Lapis (speed 451) was not simulated.
  • The route as a whole (wave, flee, return, MP item, Jedo) has not been played through in one capture; the flee's save of current MP and the MP item rest on the code alone.

Lapis can be escaped from

Reported as
The party can run from Lapis, unlike other bosses (community report)
Cause
"No escape" is one byte at the start of the fight's formation row, and Lapis's row has it at zero
Where
Mt. Dhini scene data (PROT entry 0383), formation row 11; read by the escape roll at 0x801E7B14
Affects
Lapis, plus the Rim Elm opening fights and Gold Face at the Shadow Gate
Status
explained - from disassembly and disc data; the clear flag and Lapis's stats captured in a forced fight, the Run command itself not driven

Each scene carries a table of enemy formations, eight bytes a row. The first byte of a row says "this is a scripted fight". When it is non-zero the game sets a flag for the battle, and the escape roll fails whenever that flag is set. Nothing else makes a fight inescapable: not the script command that starts it, and nothing in the monster's own data.

Lapis's row has zero there. The fight is started by a script command exactly like a boss fight, yet the battle treats it as an ordinary encounter, so Run, the escape accessories and the Spikefish Flute all work. A fight started from that row's exact bytes reads the flag as zero from start to finish; choosing Run was not part of that capture.

Lapis: Mt. Dhini (deene), row 11, at offset 0x38C3 of the decoded scene data.

For comparison, Cort: scene chitei2, row 13. The only difference that matters is the first byte.

The same flag feeds every other boss protection in the battle code, so the Lapis fight loses those as well. This is what lets the instant-kill summons of the previous section land.

What reads the flagEffect on the Lapis fightEvidence
Escape roll, 0x801E7B14The party can fleedisassembly
Jedo's kill arm, 0x801F87D4Certain killdisassembly, capture
Nighto's resist gate, 0x801F6BF0Kill and confuse rolls run although Lapis's record has the resist byte setdisassembly
Zenoir's and Aluru's resist gatesSame gate as Nightorepo doc
Seru-magic stat debuffs, FUN_801F3D3CLand on every hit, with no suppression rollrepo doc
Enemy stat boost at battle load, FUN_80054CB0The random-encounter profile is installed instead of the boss onedisassembly, capture
Spikefish Flute, PROT 0925Its flee outcome runsrepo doc

The stat-boost row means Lapis is weaker in attack and defence, and stronger in magic, than stat lists say:

ATKUpper defenceLower defenceINT
Lapis's record on the disc680975987999
Installed in the Lapis fight (flag clear)680170517261248
What a flagged boss fight would install850195019741123

The middle row is read from Lapis's actor in the forced fight, along with HP 64800 and MP 4000. The same rule shows on unmodified save states of another unflagged scripted fight, Tetsu's opening spar: the flag reads 0 and his record's 24 / 16 / 12 / 99 is installed as 24 / 28 / 21 / 123, while flagged Gaza and evolved Cort states read 4 and carry the boss boost (see battle formulas for the two profiles).

Published stat lists, this project's own curated bestiary included, show 850 / 1950 / 1974 / 1123 for Lapis. That is the boss boost applied uniformly to every enemy - a derived column, not a reading of this fight.

Register-level walkthrough

Where the flag comes from, FUN_801DA51C in PROT entry 0897, at battle launch:

801da5f8  lw  v0,0x94(s1)         ; the formation row
801da600  lbu v0,0x0(v0)          ; its first byte
801da608  beq v0,zero,0x801da620
801da610  lbu v0,-0x42a0(v1)      ; per-battle flags, 0x8007BD60
801da618  ori v0,v0,0x80
801da61c  sb  v0,-0x42a0(v1)

The latch, FUN_800513F0 in SCUS_942.54, 0x80051430..0x80051444: ctx[+0x287] = (flags >> 5) & 4, stored before any monster is loaded. The same routine then clears bit 0x80 of 0x8007BD60 again (0x80051A14), so during a fight only ctx[+0x287] tells a flagged fight from an unflagged one.

The escape roll, FUN_801E791C in PROT entry 0898:

801e7afc  sltu v0,s2,s3           ; party roll < enemy roll
801e7b00  bne  v0,zero,0x801e7b24 ; fail
801e7b14  lbu  v0,0x287(v0)
801e7b1c  beq  v0,zero,0x801e7b40 ; flag clear: success path

The script command: in the Mt. Dhini scene data the fight starts with 3E FF 0B at offset 0x3840 - "scripted battle, formation row 11". The command itself writes no flag (see encounter records).

The stat boost, FUN_80054CB0 in SCUS_942.54 (s4 is the monster record):

80055234  lbu  v0,0x287(v0)
8005523c  beq  v0,zero,0x800552ac ; flag clear: second block
80055248  lhu  v0,0x12(s4)        ; flagged: ATK += ATK >> 2
80055264  lhu  v1,0x14(s4)        ;          UDF += UDF
80055280  lhu  v1,0x16(s4)        ;          LDF += LDF
8005529c  lhu  v0,0x18(s4)        ;          INT += INT >> 3
800552ac  lhu  v1,0x14(s4)        ; clear:   UDF += (UDF >> 1) + (UDF >> 2)
800552d0  lhu  v1,0x16(s4)        ;          LDF += (LDF >> 1) + (LDF >> 2)
800552f8  lhu  v0,0x18(s4)        ;          INT += INT >> 2

The clear block leaves ATK alone. Its two shifts round down separately, so 975 becomes 975 + 487 + 243 = 1705, one less than a straight "times 7/4" would give.

Addresses

AddressImageWhatEvidence
0x801DA5F8..0x801DA61CPROT 0897, FUN_801DA51CRow byte 0 non-zero raises bit 0x80 of 0x8007BD60disassembly
0x80051430..0x80051444SCUS_942.54, FUN_800513F0ctx[+0x287] = (flags >> 5) & 4disassembly, capture
0x80051A14SCUS_942.54, FUN_800513F0Bit 0x80 of 0x8007BD60 cleared again after the latchdisassembly
0x80055234..0x80055308SCUS_942.54, FUN_80054CB0Stat boost: boss block, or the clear block at 0x800552ACdisassembly, capture
0x801E7AFC..0x801E7B24PROT 0898, FUN_801E791CEscape roll: flag set forces the failure armdisassembly
Scene data offset 0x3840PROT 0383 (deene)3E FF 0B: scripted battle on formation row 11disc data
Scene data offset 0x38C3PROT 0383 (deene)Row 11: 00 00 00 01 A7 00 00 00disc data
Monster record 167PROT 0867Lapis before the boost: ATK 680, upper defence 975, lower defence 987, INT 999disc data

Scope: every scripted fight with the byte at zero

Joining every scripted-battle command on the disc to the formation row it names gives this complete list:

SceneCommandRowEnemies
deene (Mt. Dhini)3E FF 0B11Lapis
town01 (Rim Elm)3E FF 044Tetsu, the opening spar
town0b, town0c (Rim Elm)3E FF 033Queen Bee and three Killer Bees
town0b (Rim Elm)3E 00 022One Gimard
stone (Shadow Gate)3E 00 033Gold Face

Every other scripted fight names a flagged row: the Piura pair, Zeto, every Songi, the Viguro pair, every Caruban, Dohati, every Berserker, the Kemaro pair, Van Saryu, Xain, the late Tetsu, both Gaza fights, the three Delilas siblings, Koru, Rogue, Jette and both Cort forms. Lapis is the only late-game boss on the unflagged side.

Verifying it

Observed in a fight forced from the disc row's bytes (see how the captures were made):

  • ctx[+0x287] reads 0 for the whole fight; a flagged boss reads 4. The flags byte 0x8007BD60 is no use for this check: its bit 0x80 is cleared during setup in every fight.
  • Lapis's actor at *(0x801C9370 + 0xC) reads 680 / 1705 / 1726 / 1248 at +0x158 / +0x15C / +0x160 / +0x168.

Still to check: choose Run in the Lapis fight and break at 0x801E7B14. The prediction is ctx[+0x287] = 0 and, on a winning roll, the success path at 0x801E7B40.

Open questions

  • The escape itself was not driven in a capture; that it succeeds rests on the code of the roll and on the flag reading 0.
  • The fight was started by forcing the formation row, not by reaching Lapis on Mt. Dhini. Everything that depends on the row and on Lapis's own data is the real game; the party and the stage are not.

Mushura and Theeder against Mystic Shield

Reported as
Mushura and Theeder do excessive damage to Cort while his Mystic Shield is up; Crazy Driver racks up hits on the shield, the only place in the game where that happens (community report)
Cause
Both summons rely on the target's flinch animation to stop repeat hits, and Mystic Shield removes Cort's flinch. Mushura then hits on every tick it touches him; Theeder hits twice, which only cancels the shield's halving
Where
Hit tests at 0x801F7870 (Mushura, PROT entry 0915) and 0x801F7CF0 (Theeder, PROT entry 0904); shield setup at 0x801F7690 (PROT entry 0940)
Affects
Cort's first form for his first 25000 HP
Status
explained - mechanism from disassembly; hit counts and damage measured in a forced Cort fight with injected casts

The two summons turn out to be different cases. Mushura's Crazy Driver really does deal excess damage to shielded Cort: measured, 28 run-over hits where an unshielded cast lands 4, and 2252 damage against 784 - about 2.9 times an unshielded cast. Theeder only slips past the shield: its beam lands 2 hits where an unshielded cast lands 1, each at half strength, for 284 damage against 282. The shield is cancelled out, not exceeded.

Element, overflow and a special formula play no part. Every summon uses the same damage roll, and the shield halves every hit on Cort - these two included.

The difference is how these two decide when to hit. Most summons hit on a fixed beat of their animation. Mushura and Theeder are sweeps: on every tick they test each enemy for contact with the rolling body or the swinging beam, and hit whatever is touching. The one thing that stops them from hitting the same enemy again is a test that the enemy is standing idle. A hit makes the enemy flinch or fall, and an enemy in the middle of that reaction is skipped.

Mystic Shield wipes Cort's four reaction animations so that he stands unmoved behind it. Animation number zero is the idle stance. So a hit now tells Cort to play "idle", he is still idle at the next contact, and the sweep hits him again. Mushura rolls back and forth across him and stays in contact for many ticks. Theeder's beam crosses him exactly twice.

Mule, a Ra-Seru summon built the same way, has an extra line at the same spot: if Mystic Shield is up, skip the hit. Mushura and Theeder lack it. Three summons share the pattern and one of them is guarded.

HP taken from Cort by one cast at magic level 1. Bars are to scale.TheederNo shield2821 hitShield, clips restored1411 hitShield as shipped2842 hitsMushuraNo shield7844 + 1 hitsShield, clips restored3904 + 1 hitsShield as shipped225228 + 1 hits0500100015002000HP
One cast each on Cort, captured in a forced Cort fight with injected casts, Vahn alone, magic level set to 1 (pedigree). "No shield" is Cort after the game has broken the shield at 25000 HP. "Shield, clips restored" is a control: the shield is still up and Cort's four reaction clips are written back by hand, which shows the shield working as designed. Mushura's "+ 1" is its opening strike before the run-over.
CastCort's stateHitsRoll per hitHP lost
Theedershield broken by the game at 25000 HP1282282
Theedershield up2142, 142284
Theedershield up, reaction clips written back to 03 02 04 051141141
Mushurashield broken by the game at 25000 HP4 run-over + 1 opening610 to 639, then quartered784
Mushurashield up28 run-over + 1 opening304 to 321, then quartered2252
Mushurashield up, reaction clips written back to 03 02 04 054 run-over + 1 opening304 to 320, then quartered390
  • Theeder deals 284 where the shield should allow 141: twice the intended figure, and level with an unshielded cast.
  • Mushura deals 2252 where the shield should allow 390: 5.8 times the intended figure, and 2.9 times an unshielded cast. Under the shield it touched Cort on 28 ticks, in five contact windows of 4, 2, 9, 8 and 5 ticks, and hit on every one. Without the shield it touched him on 38 ticks and hit 4 times, once per window.
  • The control rows settle the cause. With the shield still up and only the reaction clips put back, both casts return to their normal hit counts and keep the halved rolls. The extra hits come from the blanked clips alone. The same shows on an ordinary enemy with no shield anywhere: a Gobu Gobu with its HP raised and its reaction clips written to zero took 2 Theeder hits where it normally takes 1, and 12 to 24 Mushura hits (until it died) where it normally takes 4.
Where Theeder's beam points on each tick of the sweep, relative to the target.+0x100on target-0x100tick 8: hits either waytick 16: hits only under the shieldtick 0phase 0tick 4phase 0x400tick 8phase 0x800tick 12phase 0xC00tick 16phase 0x1000hit cone, 0x30 either side
To scale, from the sweep's own arithmetic and the captured phase values: at the measured frame timing the sweep is 16 ticks over 64 frames. The target stands on the beam's centre line and is inside the hit cone only at tick 8 and tick 16. Unshielded Cort is hit at tick 8 and is on his knockdown clip at tick 16; shielded Cort is still "idle" and is hit at both.
How far the numbers carry
Each figure is one cast in a forced fight with a one-character party. Theeder's count of two depends only on the sweep's step: the same arithmetic gives two cone hits for frame steps of 2, 3 and 4, one at 5 and six at 1 (only 4 was observed; the rest is calculation). Mushura's count is a count of contact ticks, so it moves with the frame rate: the measured cast ran at a frame step of 3 to 4, and the real fight, with three characters on screen, runs heavier frames and should see fewer contact ticks. The 28-to-4 ratio is a measurement of that run, not a constant.
  1. Shield goes upOn his first action Cort casts Mystic Shield. The module stores its effect handle in 0x8007BD84 and zeroes Cort's reaction clips +0x1EF..+0x1F2. 0x801F7678, 0x801F7690
  2. Every hit is halvedWhile the handle is set, the damage routine halves any hit on an enemy. Measured: Theeder rolls 282 without the shield and 142 with it. 0x801DDB98
  3. The sweep tests for idleMushura and Theeder skip an enemy whose playing clip +0x1D9 is not zero. 0x801F7870, 0x801F7CF0
  4. The hit stages a reactionAfter the damage, the module stages a clip id taken from Cort's reaction clips - all of which the shield has set to zero.
  5. Still idleClip zero is the idle clip, so +0x1D9 stays zero and the next contact hits again. Captured: it read 0 at every one of the 16 Theeder and 28 Mushura tests under the shield.
  6. Shield breaksAt half HP the per-frame pass restores the clips to 3, 2, 4, 5 and clears the handle. 0x8004D5C4
Register-level walkthrough

Cort's first action: his turn logic at 0x801EB988 in PROT entry 0898 queues action 0xAC while ctx[+0x28A] is zero.

Shield setup, the 0xAC body in FUN_801F7240, PROT entry 0940:

801f7678  sw  v0,-0x427c(v1)      ; 0x8007BD84 = effect handle
801f7690  sb  zero,0x1ef(v0)
801f769c  sb  zero,0x1f0(v0)
801f76a8  sb  zero,0x1f1(v0)
801f76b4  sb  zero,0x1f2(v0)

Mushura, FUN_801F69D8 in PROT entry 0915, every tick for every enemy seat. The clip load is reached only on a tick whose range test reads contact, so each time it runs is one contact tick:

801f7844  jal  0x8004e2f0         ; range test (slot 7, seat); 0 = touching
801f7860  lhu  v0,0x14c(v1)       ; alive
801f7870  lbu  v0,0x1d9(v1)       ; playing clip
801f7878  bne  v0,zero,<next>     ; the only re-hit lockout
801f789c  jal  0x801dd0ac         ; damage roll, power 0x12
801f78f0  srl  s0,s0,0x2          ; a quarter, floor 1
801f79c4  lbu  v0,0x1ef(v1)       ; flinch id (0x1f0 if zero)
801f79dc  sb   v0,0x1da(v1)       ; stage it

Theeder, FUN_801F69D8 in PROT entry 0904, every tick for every enemy seat:

801f7ce0  lhu  v1,0x14c(a0)       ; alive
801f7cf0  lbu  v0,0x1d9(a0)       ; playing clip
801f7cf8  bne  v0,zero,<next>     ; the same lockout
801f7d0c  addiu v0,v0,-0x30       ; beam bearing vs victim bearing
801f7d10  sltiu v0,v0,0xfb1       ; inside the 0x30 cone
801f7d38  jal  0x801dd0ac         ; damage roll, power 0x11, not quartered
801f7df0  lbu  v0,0x1f2(v1)       ; get-up id non-zero: stage the knockdown id +0x1F1
801f7e0c  lbu  v0,0x1ef(v1)       ; otherwise the flinch id (0x1f0 if zero)
801f7e24  sb   v0,0x1da(v1)       ; stage it

A living victim that has a get-up clip is knocked down, not flinched (0x801F7DF0..0x801F7E08). Unshielded Cort, whose clips are 3, 2, 4, 5, is staged clip 4: after Theeder's hit his +0x1D9 reads 4 for the rest of the sweep, and after each Mushura hit it reads 3.

Theeder's sweep. The sweep adds (frame step * speed scalar) << 3 to a phase word each tick (0x801F7AC8..0x801F7AF0) and ends when the phase reaches 0x1000 (0x801F7EE8..0x801F7EF4). The beam points at the summon's facing plus sin[phase] >> 4, a swing of 0x100 either side. Measured step 4 and scalar 8 give 256 a tick and 16 ticks; the two hits on shielded Cort land at phase 2048 and 4096.

Mule, FUN_801F69FC in PROT entry 0929 - the guarded sibling:

801f7e08  lbu  v0,0x1d9(v1)       ; the same lockout
801f7e10  bne  v0,zero,<next>
801f7e34  lw   v0,-0x427c(v0)     ; 0x8007BD84, the shield handle
801f7e3c  bne  v0,zero,<next>     ; shield up: no hit at all
801f7e4c  jal  0x801dd0ac

Mule's guard is read from the code only. A Mule cast on shielded Cort made no damage call, but both passes that reached the lockout left at a position test just before the shield test (0x801F7E2C), so the guard itself never ran.

Why only these. Across all 64 spell and summon modules, a test of +0x1D9 sitting directly in front of a damage-roll call occurs three times: Theeder, Mushura and Mule. Every other read of that byte is a wait for an animation to finish.

The halving, FUN_801DDB30 in PROT entry 0898, 0x801DDB98..0x801DDBC8: with an enemy defender and the handle non-null, the attack value becomes def + (atk - def) / 2.

Addresses

AddressImageWhatEvidence
0x801EB988..0x801EB9B4PROT 0898, FUN_801E9FD4Cort's turn logic queues 0xAC while ctx[+0x28A] is zerodisassembly, capture
0x801F7678PROT 0940, FUN_801F7240Shield handle stored to 0x8007BD84disassembly, capture
0x801F7690..0x801F76B4PROT 0940First enemy seat's reaction clips +0x1EF..+0x1F2 zeroeddisassembly, capture
0x801DDB98..0x801DDBC8PROT 0898, FUN_801DDB30Halve the hit on an enemy while the handle is setdisassembly, capture
0x8004D534..0x8004D658SCUS_942.54, FUN_8004CE2CBreak at half HP: clips restored to 3, 2, 4, 5, handle cleareddisassembly, capture
0x801F7870 / 0x801F789CPROT 0915, FUN_801F69D8Mushura: idle test / damage rolldisassembly, capture
0x801F78F0PROT 0915Mushura: roll quartered, minimum 1disassembly, capture
0x801F79C4..0x801F79E8PROT 0915Mushura: flinch staged from +0x1EF / +0x1F0disassembly
0x801F7CF0 / 0x801F7D38PROT 0904, FUN_801F69D8Theeder: idle test / damage rolldisassembly, capture
0x801F7D0C..0x801F7D14PROT 0904Theeder: cone test, 0x30 either sidedisassembly
0x801F7AC8..0x801F7AF0PROT 0904Theeder: phase step (frame step * scalar) << 3 per tickdisassembly, capture
0x801F7EE8..0x801F7EF4PROT 0904Theeder: sweep ends at phase 0x1000disassembly, capture
0x801F7DF0..0x801F7E08PROT 0904Theeder: knockdown +0x1F1 staged when +0x1F2 is non-zerodisassembly, capture
0x801F7E0C..0x801F7E24PROT 0904Theeder: flinch staged from +0x1EF / +0x1F0 otherwisedisassembly
0x801F7E08 / 0x801F7E34 / 0x801F7E4CPROT 0929, FUN_801F69FCMule: idle test / shield test / damage rolldisassembly
0x801F7E2CPROT 0929Mule: position test in front of the shield testdisassembly, capture
Monster record 180PROT 0867Cort: HP 50000, element dark, size byte 0x16disc data

Scope

  • Only Cort's first form (id 0xB4, scene chitei2, formation row 13) while the shield handle is set: his first 25000 of 50000 HP.
  • The cause is "no reaction clips", so any monster whose data supplies no reaction clips at all would be hit the same way by these two summons. That set has not been listed.
  • Each extra hit also earns its own summon experience, applies its own Seru side-effect and shows its own damage number: twice for Theeder, 28 times for the measured Mushura cast.
  • The shield can break in the middle of a Mushura cast. Once Cort is at or below half HP the per-frame pass restores his clips, one more hit lands on the clip that was already staged, and the rest of the cast is one hit per contact window. This was seen in one run from the first batch described under how the captures were made (the batch whose HP hold also wrote field memory before the battle), with the magic level left alone so that each hit took 2499: Cort crossed 25000 on the tenth run-over hit and the cast ended with 13.
  • Mule is the opposite case by its code: under the shield its sweep is written not to hit at all.

Verifying it

The three checks, as run on the forced Cort fight:

  • Execute breakpoints at 0x801F789C (Mushura) and 0x801F7D38 (Theeder) count 28 and 2 with the shield up, 4 and 1 once the game has broken it.
  • +0x1D9 of the actor at *(0x801C9370 + 0xC) reads 0 throughout under the shield; without it, 3 after a Mushura hit and 4 after a Theeder hit.
  • Writing 03 02 04 05 to that actor's +0x1EF..+0x1F2 with the shield up returns the counts to 4 and 1 while the rolls stay halved. The missing reaction is the cause and the shield handle is not.

Still to check: the same breakpoints in the real chitei2 fight, with three party members on screen, give Mushura's count at the real frame timing.

Open questions

  • Mushura's hit count in the real fight. The measured 28 comes from a one-character party on another stage, and the count follows frame timing.
  • A target that is only flinched (it has no get-up clip) and is back on idle within 32 frames is hit again at Theeder's second crossing; whether that is intended is unknown. Cort, once unshielded, is knocked down and is not.
  • Which monsters, if any, ship with no reaction clips.
  • Mule's shield guard has not been seen to run.