[FE7] Charting the Battle Stat Computations

At the start, r0 is a pointer to the attacker’s battle data, and r1 the defender. Note they switch when calculating the other side’s round of battle.
Also note that the unit data automatically calculates the stats with boosts(from equipped item and such) so when I say str, I really mean boosted str.

080289B0 B530     push    {r4,r5,r14}
080289B2 1C04     mov     r4,r0
080289B4 1C0D     mov     r5,r1

No comment here. just backing up the pointers to use later.

080289B6 F000F86F bl      #0x8028A98
  • Loads weapon ability 1.
  • check it for the bit in 0x40(magic damage)
  • if so, loads battle defense in the attacker’s battle data and defender’s resistance
  • if not, checks for weapon ability 1 against #0x2(Magic)
  • if so, do the same thing as above
  • if not, loads battle defense in the attacker’s battle data and defender’s defense stat
  • Adds the appropriate def or res to whatever was already in the defense slot(likely from terrain or something?)
  • Stores defense.

-tl;dr Calculates defense from stats

080289BA 1C20     mov     r0,r4
080289BC 1C29     mov     r1,r5
080289BE F000F8A7 bl      #0x8028B10

-tl;dr Calculates power based on (precalculated) weapon triangle, effectiveness, and strength.

080289C2 1C20     mov     r0,r4
080289C4 F000F8D0 bl      #0x8028B68
  • Loads equipped weapon’s weight
  • Loads attacker’s con
  • Subtracts them, compares to 0.
  • if weight is less than or equal to con, then load speed and subtract 0 from it
  • if weight is greater than con, then load speed and subtract the difference from it
  • Load the AS halfword from the attacker data and store it there.
  • If what we just stored is less than 0, then store 0 instead.

-tl;dr Calculates attack speed

080289C8 1C20     mov     r0,r4
080289CA F000F8E9 bl      #0x8028BA0
  • Loads the weapon’s hit%
  • Loads attacker’s skill
  • Multiplies the skill by 2 (left shifts by 1, more technically)
  • Adds them
  • Loads attacker’s luk
  • Gets the sign bit of the luk(lsign byte; luk is signed) and adds it to the luk.
  • This is for proper rounding/flooring for when luk is negative, but this isn’t necessary, I think.
  • Divides by two.
  • Adds that to what we had before
  • Loads the weapon triangle hit bonus and adds that on
  • Stores the hit into the hit word in battle data memory.
  • Loads 0x202BBF8 (Is this tactician data?)
  • Loads its 0x2B byte(I think this is the tactician stars)
  • Presumably does some more checks? It checks for not being an enemy/other unit, and it checks for something else(maybe prevent bonuses in the link arena?) and then the affinities matching. I think the branch at 080BFC88 loads the number of tactician stars(Since there’s a check for >0xA stars right below it)
  • Add on the tactician bonus and stores it back into the hit slot in battle data.
  • Interesting, the bl #0x8028194 in the subroutine seems to do an inventory scan and has a HARD CODED CHECK for item number 8A – the emblem seal. Gives 10 hit if it finds it, obviously, but it’s interesting that it’s a hard coded check for that item.

-tl;dr Calculates hit based on base hit, skl and luk, and tactician stars and emblem seal.

080289CE 1C20     mov     r0,r4
080289D0 F000F93E bl      #0x8028C50
  • The single mov hints to me that this is going to be based on the attacker only.
  • Loads the AS
  • Multiplies it by two
  • Loads the byte 7 before AS(which is undocumented)(Terrain avoid bonus, or support?)(Seeing how terrain was calculated already in the defense part, I think that’s done somewhere else.)
  • Adds it to what the AS*2
  • Loads and adds on luk
  • Stores it in avoid
  • Gets tactician star bonus(I think?) and adds it on if applicable.
  • Gets Emblem seal bonus like from above.
  • If negative, store 0 as avoid.

-tl;dr Calculates avoid based on as, luk, support bonus, tactician stars, and emblem seal.

080289D4 1C20     mov     r0,r4
080289D6 F000F993 bl      #0x8028D00
  • Gets the equipped weapon’s base critical.
  • Gets the attacker’s skill and does a signed divide by two
  • Adds them
  • Stores it into the critical slot of the unit data
  • Loads the character and class of the unit, and loads character abilities.
  • Checks character ability 1 against 0x40, i.e. “Critical +15”
  • Gives 0xF bonus if it is, and stores the cit back.
  • All in all, a rather efficiently written routine.

-tl;dr Calculates crit based on base crit, skl, and ability 1 being Crit+15.

080289DA 1C20     mov     r0,r4
080289DC F000F9AE bl      #0x8028D3C
  • Literally the shortest routine ever.
  • Loads luk and stores it in as dodge(crit avoid).

-tl;dr Go read it it’s like two lines long. Calculates dodge based on Luk.

080289E0 1C20     mov     r0,r4
080289E2 1C29     mov     r1,r5
080289E4 F000F81E bl      #0x8028A24
  • Loads halfword at 0x203A3D8 (It seems to tell what condition’s we’re calling under… 0x04 = status screen, 0x02 = battle preview, 0x01 = in battle) (Using a Vulnerary, Heal staves = 0x80)(In a no-animation battle = 0x81) (I think the 0x80 bit represents having a map animation… the 0x01 bit is battle)

  • ANDs it with 0x20 (I… don’t know what circumstances this would be called?)

  • If the result is nonzero, then load byte at 0x202BC0D(actually loads 0x202BBF8+0x15, but same thing)(Something in the tactician data? Seems to be 0?

  • I’ll finish that line of thought some other time since I don’t know when it applies.

  • It the result from before is zero(usually, I think)

  • This is a pain to track down, but one of its branch calls retrieves the supports data pointer, so I assume it calculates bonuses from supports.

-tl;dr I’m not sure, but I think this just calculates support bonuses.

080289E8 1C20     mov     r0,r4
080289EA F000FA45 bl      #0x8028E78
  • Get equipped item’s weapon type (e.g. sword, spear, … item, dragon stone, etc)
    • If it’s 0x7 and below(i.e. the 4 melee types and 4 spell types) then leads the weapon rank and checks if you’re S-rank
    • If so, loads and adds 5 onto hit and crit.

-tl;dr S Rank bonus

080289EE 1C20     mov     r0,r4
080289F0 F000FA60 bl      #0x8028EB4
  • Loads the unit’s status effect.

  • Checks for and applies one of Ninis’s Grace, Fila’s Might, etc.

  • Gets the appropriate stat, adds 0xA and stores it back and returns.

    080289F4 BC30 pop {r4,r5}
    080289F6 BC01 pop {r0}
    080289F8 4700 bx r0

  • Returns to caller. We’re done!

You’ve got this pretty well documented, I have to say! I’ll throw in some notes of my own.

The “Rough ASM notes” mostly contain the offsets of the routines you’ve described in FE8 and FE7; the others contain more information about a few specific routines. Hopefully it’ll help you and others.

I take it the “experience and autoleveling” is for FE8?

Edit: Them FE8 docs are going to make one @AlfredKamon happy when I’m much abler to port things over to FE8. Thanks!

Yep! Take note that the xp and autoleveling routine functions a little differently than their FE7 counterparts.

For example, while in FE7 the Mode Coefficient check is done based on your difficulty level, in FE8 it’s done based on your ‘mode’, i.e Eirika route/Ephraim route/ Prologue-C8.

There’s also the hardcoded checks for classes like Gorgon Eggs and Phantoms that FE7 doesn’t do.

Indeed! :smiley:

It’s good to see you’re collaborating. Venno sure knows a lot of FE8 stuff.

There’s more! This is the following routine that is called when you target an enemy (So I imagine this loads the enemy’s stats then calculated final hit/crit, etc):

Will analyze later.

r0 is the pointer to the attacker’s data, r1 is the pointer to the defender’s data.

080289FC B530     push    {r4,r5,r14}
080289FE 1C04     mov     r4,r0
08028A00 1C0D     mov     r5,r1
  • Storing the pointers as r0-r3 get clobbered.

    08028A02 F000F9A1 bl #0x8028D48

  • Gets hit and avoid of the attacker and defender and subtracts them.

  • Stores that back as the “Battle Hit” byte in the attacker’s unit data.

  • Converts the hit that it stored into a signed halfword and compares it to 100 and 0, storing each if it is greater or less than each, respectively.

-tl;dr Subtracts hit and avoid to calculate true hit.

08028A06 1C20     mov     r0,r4
08028A08 1C29     mov     r1,r5
08028A0A F000F9B3 bl      #0x8028D74
  • Same as above, but for final crit.
  • Applies Tactician Star bonus to crit.

-tl;dr Calculates final crit from crit, crit avoid and tactician stars.

08028A0E 1C20     mov     r0,r4
08028A10 1C29     mov     r1,r5
08028A12 F000FA03 bl      #0x8028E1C
  • Loads class and character ability to check for Lethality.
  • if not, stores 0 into the 0x6C halfword of the unit data. (Which Nintenlord has labeled “2: 0x32 if has morph skill, 0x19 if has morph and enemy is boss, 0 if has morph and enemy has no exp ability”; Maybe one of us has our skill mixed ut; it’s Lethality not morph, I’m pretty sure. It would be morph if the code was lsl r0, #12d, but it’s lsl r0, #0x12(in hex))
  • if so, stores 0x32(This is 50%, note) instead. Then checks the class/character of defender for the “Boss” ability. If so, stores 0x19(25%) instead. Then it loads and checks for the “Opponent doesn’t gain experience” ability and if they have that, store 0 instead(as if you don’t have lethality)

-tl;dr Handles lethality and gives it reductions or cancels it depending on the opponent’s ability.

08028A16 1C20     mov     r0,r4
08028A18 1C29     mov     r1,r5
08028A1A F000FA67 bl      #0x8028EEC
  • Checks Weapon Ability 1(From battle data; I assume there’s another check that sets this to 1 if the Light Brand is attacking indirectly/ 0 for directly) for “Magic Damage”. If so, does a hard coded check against weapon id’s <0x10(skips code) and <=0x11(Light brand and Rune sword) and !=0x99(Wind Sword)(skips code).
  • Divides the strength by two and subtracts that from the “Attack” byte in unit data. Stores it back.
  • Stores 0 into “Crit” and “Battle Crit”
  • If not magic attack, gets the weapon effect byte of the weapon. Checks for “Halves HP” effect.
  • If so, does the obvious thing and loads the defender’s attack and stores half of it as the attack. If the might is 0, then it is set to 1. Stores 0 into the defender’s Defense halfword and into the Crit/Battle Crit of attacker.
  • If not Halves HP, loads weapon ability and checks for “Negate Target Defenses” ability.
  • If so, does the expected thing and sets 0 as the defender’s defense.

-tl;dr A ton of checks. Applies the half strength/0crit for the magic swords, the half hp/0crit for eclipse, and the 0def for Luna.

08028A1E BC30     pop     {r4,r5}
08028A20 BC01     pop     {r0}
08028A22 4700     bx      r0
  • Returns

This seems to be the routine orchestrating all of this; it calls both the routines I have analyzed.
(Don’t worry; later I’ll reorganize the OP to be more coherent)

@params: r0 = pointer to data in RAM of the attacker, r1 = &the defender. NOT battle data.
@r8 = Attacker’s deployment data, r9= defender’s

08028424 B570     push    {r4-r6,r14}
  • For clobbers.

    08028426 1C0E mov r6,r1
    08028428 4D11 ldr r5,=#0x203A3F0
    0802842A 4C12 ldr r4,=#0x203A470
    0802842C 1C28 mov r0,r5
    0802842E 1C21 mov r1,r4

  • So let’s review: r6(stored for later) is the defender data in ram. r0 is &attacker battle data and r1 is &defender battle data.

    08028430 F000FABE bl #0x80289B0

  • The routine analyzed before. Does most of the calculations.

    08028434 1C20 mov r0,r4
    08028436 1C29 mov r1,r5
    08028438 F000FABA bl #0x80289B0

  • Ditto, but for the defender.

    0802843C 1C28 mov r0,r5
    0802843E 1C21 mov r1,r4
    08028440 F000FADC bl #0x80289FC

  • The other routine analyzed before. Calculated final hit, crit and applies various weapon effects.

    08028444 1C20 mov r0,r4
    08028446 1C29 mov r1,r5
    08028448 F000FAD8 bl #0x80289FC

  • Ditto for defender.

    0802844C 2E00 cmp r6,#0x0

  • Is there no defender? (Snag seems to pass 0x0 as the defender)

    0802844E D101 bne #0x8028454

  • If not snag, go to 8028454(which is just skipping the next branch call)

    08028450 F001FF42 bl #0x802A2D8

  • Stores 100 as the battle hit rate, 0 as the battle crit rate.

  • Stores 0xFF as the attack speed of the defender/snag. Since y’know, you can’t double snags.

  • Stores the snag’s (current) HP into the 0x72 byte of the battle data(Nintenlord has it marked as “???”)

  • Stores 0 into weapon triangle hit and damage effects. (This is after they’ve been factored into calculations, and snags don’t have weapons anyway – useless?)

-tl;dr Snag stuff.

08028454 4908     ldr     r1,=#0x203A3D8
  • Some data stored before the attacker? I don’t know what this is.

    08028456 2001 mov r0,#0x1
    08028458 8809 ldrh r1,[r1]
    0802845A 4008 and r0,r1
    0802845C 2800 cmp r0,#0x0
    0802845E D00F beq #0x8028480

  • This indicates it’s some kind of flags… if the LO bit is set, then do the following, if not, then skip down to 0x08028480.

  • (untested thought: maybe an arena?)

    08028460 4806 ldr r0,=#0x203A85C
    08028462 6980 ldr r0,[r0,#0x18]
    08028464 2800 cmp r0,#0x0
    08028466 D00B beq #0x8028480
    08028468 F002FA1E bl #0x802A8A8

  • Seems to load something associated with the last selected unit. I can’t figure out what it is exactly, though.

  • If it’s zero, then skip over until below the next two blocks.

  • The bl call:

  • Loads the value again

  • I think it’s supposed to be a pointer. It loads the 2nd byte after the value pointed to and ands it with 0x80.

  • Loads the battle buffer pointer… if the post-anding value is nonzero,

  • Copies the data at the place specified by the pointer and stores it into 0x203A4F0(start of battle buffer). Continues loading from the location and incrementing and repeating until it hits something with the top bit unset. (yeah idk what this is doing either)… Finding the next open slot in the battle buffer?

  • Stores the location of the battle buffer into the place where the pointer to the battle buffer goes.

    0802846C E00A b #0x8028484

  • Skips to the end of the routine.

    0802846E 0000 lsl r0,r0,#0x0
    08028470 A3F0 add r3,=#0x8028834
    08028472 0203 lsl r3,r0,#0x8
    08028474 A470 add r4,=#0x8028638
    08028476 0203 lsl r3,r0,#0x8
    08028478 A3D8 add r3,=#0x80287DC
    0802847A 0203 lsl r3,r0,#0x8
    0802847C A85C add r0,sp,#0x170
    0802847E 0203 lsl r3,r0,#0x8

  • Discourse is opposing me making that block code for some reason, so sorry for that. Anyway…

  • I think this is just a literal pool.

    08028480 F000FD96 bl #0x8028FB0

  • Allocates 2 words of data on the stack…

  • Calls 0x8028F84

  • Loads default battle buffer location and Battle Buffer Pointer Location.

  • Stores 0 into the first 7 words of the defender’s battle data.

  • Stores default battle buffer location into the battle buffer pointer location; effectively clears the battle buffer.

  • Moves r13(the stack pointer, I think?) into r0, and 0x4 after the sp into r1

  • Calls 0x8029028

  • Simple here, stores the attacking unit’s pointer(i.e. 0x203A3F0, always) 0x8 deep into the stack(into [r0]), and 0x203A470 0x4 deep into the stack(into [r1]).

  • Loads the battle buffer location.

  • Sets the 0x01 bit of the 3rd byte(unlabeled?)

  • Reloads 0x203A3F0 into r0 and 0x203A470 into r1.

  • Calls 0x80290B8

  • r8 is the attacking unit’s deployment data in ram.(r6 and r9 is the defender’s) Pushes r8.

  • Loads equipped item of enemy and sees if it’s zero(no equip)

    • If so, then it immediately returns 0x0 in r0(false, I assume).
  • If not, then it loads the location of the battle buffer and loads the first halfword.

  • It calls #0x8029114.

    • calls another subroutine that loads Weapon Abilities and checks for brave effect. Sets the flag in the buffer if so. This flag is in the 0x10 position. If so, returns 0x1.
    • lsl’s 0x1 by the return of that routine. So 0x1 for no brave, 0x2 for brave.
  • Compares that return to 0x0 (Always greater than?) Returns 0 immediately if the return value is less than or equal to 0.

  • Stores back the previous battle buffer orr’d with its new self(idk what this is supposed to accomplish since the brave code stored it anyway)

  • Calls 0x80294D8

    • Checks to see if the aggressor unit is the defender. If so marks the “Which side is attacking” bit. (0x8 of the 0x2 byte)
    • Calls 0x8029234, which does: Stores the attacker’s attack and the defender’s defense into the 0x6 and 0x8 halfwords of 0x203A3D8(I’m calling this the Battle Stats Data). Stores the Battle Hit of the attacker into the 0xA halfword and the Battle Crit to the 0xC halfword. The Lethality halfword gets stored into the 0xE byte.
    • Calls 0x8029324, which does: Checks the class and character abilities for either triangle attack. If so, checks the battle stats data’s 0x2(Range) byte against 1.
      • If it is 1(a direct attack), it checks the battle buffer to see if the “End” bit is set. Then if so then takes the first “Unknown” byte from the character’s data and takes its LO Nibble. If the LO nibble is not 0x4(exactly) then checks the 0x20 bit of the first halfword of the Battle Stats Data, then executes a routine and checks the return against 0. I assume this routine checks if you can perform a triangle attack. I’m just going to leave it at this…
    • Calls 0x8029264, which does: Stores 0 into the 0x4 byte of the Battle Stats Data (This will be damage at the end). Loads the 0xA(hit) and calls 0x80285A8 – Which grabs the average of two random numbers and determines if you hit if you’re in an actual battle. (Battle Preview always treats it as a hit). If it gets 0(miss), it writes the 0x2 byte of the battle buffer’s first halfword(miss). If it hits, then it stores the difference of attack and defense as the damage byte(0x4 of the Battle Stats Data). Then it calls 0x802857C, which I’m not analyzing, but I assume does critical checking. (Anyway at this point I have a decent idea of what to rewrite for my modular battle routine)
    • Rolls for a critical. If you get that, it rolls for lethality. If crit but no lethality, it triples damage. If lethality, it sets damage to 127. Also write to the corresponding Battle Buffer bits.

    08028484 BC70 pop {r4-r6}
    08028486 BC01 pop {r0}
    08028488 4700 bx r0

  • Returns. Finally done!

Data from the defender’s data that is cleared between battle and viewing on stat screen:
-Equipped Item After Battle
-Equipped Weapon Abilities
-0x50 is set to FF
-0x5A is set to 0xFF
-Number Battles Fought In

Notably, here is some of what is NOT cleared:
-Stats; Str, skl, spd, etc.
-Might, Hit, Crit, Avoid, etc.
-Weapon triangle bonuses

FE8 Version, credits to @circleseverywhere :

0802A95C B530     push    r4,r5,r14                               ;18 238
0802A95E 1C04     mov     r4,r0                                   ;2  240 @attacker's data in r4
0802A960 1C0D     mov     r5,r1                                   ;2  242 @defender's data in r5
0802A962 F000F86F bl      802AA44h                                ;10 252 @get def/res and store
0802A966 1C20     mov     r0,r4                                   ;2  254
0802A968 1C29     mov     r1,r5                                   ;2  256
0802A96A F000F8A7 bl      802AABCh                                ;10 266 @calculate power (include effectiveness/WTA)
0802A96E 1C20     mov     r0,r4                                   ;2  268
0802A970 F000F900 bl      802AB74h                                ;10 278 @get AS from wt and con
0802A974 1C20     mov     r0,r4                                   ;2  280
0802A976 F000F919 bl      802ABACh                                ;10 290 @calculate hit
0802A97A 1C20     mov     r0,r4                                   ;2  292
0802A97C F000F932 bl      802ABE4h                                ;10 302 @calculate avo
0802A980 1C20     mov     r0,r4                                   ;2  304
0802A982 F000F949 bl      802AC18h                                ;10 314 @calculate crit
0802A986 1C20     mov     r0,r4                                   ;2  316
0802A988 F000F964 bl      802AC54h                                ;10 326 @calculate dodge
0802A98C 1C20     mov     r0,r4                                   ;2  328
0802A98E 1C29     mov     r1,r5                                   ;2  330
0802A990 F000F81E bl      802A9D0h                                ;10 340 @calculate support bonuses...?
0802A994 1C20     mov     r0,r4                                   ;2  342
0802A996 F000F9DD bl      802AD54h                                ;10 352 @S rank bonus
0802A99A 1C20     mov     r0,r4                                   ;2  354
0802A99C F000F9F8 bl      802AD90h                                ;10 364 @status effect
0802A9A0 BC30     pop     r4,r5                                   ;13 377
0802A9A2 BC01     pop     r0                                      ;9  386

This is called by 802a398 for pre-battle
802b278 is the pierce skill check, called by 802b3ec which itself is called by 802b018??