How to read this page
Most of this page is read from the code and data of the USA disc (SCUS_942.54). Two sections go further: the Tough Love counts are also measured in a running game, on a poked state that the section describes, and the boss-debuff section also reads the Japanese (SCPS_100.59) and French (SCES_019.44) builds. The Zenoir shatter section is captured in a running game on forced formations with an injected cast. The jewel, Berserker and Evil Fly sections are static analysis only. Where a number is predicted from the code rather than proven by it, the text says so. Addresses are given so anyone with a debugger can check them: the main executable loads at 0x80010000, the battle overlay (PROT entry 0898) at 0x801CE818, and the cast and summon modules (PROT entries 0903 to 0966) at 0x801F69D8. The reports and questions come from the community.

Tough Love and Love You roll their status every frame

Reported as
Tough Love and Love You attempt to apply the status they cause seven times during the animation, making it nearly guaranteed (community report)
Cause
A hand-written routine flips a 50% coin on every battle frame from the first hit to the end of the animation: 20 flips for Tough Love and 80 for Love You at the battle's normal pace, fewer when frames drop, never under 10.
Where
Per-frame battle actor pass FUN_8004CE2C in SCUS_942.54, arm 0x8004D0A4..0x8004D120
Affects
Noa only, these two arts only; random encounters only
Status
Explained - counted in a running game, on a poked state (see How it was measured)

Noa's Tough Love and her Super Art Love You do not roll for their status when they hit. The normal status on hit slot of an art is empty on both. Their effect is written by hand into a routine that runs once per battle frame.

From the frame in which the first blow lands until the animation runs out, 20 keyframes later, that routine flips a coin every battle frame and puts the confusion status on Noa's target whenever it comes up heads. Nothing records that the coin already came up heads and nothing checks a resistance.

In ordinary play that is 20 flips for Tough Love and 80 for Love You. The count is not quite a constant, because the flip is made once per battle frame while the animation advances by a fixed amount of time per battle frame. A battle normally shows one battle frame every two video frames (60ths of a second). When the screen is busy the game drops to one in three, or at worst one in four; the same animation is then covered in fewer battle frames, so fewer flips fit. The table gives the normal row first.

How many times it rolls, and the final chance

ArtBattle paceCoin flipsFlips that decide the outcomeChance the status is on at the endA miss is
Tough Lovenormal: one battle frame per 2 video frames202099.9999%1 in 1,048,576
Tough Lovebusy screen: one per 3141499.994%1 in 16,384
Tough Loveslowest the game allows: one per 4101099.90%1 in 1,024
Love Younormal: one battle frame per 2 video frames (computed, not captured)8068all but certainfar below 1 in a trillion
Love Youbusy screen: one per 35446all but certain1 in 70 trillion
Love Youslowest the game allows: one per 44034all but certain1 in 17 billion
the reported figure-7-99.22%1 in 128

Each flip is exactly one chance in two, so after N flips the chance is 1 - 0.5^N. The two flip columns differ only for Love You, and for one reason: every hit wipes the status again, so the flips made before its fourth and last hit cannot decide anything. Tough Love has a single hit and the flips start on it, so all of them count.

Two numbers that are easy to mix up
Each art record has a playback rate byte (entry +0x78): 4 on Tough Love, 2 on Love You. It is fixed on the disc and nothing in play changes it; it is why Love You makes four times as many flips. The frame step in the table above is a different thing and belongs to no art: it is the game's own frame-skip counter (0x1F800393), rebuilt every frame from how long the last sixteen frames took to draw. It is 2 in a normal battle and 3 while the screen is busy, the same for every action on screen. No mod or exploit is needed to change it - lag does.

So the report is right in kind - a repeated roll, close to unmissable - but the roll follows frames, not hits, and the count is never seven. 10 is the fewest either art can make. Another figure that circulates in the community, about 13, is the Tough Love count in a battle that is dropping to one frame in three.

The status makes the monster aim its own action at its own side. It has no timer: it ends when the monster next takes damage. That is why it only sticks when the art is the last hit the monster takes - a later swing in the same chain removes it. The whole effect is switched off in scripted fights, which covers the bosses.

keyframe 024: first hit lands44: animation endscoin flipsa miss isTough Loveone hit4 vsyncs per frame101 in 1,0243 vsyncs per frame141 in 16,3842 vsyncs (normal)201 in 1,048,576Love Youfour hits4 vsyncs per frame34 of 401 in 17 billion3 vsyncs per frame46 of 541 in 70 trillion2 vsyncs (normal)68 of 80even rarera flip that can decide the outcomea flip made before the last hit (wiped by it)The reported 7 flips would miss 1 time in 128. The game never makes fewer than 10.
Each tick is one coin flip, drawn at the point of the animation where it happens. A slower battle frame covers more of the animation per flip, so fewer flips fit. The 2-vsync rows are the normal battle pace. Counts at 4 and 3 vsyncs per frame, and Tough Love at 2, are captured on a poked state; Love You at 2 is computed from the same formula.

Step by step

  1. The art records carry no status: the selector byte at entry +0x7A is 0 on Tough Love (clip key 0x29) and Love You (clip key 0x2D). Byte +0x76 is 1 on both, so a queued follow-up never cuts the animation short.PROT 0864 art bank, records 19 and 33
  2. Once per battle frame the actor maintenance pass runs. It is the last call of a resident per-frame tick.jal at 0x8004DC4C -> FUN_8004CE2C
  3. If the acting character is Noa and her current clip key is 0x29 or 0x2D, the pass continues.0x8004D0A4..0x8004D0C4
  4. It requires that a strike has already fired (strike counter non-zero) and that the fight is not scripted.0x8004D0CC, 0x8004D0DC
  5. It draws one random number and continues only when the low bit is 0: one chance in two. There is no latch.0x8004D0EC..0x8004D0F8
  6. Unless the first monster is id 167, it ORs 0x380 into the target's status word.0x8004D100..0x8004D120
  7. Next frame the same block runs again. The strike counter is zeroed only when the next animation is installed, which for these arts is the end of the 44-keyframe clip.0x8004B064, 0x80047A48..0x80047A60
  8. Any damaging hit on the monster clears the status group again - Love You's own later hits included.0x801EDA5C, 0x801DE2EC
The instructions and the count formula
8004d0a4  lw   v0,0x22c(s0)         ; animation node
8004d0ac  lw   v0,0x4c(v0)          ; the clip record being played
8004d0b4  lbu  v1,0x77(v0)          ; clip key
8004d0b8  li   v0,0x29  / beq       ; Tough Love
8004d0c0  li   v0,0x2d  / bne out   ; Love You
8004d0cc  lbu  v0,0x1f4(s0) / beq v0,zero,out    ; a strike has fired
8004d0dc  lbu  v0,0x287(a2) / bne v0,zero,out    ; not a scripted fight
8004d0ec  jal  0x80056798                        ; rand()
8004d0f4  andi v0,v0,0x1 / bne v0,zero,out       ; one chance in two
8004d100  lbu  v1,0x9f4(gp) ; li v0,0xa7 ; beq out
8004d110  lhu  v0,0x16e(s1)
8004d118  ori  v0,v0,0x380
8004d120  sh   v0,0x16e(s1)

s0 is the acting actor (from ctx[+0x13]), s1 its target (from the actor's +0x1DD), a2 the battle context.

The animation cursor counts sixteenths of a keyframe and advances by (frame step × speed × rate) >> 1 per battle frame (0x80047730..0x80047764). The art commit sets speed to 2 when ctx[+0x243] is set and 4 otherwise (0x8004BB64..0x8004BBA4): Tough Love runs at speed 4 with rate 4, Love You, staged as a Super Art, at speed 2 with rate 2. That is 8 × step per frame for Tough Love and 2 × step for Love You.

The window opens in the frame the cursor reaches 384 (keyframe 24, the first strike) and closes in the frame it reaches 704 (keyframe 44, the end of the clip). The animation driver runs before the actor pass, so the strike frame itself rolls and the closing frame does not. The count is the number of frames with the cursor in [384, 704): 320 / (8 × step) and 320 / (2 × step), rounded up. Love You's last hit is at cursor 432, so its deciding flips are 272 / (2 × step). At step 3 Tough Love gives 13 instead of 14 when the cursor does not land on 384 exactly.

The frame step lives at 0x1F800393. The frame driver FUN_80016B6C keeps the draw time of the last sixteen frames, in scanlines, in a ring at 0x80084098 and takes the slowest: under 0xF1 is step 1, under 0x1FF step 2, under 0x2D1 step 3, and 4 only when the newest frame alone ran past that (0x80017098..0x8001715C). A per-mode floor is applied afterwards; the battle's floor is 1. The playback rate is a separate byte, entry +0x78 of the art record.

How it was measured

The counts come from a running game (PCSX-Redux, retail disc) with breakpoints on the pass entry, on 0x8004D0F4 where the roll is read, on the status write, and on the strike counter. They are captured on a poked state, which is weaker than played input: in a random encounter where Noa is on the first swing of an auto-executing chain, the unconsumed tail of her action queue (actor +0x1DF) is rewritten so that the game's own queue consumer stages Tough Love, or Love You behind the Super Art starter. The art commit, animation, strikes, damage, roll and status write are then the game's own code. Where the table says forced, the frame-step byte was also overwritten.

ArtFrame stepFlipsFlips at or after the last hitHeads in that run (luck)
Tough Love4 (the emulator's own pacing)10107
Tough Love3 (forced)14146
Tough Love2 (forced)20208
Tough Love, two swings queued after it410103
Love You4 (the emulator's own pacing)403426
Love You3 (forced)544628

The last column is not a property of the art. It is how many of that one run's flips happened to come up heads, each of which writes the same status bits again; it is there to show the write really happens more than once, and another run of the same art gives a different number. The first run happened to write the status seven times out of its ten flips. That is chance - the other Tough Love runs wrote it 6, 8 and 3 times - and not a count the code makes. In the run with two swings queued behind Tough Love the animation still played to its end, the status was on when it finished, and Noa's next swing cleared it. In the Love You runs each later hit took the status word from 0x380 back to 0 and a following flip set it again.

Which row a player gets depends on the frame step. Battle save states from a timing-accurate emulator show what a console holds: of 76 states that are in a battle, 57 hold step 2, 18 hold step 3 (summon close-ups, cast modules and other effect-heavy moments), one loading frame holds 1 and none holds 4. Step 2 is therefore the normal case. None of the 76 is taken inside either of these two animations, so which of the two steps Love You's own effects produce is inferred. Step 4 is what the capture emulator runs at on its own, a property of its timing and not of the game, which is why the measured table starts at 10 and 40.

What else it affects

Never fires in a scripted fight or when the first monster is id 167 (Lapis). The status lasts only until the target's next damage, from anyone. No other player art inflicts any status: across the four party art banks the selector byte is only 0, 1 (Vahn's fire arts) or 2 (Gala's Thunder Punch), and 1 / 2 only tint the target. A monster's physical attack, by contrast, rolls its status once per strike.

Addresses

AddressImageWhatEvidence
0x8004CE2CSCUS_942.54Per-frame battle actor pass; entered once per battle framedisassembly, capture
0x8004D0A4..0x8004D120SCUS_942.54 (file 0x3D8A4)Noa's clip-key arm: gates, roll and status writedisassembly, capture
0x8004D0ECSCUS_942.54The rand() call of the 1-in-2 rolldisassembly
0x8004D0F4SCUS_942.54Where the roll is read; one execution per flipdisassembly, capture
0x8004DC4CSCUS_942.54Call to the pass, the last call of the per-frame tick FUN_8004DA00disassembly
entry +0x77PROT 0864, art bank at decoded +0x5A30, records 19 and 33Clip keys 0x29 (Tough Love) and 0x2D (Love You)disc data
entry +0x7APROT 0864, same recordsStatus selector, 0 on both artsdisc data
entry +0x76PROT 0864, same records1 on both: no early cut after the last strikedisc data
entry +0x78PROT 0864, same recordsPlayback rate: 4 (Tough Love), 2 (Love You). Fixed per art; not the frame stepdisc data
actor +0x1F4RAMPer-clip strike counter; raised at 0x801EECE8, zeroed at 0x8004B064disassembly, capture
actor +0x16E bits 0x0380RAMConfusion (AI-delegation) group the arm setsdisassembly
ctx +0x287RAMScripted-fight flag; non-zero disables the armdisassembly
0x8007BD0CRAMFirst formation monster id, compared with 0xA7disassembly
0x801EDA5CPROT 0898A damaging physical hit clears status mask 0x0F80 on the defenderdisassembly, capture
0x801DE2ECPROT 0898The same clear in the magic damage finisherdisassembly
0x80047730..0x80047764SCUS_942.54Animation cursor advance: frame step x speed x rate / 2disassembly, capture
0x80047A48..0x80047A60SCUS_942.54Clip-end test against the stream lengthdisassembly, capture
0x80047940..0x80047948SCUS_942.54Early-cut veto on entry +0x76disassembly, capture
0x8004BB64..0x8004BBA4SCUS_942.54Art commit: speed 2 or 4 from ctx +0x243disassembly, capture
0x1F800393RAM (scratchpad)Frame step, 1 to 4disassembly
0x80017098..0x8001715CSCUS_942.54Pacer that sets the frame step; frame-cost ring at 0x80084098disassembly

Every capture on this list is from the poked state described above.

Verifying it

Count executions of 0x8004D0F4 while Noa uses either art in a random encounter: that is the number of flips, and v0 & 1 == 0 marks the ones that apply. Read 0x1F800393 in the same frames to see which row the run belongs to. A write watch on the target's +0x16E shows each Love You hit clearing 0x380 and a later flip restoring it.

Open questions

  • The frame step held while these two animations play, measured on a timing-accurate emulator or a console. Battle states in general hold 2, and 3 on a busy screen; no state inside either animation is on hand. Every possible value gives 10 or more.
  • A played confirmation, with the art entered from the arts menu instead of a rewritten queue.
  • Whether a strike that misses still opens the window.
  • Which piece of code raises ctx +0x243 for a Super Art; the resulting speed is measured, its setter is not named.

Background: the battle hub page (per-frame actor pass) and art records.

Seven boss attacks ignore the defensive jewels

Reported as
Bloody Horns, Terio Punch, Megaton Press, Plasma Strike, Blazing Slash and Guilty Cross ignore the damage reduction of the defensive jewels (community report)
Cause
These attacks use a physical (ATK-based) damage wrapper that tells the shared finisher to skip its jewel block; it reads as intended.
Where
Damage finisher FUN_801DDB30 and wrapper FUN_801DD6B4, PROT 0898; six cast modules
Affects
Seven attacks: the six reported plus Bull Charge
Status
Explained

The elemental jewels do not change the damage roll. Their halving is one block at the top of the routine that finishes every magic and special-attack hit, and that block is skipped when the routine's fifth argument is non-zero.

Boss cinematic attacks reach that routine through one of two small wrappers. One passes 0, so jewels apply. The other passes 1, and it builds its damage from the attacker's ATK against the target's upper and lower defence instead of from INT. The reported attacks are exactly the ones whose code calls the second wrapper - plus one more, Xain's Bull Charge, which shares its code with Terio Punch.

This reads as intended design rather than a slip: the second wrapper is a physical-damage formula, and an elemental guard has nothing to say about a physical hit. What the game never does is tell the player which boss attacks are in that class. The Rainbow Jewel's all-element guard is skipped too; defending still halves these attacks.

Monster special attackFUN_801DD0AC, arg5 = 0Boss cast, INT-basedFUN_801DD4B0, arg5 = 0Boss cast, ATK-basedFUN_801DD6B4, arg5 = 1Jewel blockelement guard: halve; All Guard: x3/4Guard halvedefender is guardingFloor and capminimum 8-16, maximum 9999finisher FUN_801DDB30skips the jewelsATK-based route: Guilty Cross, Bloody Horns, Terio Punch, Bull Charge,Blazing Slash, Megaton Press, Plasma StrikeEvery other enemy cast and special takes one of the two upper routes.
Three routes into one finisher. Only the ATK-based wrapper enters below the jewel block.

Step by step

  1. The finisher reads its fifth argument from the stack.0x801DDB74
  2. For a party defender, a non-zero fifth argument branches over the whole jewel block.0x801DDBD4
  3. The skipped block tests All Guard (damage x3/4) and then each element's guard against the attacker's element (halve).0x801DDC0C, 0x801DDCA8, 0x801DDD3C ...
  4. The guard halve sits after the skipped block and still applies.0x801DE094
  5. Of the three callers of the finisher, only one stores 1 as the fifth argument.0x801DD824 in FUN_801DD6B4
  6. That wrapper adds the attacker's ATK and never reads INT.0x801DD738
  7. The call to that wrapper appears at thirteen places, in six cast modules and nowhere else.word 0x0C0775AD
The instructions
801ddb74  lw   a3,0x40(sp)                    ; fifth argument
801ddbd4  bne  a3,zero,0x801de094             ; set: skip everything below
801ddc0c  lw   v0,0x6c0(v0) ; andi 0x10        ; All Guard: damage x 3/4
801ddca8  lw   v0,0x6bc(v0) ; and 0x20000000   ; Earth Guard vs an earth attacker: halve
801ddd3c  lw   v0,0x6bc(v0) ; and 0x40000000   ; Water Guard vs a water attacker: halve
801de094  lbu  v1,0x1de(s1) ; li v0,4          ; guard halve, after the skipped block

801dd824  li   v0,0x1                          ; FUN_801DD6B4
801dd828  jal  0x801ddb30
801dd82c  sw   v0,0x10(sp)                     ; ... passes 1

The two words tested are the character's accessory-passive bitfield; passive 0x1D + element is that element's guard and 0x24 is All Guard. The other two callers (0x801DD478, 0x801DD678) store zero in the same delay slot.

Every call to the bypass wrapper on the disc

PROTCall sitesPowerSpell idAttack
09440x801F6AD80x38E0x37Guilty Cross
09520x801F79480x1D00x5CBloody Horns
09530x801F74100x2740x5D, 0x5ETerio Punch, Bull Charge
09580x801F74EC, 0x801F7854, 0x801F7CA4, 0x801F80E4, 0x801F8754, 0x801F88D80x30, 0x38 x3, 0x40, one computed0x79Blazing Slash
09590x801F71E8, 0x801F7A90, 0x801F7EBC0x80, 0x80, 0x300x7AMegaton Press
09600x801F81680x1C00x7BPlasma Strike

What else it affects

The complete set is seven attacks: Guilty Cross, Bloody Horns, Terio Punch, Bull Charge, Blazing Slash, Megaton Press and Plasma Strike. No other code on the disc sets the bypass argument. Users are Cort, Xain, the three Delilas siblings and Gaza. Because the wrapper reads ATK, the boss's INT does not affect these attacks either.

Addresses

AddressImageWhatEvidence
0x801DDB30PROT 0898 (file 0xF318)Damage finisherdisassembly
0x801DDBD4PROT 0898Branch that skips the jewel block when argument five is setdisassembly
0x801DDC0CPROT 0898All Guard test (passive word +0x6C0, bit 0x10)disassembly
0x801DDCA8, 0x801DDD3CPROT 0898Earth Guard and Water Guard tests (passive word +0x6BC)disassembly
0x801DE094PROT 0898Guard halve, still applieddisassembly
0x801DD6B4PROT 0898 (file 0xEE9C)Bypass wrapper; li v0,1 at 0x801DD824, stored at 0x801DD82Cdisassembly
0x801DD738PROT 0898The wrapper reads attacker ATK (+0x158)disassembly
0x801DD4B0, 0x801DD0ACPROT 0898The two finisher callers that pass zerodisassembly
0x801F7EE8PROT 0944Spell-id test: 0x37 goes to the bypass routine 0x801F6A04disassembly
0x801F7B54PROT 0952Spell-id test: 0x5C goes to the bypass routine 0x801F7118disassembly
0x801F69FCPROT 0953Single routine for both 0x5D and 0x5E, no id testdisassembly
0x801F8664PROT 0960Spell-id test: 0x7B goes to the bypass routine 0x801F74E4disassembly
0x800754C8 + id*12SCUS_942.54Spell record: class 0x63 at +0, module index at +1 (PROT = 935 + index)disc data
record +0x1DPROT 0867 monster recordAttacker element the jewel block comparesdisassembly

Verifying it

Break on 0x801DDBD4 as an attack lands and read a3: 1 for the seven attacks, 0 for any other enemy cast (Neo Star Slash, from the same module as Plasma Strike, is a good control).

Astral Slash is a melee hit

Astral Slash shares its module with Bloody Horns, and its routine calls none of the three wrappers. It stages clip 0x0A on Gaza, and the ordinary melee kernel rolls that clip's four beats, so a matching jewel halves it through the kernel's own copy of the guard block, keyed on Gaza's light element. The same block covers every plain monster swing: an elemental monster's ordinary attack is halved by the matching jewel too.

Background: battle formulas, accessory passives and the spell table.

USA bosses cannot have ATK, INT or DEF lowered

Reported as
In the US version, bosses' ATK, INT and DEF cannot be reduced with spell effects, due to the way it is programmed (community report)
Cause
The debuff only lands if the boss's base stat still equals its disc record, and the USA difficulty boost changes the base before the fight starts.
Where
Debuff stager FUN_801F3D3C (PROT 0898) against the loader boost in FUN_80054CB0 (SCUS_942.54)
Affects
Every scripted fight on the USA disc; ATK, DEF and INT only. Not the Japanese or French discs, which have no boost
Status
Explained

Levelled Seru magic carries a side effect by element: fire lowers ATK, earth lowers DEF, thunder lowers INT. There is no boss-immunity flag for these. Two unrelated pieces of code collide.

The first is a rule for scripted fights: lower a stat only if it is still at its original value. The code checks that by comparing the monster's live base stat with the number in its disc record. Once a debuff lands the base has moved, so each debuff works once per boss fight.

The second is the USA difficulty boost. When a scripted fight loads, the USA executable raises ATK, defence and INT above the record values - and writes the raised numbers into the base as well. From the first frame the base no longer equals the record, so the debuff code decides the stat was already lowered and shows No effect.

Speed and MP are not boosted, which is why those two side effects still work on bosses.

The boost is in the USA executable only. The Japanese (SCPS_100.59) and French (SCES_019.44) loaders go from the stat copy straight to the next piece of work, and both carry the same base-versus-record check in the debuff code. On those discs a boss starts with base equal to record, so the first attack-down, defence-down or intelligence-down lands, and a second one of the same kind is refused. The code is read in all three builds; the on-screen result on the other two discs has not been watched in a running game.

upper bar: disc record (also the base on a Japanese or French disc); lower bar: USA baseATK108135base differs from recorddebuff blocked from the first castUDF95190base differs from recorddebuff blocked from the first castINT117131base differs from recorddebuff blocked from the first castSPD6666base = recorddebuff lands (once)
Zeto to scale, one drawing unit per stat point. On the USA disc the three boosted stats no longer match the record, so their debuffs are refused; SPD is untouched and still works. On a Japanese or French disc every base equals the record and each debuff lands once.

Step by step

  1. The stager runs once per cast, returns for a spell below level 3, and in scripted fights applies a four-in-five suppression roll.FUN_801F3D3C, 0x801F3DE0, 0x801F3DEC
  2. It jumps on the summoned creature's element.table at 0x801CFA2C
  3. In a scripted fight the element's arm loads the target's live base stat and the same stat from the monster record.e.g. 0x801F3F38 / 0x801F3F3C
  4. If the two differ, it returns without staging anything.0x801F43F0
  5. At battle load the monster loader copies each record stat into both the current and the base halfword.0x8005516C..0x8005520C
  6. For a scripted fight the USA loader then raises ATK by a quarter, doubles both defences and raises INT by an eighth, writing both halfwords.0x80055234..0x800552A8
  7. The Japanese and French loaders have no such block: the stat copy is followed directly by the spell-list loop.0x80057014 (SCPS_100.59), 0x80055B1C (SCES_019.44)
The instructions
; FUN_801F3D3C, earth arm (PROT 0898)
801f3ec8  lbu  v0,0x287(v1)
801f3ed0  beqz v0,0x801f4414        ; random encounter: no compare
801f3f38  lhu  v1,0x15e(v1)         ; live upper-defence base
801f3f3c  lhu  v0,0x14(v0)          ; the record's upper defence
801f43f0  beq  v1,v0,0x801f4414     ; equal: stage the debuff
801f43fc  addiu s0,s0,1             ; different: blocked
801f4414  bnez s0,0x801f4484        ; blocked: return

; FUN_80054CB0, scripted-fight boost (SCUS_942.54)
80055234  lbu  v0,0x287(v0) ; beq v0,zero,0x800552ac
80055248  ATK += ATK >> 2      ; sh 0x15a / sh 0x158
80055264  UDF += UDF           ; sh 0x15e / sh 0x15c
80055280  LDF += LDF           ; sh 0x162 / sh 0x160
8005529c  INT += INT >> 3      ; sh 0x16a / sh 0x168

What each element compares

ElementArmLive baseRecord fieldStatBoosted on USA?
earth0x801F3EBC+0x15E+0x14upper defenceyes
water0x801F3FA4+0x156+0x0EAGLno
fire0x801F407C+0x15A+0x12ATKyes
wind0x801F4164+0x166+0x1ASPDno
thunder0x801F424C+0x16A+0x18INTyes
dark0x801F4334+0x152+0x10MPno

The same code on the three discs

BuildLoader: what follows the stat copyDebuff stagerShared compareScripted-fight flag
USA SCUS_942.54the boost block 0x80055228..0x8005530C, then the spell loop at 0x800553100x801F3D3C, PROT 0898 at base 0x801CE8180x801F43F0ctx +0x287
France SCES_019.44two byte clears, then the spell loop at 0x80055B1C0x801F468C, PROT 0898 at base 0x801CF0A80x801F4D40ctx +0x2A7
Japan SCPS_100.59two byte clears, then the spell loop at 0x800570140x801F7394, PROT 0898 at base 0x801D05C80x801F79BCctx +0x1E7

The French stager matches the USA routine instruction for instruction once addresses are masked. The Japanese one is a different compile with the same level gate, suppression roll, jump table (0x801D1880), stat pairs and shared compare. The USA block also holds a second arm for random encounters (both defences plus three quarters, INT plus a quarter), which the other two discs lack as well; it never shows in the debuffs because the check is skipped outside scripted fights.

Zeto, record against the USA in-battle base

StatRecordUSA baseDebuff
ATK108135blocked
Upper defence95190blocked
INT117131blocked
SPD6666lands once

Published stat lists, this project's curated table included, print the boosted boss figures for every enemy, so the number in a list is the USA base and the record value is the lower one.

What else it affects

The boost could only leave a stat unchanged when ATK is below 4, upper defence is 0 or INT is below 8. Checked against every formation row on the disc, no monster on a scripted row meets any of those, so ATK-down, DEF-down and INT-down never land in any scripted fight on the USA disc. Still possible there: SPD-down once, AGL-down once, MP-down on each cast that survives the suppression roll. In random encounters the compare is skipped and the debuffs stack.

Addresses

AddressImageWhatEvidence
0x801F3D3CPROT 0898 (file 0x25524)Debuff stagerdisassembly
0x801CFA2CPROT 0898Seven-entry element jump tabledisc data
0x801F3EC8PROT 0898Earth arm reads the scripted-fight flag; clear skips the comparedisassembly
0x801F43F0PROT 0898Base-versus-record compare shared by every armdisassembly
0x801F6870PROT 0898 (file 0x28058)Debuff percentages 5/10/15/20 by element and level banddisc data
0x801F6960RAMStaged percentage; zero leads to the No effect bannerdisassembly
0x801DE648..0x801DE8ECPROT 0898Finisher tail applying the debuff to both halfwordsdisassembly
0x80054CB0SCUS_942.54Monster stat loaderdisassembly
0x80055234..0x800552A8SCUS_942.54Scripted-fight boost: ATK +1/4, defences x2, INT +1/8disassembly
ctx +0x287RAMScripted-fight flagdisassembly
record +0x12, +0x14, +0x18PROT 0867 monster recordATK, upper defence, INTdisc data
0x800552AC..0x80055300SCUS_942.54Random-encounter boost: defences +3/4, INT +1/4disassembly
0x80055B1CSCES_019.44Spell-list loop directly after the stat copy: no boost blockdisassembly
0x80057014SCPS_100.59Spell-list loop directly after the stat copy: no boost blockdisassembly
0x801F468C, 0x801F4D40French PROT 0898 (base 0x801CF0A8)Debuff stager and its shared comparedisassembly
0x801F7394, 0x801F79BCJapanese PROT 0898 (base 0x801D05C8)Debuff stager and its shared comparedisassembly
0x801D1880Japanese PROT 0898 (file 0x12B8)Seven-entry element jump tabledisc data

Verifying it

In a boss fight cast a level-3 or higher fire spell and break on 0x801F43F0: v1 is the boosted base (135 for Zeto), v0 the record value (108), the branch is not taken and 0x801F6960 stays zero. Write the record value into the actor's +0x15A and cast again: the debuff lands. On a Japanese disc the same break is 0x801F79BC, where a0 and v0 are equal on the first cast.

Open questions

  • A boss debuff landing on a Japanese or PAL disc has not been watched in a running game.
  • Which target-byte value each player spell uses for group casts is not enumerated; the arms treat value 8 differently.

Background: battle formulas (Seru-magic side effects) and the battle hub page (the loader boost and its regional comparison).

Every Berserker can be killed outright

Reported as
All Berserker bosses can be insta-killed (community report)
Cause
Instant-death summons treat a monster as a boss by a texture-size byte in its record, and that byte is 0 on all three Berserkers.
Where
Nighto module PROT 0907 (0x801F6BF0), Aluru module PROT 0916 (0x801F6D44); monster record byte +0x20
Affects
Berserker ids 77, 173 and 174; no other story boss
Status
Explained

The monster data has no instant-death immunity field. The two player summons that can kill outright, Nighto and Aluru, decide whether the target is a boss with a makeshift test: the fight is scripted, and one particular byte of the monster record is non-zero.

That byte is a graphics setting. It tells the model loader that the monster's texture needs a double-width page of video memory. Nearly every large boss has it set, so it works as a stand-in for boss - except for Berserker, whose texture fits in a normal page. Its byte is zero, the test fails, and the summon rolls its ordinary chance.

Nighto lands 9 / (19 - level) of the time (50% at level 1, 90% at level 9) and a landed Nighto kills one time in eight, otherwise it confuses. Aluru lands 8 / (17 - level) of the time - every time at level 9 - and a landed Aluru always kills.

A third summon shows the byte's blind spot was noticed somewhere: Zenoir uses the same test to keep its shatter effect off bosses, and lists the three Berserker ids by hand. Nighto and Aluru never received that list. Zenoir's list is about how a dead monster is drawn, not about damage, and nothing in the code says its author knew the kill rolls shared the gap (below).

byte = 1: resistsbyte = 0: exposed, not a bossbyte = 0: exposed bossRed Piura0Black Piura0Caruban1Viguro0Zeto1Songi1Berserker0Golem0Songi1Van Saryu1Dohati1Xain1Kemaro0Puera0Gi Delilas1Che Delilas1Lu Delilas1Gaza1Gaza1Rogue1Zora1Jette1Caruban1Caruban1Berserker0Berserker0Tetsu1Songi1Cort1Cort1Koru1
The scripted-fight roster by record byte +0x20. The three Berserkers are the only bosses with the byte clear.

Step by step

  1. Nighto's routine reads the scripted-fight flag; in a random encounter it goes straight to the roll.0x801F6BF0
  2. In a scripted fight it loads byte +0x20 of the target's monster record.0x801F6C18
  3. Non-zero: the target resists. Zero: fall through to the roll.0x801F6C20
  4. The roll is rand % (19 - spell level) < 9.0x801F6C28..0x801F6CA8
  5. A separate rand % 8 picks the outcome: 0 sets HP to zero, anything else sets the confuse bits.0x801F6B50, 0x801F7E58, 0x801F7F50
  6. Aluru repeats the gate, rolls rand % (17 - level) < 8 and sets HP to zero.0x801F6D44, 0x801F6D70, 0x801F7E14
  7. Zenoir's module adds an explicit test for monster ids 0x4D, 0xAD, 0xAE - the three Berserkers. It guards Zenoir's shatter effect, not damage (next section).0x801F8218..0x801F8230
The instructions
; Nighto, PROT 0907
801f6bf0  lbu  v0,0x287(a1)         ; scripted fight?
801f6bf8  beqz v0,0x801f6c28        ; no: roll
801f6c18  lbu  v0,0x20(v0)          ; the target's record +0x20
801f6c20  bnez v0,0x801f6cb8        ; non-zero: resisted
801f6c28  jal  0x80056798           ; rand()
801f6c70  rand % (19 - spell level)
801f6ca8  slti v1,v1,9              ; below 9: it lands

; Zenoir, PROT 0908
801f8218  lbu  v1,-0x42f4(v0)       ; first formation monster id
801f821c  li v0,0x4d / beq          ; 77
801f8224  li v0,0xad / beq          ; 173
801f822c  li v0,0xae / beq          ; 174

Every monster on a scripted formation row, with record byte +0x20

IdMonsterHP+0x20Nighto / Aluru
19Red Piura (tutorial)750exposed
20Black Piura (tutorial)850exposed
73Caruban10241immune
74Viguro12800exposed
75Zeto50001immune
76Songi8881immune
77Berserker50000exposed
128Golem6400exposed
136Songi128881immune
137Van Saryu132451immune
138Dohati172001immune
139Xain95111immune
144Kemaro14000exposed
156Puera16000exposed
162Gi Delilas100001immune
163Che Delilas120001immune
164Lu Delilas95001immune
165Gaza120001immune
166Gaza150001immune
168Rogue282001immune
169Zora224221immune
170Jette345671immune
171Caruban40961immune
172Caruban81921immune
173Berserker100000exposed
174Berserker150000exposed
175Tetsu99991immune
179Songi480001immune
180Cort500001immune
181Cort655351immune
182Koru200001immune

What else it affects

Of the monsters met in scripted fights, 22 have the byte set and resist; nine do not. Three of the nine are the Berserkers (5000, 10000 and 15000 HP); the other six are ordinary monsters and capturable Seru fought through a scripted row (the two tutorial Piuras, Golem, Viguro, Kemaro, Puera). No other story boss is exposed.

Lapis shows that the byte alone protects nothing. Its byte is 1, but its only formation row does not mark the fight as scripted, so the test is never consulted and Lapis rolls like a random encounter. The scripted-fight flag reads 0 for the whole Lapis fight in a capture on a forced formation (the Lapis row's bytes written over another scripted fight's row as it loads); see the Lapis page.

Jedo is no third route against a Berserker. Its module (PROT 0931) looks at the scripted-fight flag alone: clear, it sets every living enemy's HP to zero (0x801F8814); set, it deals rolled damage (0x801F8438). A Berserker fight is flagged, so Jedo only deals damage there.

Addresses

AddressImageWhatEvidence
record +0x20PROT 0867, slot (id-1)*0x14000Double-width texture flag, reused as the boss testdisc data
0x801C9348[slot-3]RAMPointer to the target's monster recorddisassembly
0x801F6BF0, 0x801F6C18PROT 0907Nighto's gate: scripted flag, then record +0x20disassembly
0x801F6C28..0x801F6CC4PROT 0907Nighto's roll; resist word 0x801F853Cdisassembly
0x801F6CE4..0x801F6D28PROT 0907Extra one-in-three failure when Gala castsdisassembly
0x801F6B50..0x801F6B7CPROT 0907Outcome draw rand % 8 into 0x801F8534disassembly
0x801F7E58PROT 0907Kill: HP set to zerodisassembly
0x801F7F50PROT 0907Confuse: status bits 0x380 setdisassembly
0x801F6D44, 0x801F6D70PROT 0916Aluru's gatedisassembly
0x801F6DE4..0x801F6E1CPROT 0916Aluru's roll; resist word 0x80209010disassembly
0x801F7E14PROT 0916Kill: HP set to zerodisassembly
0x801F81F0..0x801F8230PROT 0908Zenoir's gate plus the Berserker id list (shatter effect only)disassembly, capture
0x801F87DC, 0x801F8814, 0x801F8438PROT 0931Jedo: flag test, kill store (flag clear), rolled damage (flag set)disassembly; capture on a forced Lapis formation with an injected cast
ctx +0x287RAMScripted-fight flagdisassembly

Verifying it

In a Berserker fight break on 0x801F6C20 during a Nighto cast or 0x801F6D78 during an Aluru cast: v0 reads 0 and execution continues into the random call. In the Zeto or Xain fight it reads 1 and the resist arm is taken. A level-9 Aluru should kill a Berserker on every cast.

Open questions

  • Nighto and Aluru have not been cast on a Berserker in a running game; the result is read from the test and the three records.
  • Instant-death sources other than these two summons and Jedo are not surveyed.

Background: the battle hub page (monster record layout).

Zenoir leaves a lone Golem in pieces

Reported as
What is special about Zenoir against Berserker? (community question, following the Berserker write-up above)
Cause
Zenoir shatters the monsters it kills. The last monster of a scripted fight is never faded away, so Zenoir skips bosses with the texture-byte test plus a hand list of the three Berserkers. The list misses the Golem and the lone Puera.
Where
Zenoir module PROT 0908, phase 0xC loop 0x801F81C8..0x801F8264; shatter renderer FUN_80029888 / FUN_80029DD8; grey stamp in FUN_800480D8
Affects
The Golem (id 128, Rikuroa) and the lone Puera (id 156); the Berserkers are protected by the list
Status
Explained - captured in a running game on forced formations with an injected cast

When Zenoir kills a monster, the monster does not just fade. Its model breaks into its own triangles, which spin and fly outward while the body fades to black. In an ordinary fight the fire covers most of it and the shards are gone with the fade.

The last monster of a scripted fight is different. The game never fades it away: it keeps drawing the body at full colour so it stays on stage until the victory scene. A shattered boss would leave a lit cloud of shards hanging over the stage, so Zenoir skips bosses. It uses the same makeshift test as Nighto and Aluru - a scripted fight and record byte +0x20 set - and, because that byte is 0 on Berserker, adds the three Berserker ids by hand.

The list stops there. Two more lone scripted monsters have the byte at 0: the Golem fought in Rikuroa and the lone Puera. A Zenoir kill on either leaves a lit cloud of shards on the stage until the battle ends.

Zenoir kills a monsterphase 0xC, seat HP = 0Scripted and +0x20 = 1?or formation id 77/173/174Body stays wholeZeto, Xain, Cort, BerserkeryesBody shatters+0x1BA ramps to ~1000noLone monster, scripted fight?ctx+0x287, 0x8007BD0D = 0Shards stay lit on stagegrey stamp: Golem, PuerayesShards fade to blackevery random fightno
What a Zenoir kill does to the body. Only the last box is a defect: the boss test cannot see the Golem or the lone Puera.

What the shatter is

The effect is one number per actor, +0x1BA, which the draw copies to the body's +0x7A. While it is non-zero the body is drawn by a separate renderer that treats every triangle on its own: it finds the triangle's centre, spins the triangle about that centre by v × 16 4096ths of a turn, and moves it outward to 1 + v / 128 times its distance from the body's centre. Zenoir raises v by 32 a tick until the body's fade runs out, about 1000 in the capture, where each shard is almost nine times as far out as it started. The same renderer, with other settings, is the field's vanish effect for scripted characters and the way several summon creatures appear and leave.

v = 0the body as modelledv = 128each triangle 1/2 turn, 2x as far outv = 2247/8 turn, 2.75x as far out
The retail formula applied to a toy mesh. Each triangle keeps its shape and turns about its own centre while the whole set spreads out; Zenoir carries this on to v ≈ 1000. Edge-on triangles in a real cast are the X part of the spin.

Step by step

  1. Phase 8 of the cast deals the damage.0x801F76CC (FUN_801DD0AC)
  2. Phase 0xC visits each monster seat whose HP is now 0.0x801F81C8..0x801F81E8
  3. It skips the seat in a scripted fight when +0x20 is set on the targeted victim's record (the same record for every seat, so in a two-monster scripted fight the targeted one decides for both).0x801F81F0..0x801F8210
  4. It skips the fight when the first formation id is 77, 173 or 174 (the Berserkers).0x801F8218..0x801F8230
  5. Otherwise it adds the step to +0x1BA; the battle draw copies it to the body and routes the body through the shatter renderer.0x801F8254, 0x800480A4, 0x80048EAC
  6. A killed monster fades to black and stops being drawn - unless it is the lone monster of a scripted fight, which is stamped grey and drawn on.0x800481D4..0x800482DC

How it was measured

Captured under PCSX-Redux on the USA disc from a Rim Elm battle with Vahn alone: the formation row was rewritten as the battle loaded, Zenoir was injected into Vahn's queued action and the target's HP set to 1. Every fight here is forced and every cast injected.

Forced fightScriptedWhat happened
Queen Bee + Killer BeenoBoth bodies shatter; the shards fade to black and vanish
Golem alonenoShatters and fades to black
Golem aloneyesShatters; its colour reads 0x00808080 (the grey stamp) from the first shattered frame and the shards stay lit over the stage until the victory camera
Berserker aloneyesThe list test runs every tick and the store never does; the body stays whole
Berserker alone, id hidden from the list testyesShatters into a lit cloud of green and purple shards

Every lone scripted fight

IdMonster+0x20Body after a Zenoir kill
19, 20Red / Black Piura (tutorial)0would shatter and stay; Zenoir cannot be cast there
77, 173, 174Berserker0whole (the hand list)
128Golem (Rikuroa)0shards stay lit
156Puera (lone row)0shards stay lit (predicted, not run)
othersZeto, Xain, Cort, Songi and the other bosses1whole

What else it affects

Only Zenoir and Jedo shatter monsters. Jedo shatters every monster it kills in a random fight and skips the effect in every scripted fight, so it never meets the problem. The other summons that use the renderer apply it to their own creature. Scripted fights with two monsters (Viguro, Kemaro, the paired Puera) fade normally, because the grey stamp needs a lone monster.

Addresses

AddressImageWhatEvidence
0x801F81C8..0x801F8264PROT 0908Shatter loop: dead seats, boss test, Berserker list, +0x1BA adddisassembly, capture
0x800480A4SCUS_942.54+0x1BA copied to the body's +0x7Adisassembly
0x80048EAC..0x80048FC0SCUS_942.54Non-zero +0x7A routes the body to the shatter rendererdisassembly, capture
0x80029888, 0x80029DD8SCUS_942.54Shatter renderer: per-triangle spin about the centre and outward pushdisassembly
0x800481D4..0x800482DCSCUS_942.54Grey stamp for the lone monster of a scripted fightdisassembly, capture
0x801F86E0..0x801F874CPROT 0931Jedo's shatter loop, skipped in scripted fightsdisassembly

In the port

The port draws the shatter with the same per-triangle formula on both hosts, and runs Zenoir's loop with the boss test and the Berserker list, so a Zenoir kill on the scripted lone Golem leaves the same lit shards. The field vanish uses the same renderer.

Verifying it

Kill the Rikuroa Golem with Zenoir: the shards should still be on screen, lit, after the fire has cleared. Break on 0x801F8254 to watch the store. In a Berserker fight the same breakpoint never fires.

Open questions

  • The lone Puera is predicted from the same code and record; it was not run.
  • The Golem capture is a forced formation in Rim Elm with an injected cast, not the real Rikuroa fight.

Background: the renderer page (the shatter renderer), the cast-module page.

Evil Fly and other boss-byte monsters can be killed outright

Reported as
Evil Fly has the non-insta-killable flag set, but can still be killed outright because it is not a scripted fight (old forum note)
Cause
The texture byte is only read in scripted fights; eleven monsters that have it, Lapis among them, are never met in one.
Where
Nighto 0x801F6BF0, Aluru 0x801F6D44: the scripted-fight test comes before the record is read
Affects
Evil Fly, Death Wings, Demon Fly, the three Gizams, the three Giliums, the 999-HP Tetsu, Lapis
Status
Explained - read from the code; not yet cast on an Evil Fly in a running game

The note is right. Nighto and Aluru only look at record byte +0x20 after they have checked that the fight is scripted. In any other fight they roll the kill chance without looking at the monster at all. The game has no "boss" field, and immunity needs both halves:

Record +0x20 = 1Record +0x20 = 0
Scripted fightresists (Zeto, Xain, Cort, …)rolls: Berserker, Golem, Puera, Viguro, Kemaro, the tutorial Piuras
Any other fightrolls - the byte is never read (Evil Fly, Gizam, Gilium, Lapis, …)rolls (every ordinary monster)

The two cells of the bottom row behave the same; the split only shows that the byte makes no difference there.

What the byte is. Record +0x20 is a graphics setting: when it is 1 the model upload doubles the width of the monster's texture in video memory. Large detailed models need the wide page, which is why most big bosses carry it, but so do a few ordinary monsters with wide textures. It is set on 37 of 186 monster records. Apart from the upload, exactly three places on the disc read it, all borrowing it as a "big boss" test: Nighto's and Aluru's resist checks and Zenoir's shatter check. No stat, AI or reward code reads it.

The monsters that have the byte and are never in a scripted fight

IdMonsterHPWhere it is met
1, 2, 3Evil Fly, Death Wings, Demon Fly82, 209, 324random encounters
55, 56, 57Gizam (three versions)266, 567, 2192random encounters; the strongest also on unflagged event rows
79Tetsu999unflagged event rows only
159, 160, 161Gilium (three versions)2880, 3280, 3542random encounters and unflagged event rows
167Lapis64800one event row with a zero header byte (Lapis and Cort)

Counted by joining each monster record's byte with a census of every formation row in every scene, split by the row's header byte.

Verifying it

Cast a level-9 Aluru on an Evil Fly in a random encounter: it lands every time and the fly dies. A breakpoint on 0x801F6D44 shows the scripted-fight flag at 0, and execution never reaches the record read at 0x801F6D70.

Open questions

  • Not yet cast on an Evil Fly in a running game; the outcome is read from the code.