Final Fantasy Wiki
Relm: I couldn't miss the chance to practice my drawing!
This article is in need of a few pictures. Perhaps you can help by uploading a picture of the different damage output with and without a patch that fixes the bug.

There exists a bug in the damage formulas of certain weapons in Final Fantasy V.

The agility stat is meant to play a major role in the damage formula of most weapons belonging to certain classes (bells, boomerangs, bows, knives, short swords, throwables, and whips). However, only the damage formulas of bells, throwables, and the Chicken Knife implement that properly.

While some have speculated that this behavior is intentional, possibly to prevent certain weapon classes (e.g., knives) from becoming overly powerful, the consequences and the technical explanation suggest that it is more likely the result of an accidental oversight.

The bug can be fixed in the SNES version of the game by modding the ROM (preferably after making a safety back-up).

The damage formula[]

The general damage algorithm of Final Fantasy V can be written as a damage formula:

where:

With limited exceptions, boomerangs, bows, knives, short swords, and whips share the same sub-formulas for A, D, and M:

where // represents the integer division operation (i.e. taking the quotient ignoring the remainder), and mod represents the modulo operation (i.e. taking the remainder ignoring the quotient).

The formula for M is bifurcated: the first one represents the original intention of the developers, and the second one represents the actual implementation, including the bug. In that regard, the mod 256 reduction is purely a mathematical tool to represent the bug. The technicals details of the likely oversight causing the bug are explained in detail here.

The bug[]

As Level and Agility are capped at 99 for the purpose of the damage formula, the upper limit of their product is 9801, which is in the [256, 65535] range. Every unsigned number in that range:

  • Cannot be represented in binary using fewer than 2 bytes.
  • Can always be represented in binary using 2 bytes.

Therefore, to consistently be able to represent in binary all possible products, 2 bytes (16 bits) are needed.

The bug consists in only the value encoded by the least significant byte of being used in the damage formula, while the most significant byte is ignored. Mathematically, this is equivalent to reducing .

Since, by definition, any value modulo 256 must be in the [0, 255] range, when dividing it by 128, the only two possible integer quotients are 0 and 1. Therefore, the impact of the agility stat on the multiplier formula (and, thus, on the overall damage formula) is very limited.

Consequences[]

The primary consequence of the bug is that the agility stat can only add 0 or 1 to the damage formula multiplier. Therefore, the contribution of the agility stat to the final damage becomes almost negligible. If the bug was not there, the maximum additive impact on the multiplier would be , which makes the actual impact of the agility stat even more underwhelming.

Additionally, it is possible for a character attacking with an affected weapon to deal less damage after a level up, everything else being equal. That is the case whenever M decreases after a level up (i.e. from Level to Level + 1):

The easiest scenario in which the above inequality is satisfied (due to the agility bug) is when:

In other words, the additive contribution of the Strength stat to the multipler does not increase, and the bugged contribution of the Agility stat decreases from 1 to 0. For instance, considering a character with 55 strength and 50 agility before and after leveling up from 10 to 11:

Exceptions[]

Certain bells and the shuriken-type throwables also feature the agility stat in the multiplier M of their damage formula. However, in those cases, both bytes of are correctly used. Additionally, within the weapon classes that are affected by the bug, there are some exceptions:

  • The Twin Lance and the Man-Eater use the spears' damage formula, which does not feature agility.
  • The Magic Bow deals no damage by design. As such, it does not use a damage formula.
  • The Chicken Knife uses a unique damage formula, which does not feature the bug in the multiplier. As such, it is considerably stronger than all other knives, and deals more damage than any other weapon in the game in most practical scenarios.

In-depth analysis[]

The SNES uses a 65C816-derived Ricoh 5A22 CPU, which, among other things, is little-endian, and features a variable-width accumulator register for basic arithmetic.

In the affected weapons' damage formulas (all of which share the same code for the computation of the multiplier M), after performing the multiplication, the result is stored in a temporary 16-bit memory location (virtual addresses $26 and $27)[note 1] for further use in arithmetic operations. Due to little-endianness, the least significant byte is stored at $26 and the most significant byte is stored at $27.

The instruction LDA loads data from memory into the accumulator. By default, if the accumulator is in 8-bit mode, it only loads a single byte. If the M flag[note 2] in the processor status register is cleared (with REP #$20), the accumulator switches to 16-bit mode and LDA will then load two bytes.

In the relevant routine (ROM addresses [0x28113 ~ 0x28116]), the game code executes these instructions in the wrong order:

Addresses Hex bytes Operation Meaning
0x28113 ~ 0x28114 A5 26 LDA $26 Loads the value (8-bit by default) from memory location $26 into the accumulator.
0x28115 ~ 0x28116 C2 20 REP #$20 Clears the M flag, switching the accumulator to 16-bit mode.

By loading the value first while still in 8-bit mode, only the least significant byte of the product is kept. The high byte is lost, effectively reducing to .

The correct sequence is:

Addresses Hex bytes Operation Meaning
0x28113 ~ 0x28114 C2 20 REP #$20 Clears the M flag, switching the accumulator to 16-bit mode.
0x28115 ~ 0x28116 A5 26 LDA $26 Loads the full 16-bit value from memory locations $26/$27 into the accumulator.
  1. The dollar sign ($) is not a currency symbol here; it is standard syntax in some assembly language notations to denote an address.
  2. The M flag and the M multiplier of damage formulas are different concepts that happen to share them same name by coincidence.

Fix[]

To fix the bug in the SNES version (both the original, and the RPGe translated version), the rom needs to be patched (e.g. with a hex editor). As mentioned in the previous section, the relevant address range is [0x28113 ~ 0x28116].

Address Original value Patched value
0x28113 A5 C2
0x28114 26 20
0x28115 C2 A5
0x28116 20 26

The player can quickly test the patch by comparing the damage of the Assassin's Dagger to the damage of the Enhancer against enemies with low defense before and after patching the rom.