EE/UI: Remove the FPU multiply gamefix

It patched one product, 0.25 * pi, from the correctly-rounded 0x3f490fdb
to the 0x3f490fda the EE's multiplier returns, so Tales of Destiny stops
hanging. That product is one sample of the multiplier's one-ULP deficit,
and eeClampMode 3 models the deficit for every operand pair -- including
the asymmetry the gamefix reproduced by comparing fs and ft against
their own constants, which falls out of a predicate that reads ft alone.
The seven serials carry the clamp mode instead, and iFPU-arm64.cpp's
multiply is a bare Fmul again.

The toggle goes from the Qt, FullscreenUI, Android and iOS front ends,
along with the GameDB schema enum and the harness helper that set it.

Fix_FpuMultiply and the config bit stay: vu_capture's on-disk gamefix
mask is bit-indexed by GamefixId, so dropping ordinal 0 would
reinterpret every capture already recorded. tbl_GamefixNames keeps the
name, so a GameDB that still lists it parses and does nothing.
This commit is contained in:
pstef
2026-08-16 00:11:21 +02:00
parent 94c4fa72ea
commit b0ddecfede
37 changed files with 80 additions and 191 deletions
+14 -14
View File
@@ -2470,8 +2470,8 @@ SCAJ-20181:
SCAJ-20182:
name: "Tales of Destiny"
region: "NTSC-Unk"
gameFixes:
- FpuMulHack
clampModes:
eeClampMode: 3 # Hangs otherwise: it needs the EE multiplier's one-ULP deficit, which the single-precision tier does not model.
SCAJ-20183:
name: "ワイルドアームズ ザ フィフスヴァンガード"
name-sort: "わいるどあーむず ざ ふぃふすゔぁんがーど"
@@ -2517,8 +2517,8 @@ SCAJ-20192:
SCAJ-20193:
name: "Tales of Destiny [Director's Cut] [Premium Box]"
region: "NTSC-C-J"
gameFixes:
- FpuMulHack
clampModes:
eeClampMode: 3 # Hangs otherwise: it needs the EE multiplier's one-ULP deficit, which the single-precision tier does not model.
SCAJ-20194:
name: "Minna no Golf 4 [PlayStation2 the Best]"
region: "NTSC-Unk"
@@ -7967,8 +7967,8 @@ SCKA-20100:
name: "테일즈 오브 데스티니" # Undumped on ReDump as of 2025-08-28
name-en: "Tales of Destiny"
region: "NTSC-K"
gameFixes:
- FpuMulHack
clampModes:
eeClampMode: 3 # Hangs otherwise: it needs the EE multiplier's one-ULP deficit, which the single-precision tier does not model.
SCKA-20101:
name: "성검전설 4"
name-en: "Seiken Densetsu 4"
@@ -8069,8 +8069,8 @@ SCKA-20119:
name: "테일즈 오브 데스티니 디렉터즈컷 [Premium Box]" # Undumped on ReDump as of 2025-08-28
name-en: "Tales of Destiny - Director's Cut [Premium Box]"
region: "NTSC-K"
gameFixes:
- FpuMulHack
clampModes:
eeClampMode: 3 # Hangs otherwise: it needs the EE multiplier's one-ULP deficit, which the single-precision tier does not model.
SCKA-20120:
name: "라쳇 & 클랭크 - 공구들고 바캉스" # Undumped on ReDump as of 2025-08-28
name-en: "Ratchet & Clank - Vacation with Tools" # Ratchet & Clank - Size Matters
@@ -61771,8 +61771,8 @@ SLPS-25715:
name-sort: "ているず おぶ ですてぃにー"
name-en: "Tales of Destiny"
region: "NTSC-J"
gameFixes:
- FpuMulHack
clampModes:
eeClampMode: 3 # Hangs otherwise: it needs the EE multiplier's one-ULP deficit, which the single-precision tier does not model.
SLPS-25716:
name: "デジモンセイバーズ アナザーミッション"
name-sort: "でじもんせいばーず あなざーみっしょん"
@@ -62558,8 +62558,8 @@ SLPS-25841:
name-sort: "ているず おぶ ですてぃにー でぃれくたーずかっと [ぷれみあむBOX]"
name-en: "Tales of Destiny [Director's Cut] [Premium Box]"
region: "NTSC-J"
gameFixes:
- FpuMulHack
clampModes:
eeClampMode: 3 # Hangs otherwise: it needs the EE multiplier's one-ULP deficit, which the single-precision tier does not model.
memcardFilters: # Allows import of non-DC Tales of Destiny data.
- "SLPS-25841"
- "SLPS-25842"
@@ -62569,9 +62569,9 @@ SLPS-25842:
name-sort: "ているず おぶ ですてぃにー でぃれくたーずかっと"
name-en: "Tales of Destiny [Director's Cut]"
region: "NTSC-J"
clampModes:
eeClampMode: 3 # Hangs otherwise: it needs the EE multiplier's one-ULP deficit, which the single-precision tier does not model.
compat: 5
gameFixes:
- FpuMulHack
memcardFilters:
- "SLPS-25841"
- "SLPS-25842"
@@ -17,7 +17,6 @@ GameFixSettingsWidget::GameFixSettingsWidget(SettingsWindow* settings_dialog, QW
setupTab(m_ui);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.FpuMulHack, "EmuCore/Gamefixes", "FpuMulHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.GoemonTlbHack, "EmuCore/Gamefixes", "GoemonTlbHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.SoftwareRendererFMVHack, "EmuCore/Gamefixes", "SoftwareRendererFMVHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.SkipMPEGHack, "EmuCore/Gamefixes", "SkipMPEGHack", false);
@@ -36,7 +35,6 @@ GameFixSettingsWidget::GameFixSettingsWidget(SettingsWindow* settings_dialog, QW
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.XgKickHack, "EmuCore/Gamefixes", "XgKickHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.BlitInternalFPSHack, "EmuCore/Gamefixes", "BlitInternalFPSHack", false);
dialog()->registerWidgetHelp(m_ui.FpuMulHack, tr("FPU Multiply Hack"), tr("Unchecked"), tr("For Tales of Destiny."));
dialog()->registerWidgetHelp(m_ui.GoemonTlbHack, tr("Preload TLB Hack"), tr("Unchecked"), tr("To avoid TLB miss on Goemon."));
dialog()->registerWidgetHelp(m_ui.SoftwareRendererFMVHack, tr("Use Software Renderer For FMVs"), tr("Unchecked"), tr("Needed for some games with complex FMV rendering."));
dialog()->registerWidgetHelp(m_ui.SkipMPEGHack, tr("Skip MPEG Hack"), tr("Unchecked"), tr("Skips videos/FMVs in games to avoid game hanging/freezes."));
@@ -17,13 +17,6 @@
<string>Game Fixes</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="FpuMulHack">
<property name="text">
<string extracomment="FPU = Floating Point Unit. A part of the PS2's CPU. Do not translate.\nMultiply: mathematical term.\nTales of Destiny: a game's name. Leave as-is or use an official translation.">FPU Multiply Hack</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="SoftwareRendererFMVHack">
<property name="text">
@@ -162,7 +155,6 @@
</layout>
</widget>
<tabstops>
<tabstop>FpuMulHack</tabstop>
<tabstop>SoftwareRendererFMVHack</tabstop>
<tabstop>SkipMPEGHack</tabstop>
<tabstop>GoemonTlbHack</tabstop>
+4 -1
View File
@@ -1280,6 +1280,10 @@ struct Pcsx2Config
{
BITFIELD32()
bool
// No reader: eeMulRound (FPU.cpp) and emitDefectiveFmul
// (iFPUd-arm64.cpp) model the multiplier defect this patched one
// product of. The bit stays because its GamefixId indexes
// vu_capture's on-disk gamefix mask.
FpuMulHack : 1, // Tales of Destiny hangs.
GoemonTlbHack : 1, // Gomeon tlb miss hack. The game need to access unmapped virtual address. Instead to handle it as exception, tlb are preloaded at startup
SoftwareRendererFMVHack : 1, // Switches to software renderer for FMVs
@@ -1697,7 +1701,6 @@ namespace EmuFolders
//------------ SPECIAL GAME FIXES!!! ---------------
#define CHECK_VUADDSUBHACK (EmuConfig.Gamefixes.VuAddSubHack) // Special Fix for Tri-ace games, they use an encryption algorithm that requires VU addi opcode to be bit-accurate.
#define CHECK_FPUMULHACK (EmuConfig.Gamefixes.FpuMulHack) // Special Fix for Tales of Destiny hangs.
#define CHECK_XGKICKHACK (EmuConfig.Gamefixes.XgKickHack) // Special Fix for Erementar Gerad, adds more delay to VU XGkick instructions. Corrects the color of some graphics.
#define CHECK_EETIMINGHACK (EmuConfig.Gamefixes.EETimingHack) // Fix all scheduled events to happen in 1 cycle.
#define CHECK_INSTANTDMAHACK (EmuConfig.Gamefixes.InstantDMAHack) // Attempt to finish DMA's instantly, useful for games which rely on cache emulation.
-4
View File
@@ -26,7 +26,6 @@ SERIAL-12345: # !required! Serial number for the game, this is how games are loo
# If you'd like to temporarily disable it, either comment out the line, or remove it!
gameFixes:
- VuAddSubHack
- FpuMulHack
- XGKickHack
- EETimingHack
- SkipMPEGHack
@@ -207,9 +206,6 @@ These values are case-sensitive, so take care. If you incorrectly specify a Gam
### Game Fixes Options
* `FpuMulHack`
* For Tales of Destiny: This fix addresses hanging issues.
* `SoftwareRendererFMVHack`
* Used for complex FMV rendering in certain games.
-1
View File
@@ -101,7 +101,6 @@
"BlitInternalFPSHack",
"DMABusyHack",
"EETimingHack",
"FpuMulHack",
"GIFFIFOHack",
"GoemonTlbHack",
"IbitHack",
-2
View File
@@ -5884,7 +5884,6 @@ void FullscreenUI::DrawGameFixesSettingsPage()
FSUI_CSTR("Game fixes should not be modified unless you are aware of what each option does and the implications of doing so."),
false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_WRENCH, "FPU Multiply Hack"), FSUI_CSTR("For Tales of Destiny."), "EmuCore/Gamefixes", "FpuMulHack", false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_MICROCHIP, "Use Software Renderer For FMVs"),
FSUI_CSTR("Needed for some games with complex FMV rendering."), "EmuCore/Gamefixes", "SoftwareRendererFMVHack", false);
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FORWARD_FAST, "Skip MPEG Hack"), FSUI_CSTR("Skips videos/FMVs in games to avoid game hanging/freezes."),
@@ -6338,7 +6337,6 @@ TRANSLATE_NOOP("FullscreenUI", "Activating game patches can cause unpredictable
TRANSLATE_NOOP("FullscreenUI", "Use patches at your own risk, the ARMSX2 team will provide no support for users who have enabled game patches.");
TRANSLATE_NOOP("FullscreenUI", "Game Fixes");
TRANSLATE_NOOP("FullscreenUI", "Game fixes should not be modified unless you are aware of what each option does and the implications of doing so.");
TRANSLATE_NOOP("FullscreenUI", "For Tales of Destiny.");
TRANSLATE_NOOP("FullscreenUI", "Needed for some games with complex FMV rendering.");
TRANSLATE_NOOP("FullscreenUI", "Skips videos/FMVs in games to avoid game hanging/freezes.");
TRANSLATE_NOOP("FullscreenUI", "To avoid TLB miss on Goemon.");
+4 -42
View File
@@ -619,51 +619,13 @@ static void fpuEmitGuardedAddSub(const a64::VRegister& dst,
_freeNEONreg(tmp);
}
// FpuMulHack (Tales of Destiny Remake gamefix, EmuConfig.Gamefixes.FpuMulHack).
// x86 routes every FPU multiply (MUL/MULA/MADD/MSUB) through FPU_MUL, which
// when the gamefix is on — patches the single specific product 0.25 * π
// (0x3e800000 * 0x40490fdb) from the correctly-rounded 0x3f490fdb to 0x3f490fda
// so the game stops hanging in one late-game room. Emit
// `dst = (hit) ? 0x3f490fda : s*t`; callers clamp/accumulate dst as they normally
// would (the magic value is an ordinary small float, so a following
// fpuClampResult is a no-op). In the default config (gamefix off) this is a bare
// Fmul — zero added cost.
//
// The patched value is not arbitrary: 0x3f490fda is π/4 one ULP low, which is
// what the EE's multiplier actually returns. Its Booth recoding drops one ULP
// when ft's significand has an odd digit pair (ft & 0x2AA) and the exact product
// has no tail below the single ULP — here fs = 2^-2, so the product is exact and
// the deficit reaches the result. The general model reproduces this pair (and
// leaves the swapped operand order alone, exactly as the check below does).
// It is NOT generalized here: this path has no exact product to test a tail
// against, so it would need one built out of single-precision pieces on every
// multiply in every game, against 1 instruction today. Its home is
// emitDefectiveFmul (iFPUd-arm64.cpp), where the double product is already
// there and the tail is 29 bits of it — extending it to this path needs its own
// measured case.
// The EE multiplier's one-ULP deficit is not modelled here: it needs the exact
// product's tail below the single's ULP, which a single-precision multiply has
// already discarded. It lives at emitDefectiveFmul (iFPUd-arm64.cpp), where the
// product is a double and the tail is 29 bits of it.
static void emitFpuMul(const a64::VRegister& dst, const a64::VRegister& s, const a64::VRegister& t)
{
if (!CHECK_FPUMULHACK)
{
armAsm->Fmul(dst, s, t);
return;
}
a64::Label noHack, done;
armAsm->Fmov(RWARG1, s);
armAsm->Fmov(RWARG2, t);
armAsm->Mov(RWSCRATCH, 0x3e800000);
armAsm->Cmp(RWARG1, RWSCRATCH);
armAsm->B(&noHack, a64::ne);
armAsm->Mov(RWSCRATCH, 0x40490fdb);
armAsm->Cmp(RWARG2, RWSCRATCH);
armAsm->B(&noHack, a64::ne);
armAsm->Mov(RWSCRATCH, 0x3f490fda);
armAsm->Fmov(dst, RWSCRATCH);
armAsm->B(&done);
armAsm->Bind(&noHack);
armAsm->Fmul(dst, s, t);
armAsm->Bind(&done);
}
//------------------------------------------------------------------
@@ -512,7 +512,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "GameDB Fixes",
"perf.fix.gifFifo": "GIF FIFO",
@@ -564,7 +564,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "GameDB-Fixes",
"perf.fix.gifFifo": "GIF FIFO",
@@ -512,7 +512,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "GameDB Fixes",
"perf.fix.gifFifo": "GIF FIFO",
@@ -579,7 +579,6 @@
"perf.fix.eeTiming": "زمان بندی EE",
"perf.fix.extraXgkick": "XGKICK اضافی",
"perf.fix.fmvSoftware": "نرم افزار FMV",
"perf.fix.fpuMultiply": "ضرب کردن FPU",
"perf.fix.fullVu0Sync": "همگام سازی کامل VU0",
"perf.fix.gamedbFixes": "رفع GameDB",
"perf.fix.gifFifo": "GIF FIFO",
@@ -512,7 +512,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "GameDB Fixes",
"perf.fix.gifFifo": "GIF FIFO",
@@ -512,7 +512,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "GameDB Fixes",
"perf.fix.gifFifo": "GIF FIFO",
@@ -512,7 +512,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "Correzioni GameDB",
"perf.fix.gifFifo": "GIF FIFO",
@@ -512,7 +512,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "GameDB Fixes",
"perf.fix.gifFifo": "GIF FIFO",
@@ -512,7 +512,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "GameDB Fixes",
"perf.fix.gifFifo": "GIF FIFO",
@@ -565,7 +565,6 @@
"perf.fix.eeTiming": "EE Timeming",
"perf.fix.extraXgkick": "XGKICK Zêdeyî",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Tevahiya VU0 Sync",
"perf.fix.gamedbFixes": "GameDB rast dike",
"perf.fix.gifFifo": "GIF FIFO",
@@ -512,7 +512,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "GameDB Fixes",
"perf.fix.gifFifo": "GIF FIFO",
@@ -511,7 +511,6 @@
"perf.fix.eeTiming": "EE Timing",
"perf.fix.extraXgkick": "Extra XGKICK",
"perf.fix.fmvSoftware": "FMV Software",
"perf.fix.fpuMultiply": "FPU Multiply",
"perf.fix.fullVu0Sync": "Full VU0 Sync",
"perf.fix.gamedbFixes": "Correções GameDB",
"perf.fix.gifFifo": "GIF FIFO",

Some files were not shown because too many files have changed in this diff Show More