Skip to content
Permalink
Browse files
Merge pull request #10043 from JosJuice/true-crime
Raise program exception on floating point exceptions
  • Loading branch information
leoetlino committed Oct 13, 2021
2 parents 4541abd + c250ed0 commit 71051b7
Show file tree
Hide file tree
Showing 38 changed files with 493 additions and 201 deletions.
@@ -0,0 +1,4 @@
# G2CD52, G2CE52, G2CP52, G2CX52 - True Crime: New York City

[Core]
DivByZeroExceptions = True
@@ -0,0 +1,4 @@
# GCOD52, GCOE52, GCOF52, GCOP52 - Call of Duty: Finest Hour

[Core]
DivByZeroExceptions = True
@@ -75,6 +75,8 @@ struct ConfigCache
bool bCPUThread;
bool bJITFollowBranch;
bool bSyncGPUOnSkipIdleHack;
bool bFloatExceptions;
bool bDivideByZeroExceptions;
bool bFPRF;
bool bAccurateNaNs;
bool bMMU;
@@ -109,6 +111,8 @@ void ConfigCache::SaveConfig(const SConfig& config)
bCPUThread = config.bCPUThread;
bJITFollowBranch = config.bJITFollowBranch;
bSyncGPUOnSkipIdleHack = config.bSyncGPUOnSkipIdleHack;
bFloatExceptions = config.bFloatExceptions;
bDivideByZeroExceptions = config.bDivideByZeroExceptions;
bFPRF = config.bFPRF;
bAccurateNaNs = config.bAccurateNaNs;
bDisableICache = config.bDisableICache;
@@ -154,6 +158,8 @@ void ConfigCache::RestoreConfig(SConfig* config)
config->bCPUThread = bCPUThread;
config->bJITFollowBranch = bJITFollowBranch;
config->bSyncGPUOnSkipIdleHack = bSyncGPUOnSkipIdleHack;
config->bFloatExceptions = bFloatExceptions;
config->bDivideByZeroExceptions = bDivideByZeroExceptions;
config->bFPRF = bFPRF;
config->bAccurateNaNs = bAccurateNaNs;
config->bDisableICache = bDisableICache;
@@ -256,6 +262,9 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
core_section->Get("JITFollowBranch", &StartUp.bJITFollowBranch, StartUp.bJITFollowBranch);
core_section->Get("SyncOnSkipIdle", &StartUp.bSyncGPUOnSkipIdleHack,
StartUp.bSyncGPUOnSkipIdleHack);
core_section->Get("FloatExceptions", &StartUp.bFloatExceptions, StartUp.bFloatExceptions);
core_section->Get("DivByZeroExceptions", &StartUp.bDivideByZeroExceptions,
StartUp.bDivideByZeroExceptions);
core_section->Get("FPRF", &StartUp.bFPRF, StartUp.bFPRF);
core_section->Get("AccurateNaNs", &StartUp.bAccurateNaNs, StartUp.bAccurateNaNs);
core_section->Get("DisableICache", &StartUp.bDisableICache, StartUp.bDisableICache);
@@ -370,6 +379,8 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
StartUp.bAccurateNaNs = netplay_settings.m_AccurateNaNs;
StartUp.bDisableICache = netplay_settings.m_DisableICache;
StartUp.bSyncGPUOnSkipIdleHack = netplay_settings.m_SyncOnSkipIdle;
StartUp.bFloatExceptions = netplay_settings.m_FloatExceptions;
StartUp.bDivideByZeroExceptions = netplay_settings.m_DivideByZeroExceptions;
StartUp.bSyncGPU = netplay_settings.m_SyncGPU;
StartUp.iSyncGpuMaxDistance = netplay_settings.m_SyncGpuMaxDistance;
StartUp.iSyncGpuMinDistance = netplay_settings.m_SyncGpuMinDistance;
@@ -86,6 +86,9 @@ const Info<int> MAIN_SYNC_GPU_MIN_DISTANCE{{System::Main, "Core", "SyncGpuMinDis
const Info<float> MAIN_SYNC_GPU_OVERCLOCK{{System::Main, "Core", "SyncGpuOverclock"}, 1.0f};
const Info<bool> MAIN_FAST_DISC_SPEED{{System::Main, "Core", "FastDiscSpeed"}, false};
const Info<bool> MAIN_LOW_DCBZ_HACK{{System::Main, "Core", "LowDCBZHack"}, false};
const Info<bool> MAIN_FLOAT_EXCEPTIONS{{System::Main, "Core", "FloatExceptions"}, false};
const Info<bool> MAIN_DIVIDE_BY_ZERO_EXCEPTIONS{{System::Main, "Core", "DivByZeroExceptions"},
false};
const Info<bool> MAIN_FPRF{{System::Main, "Core", "FPRF"}, false};
const Info<bool> MAIN_ACCURATE_NANS{{System::Main, "Core", "AccurateNaNs"}, false};
const Info<bool> MAIN_DISABLE_ICACHE{{System::Main, "Core", "DisableICache"}, false};
@@ -68,6 +68,8 @@ extern const Info<int> MAIN_SYNC_GPU_MIN_DISTANCE;
extern const Info<float> MAIN_SYNC_GPU_OVERCLOCK;
extern const Info<bool> MAIN_FAST_DISC_SPEED;
extern const Info<bool> MAIN_LOW_DCBZ_HACK;
extern const Info<bool> MAIN_FLOAT_EXCEPTIONS;
extern const Info<bool> MAIN_DIVIDE_BY_ZERO_EXCEPTIONS;
extern const Info<bool> MAIN_FPRF;
extern const Info<bool> MAIN_ACCURATE_NANS;
extern const Info<bool> MAIN_DISABLE_ICACHE;
@@ -69,6 +69,8 @@ class NetPlayConfigLayerLoader final : public Config::ConfigLayerLoader
layer->Set(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES,
m_settings.m_SafeTextureCacheColorSamples);
layer->Set(Config::GFX_PERF_QUERIES_ENABLE, m_settings.m_PerfQueriesEnable);
layer->Set(Config::MAIN_FLOAT_EXCEPTIONS, m_settings.m_FloatExceptions);
layer->Set(Config::MAIN_DIVIDE_BY_ZERO_EXCEPTIONS, m_settings.m_DivideByZeroExceptions);
layer->Set(Config::MAIN_FPRF, m_settings.m_FPRF);
layer->Set(Config::MAIN_ACCURATE_NANS, m_settings.m_AccurateNaNs);
layer->Set(Config::MAIN_DISABLE_ICACHE, m_settings.m_DisableICache);
@@ -213,6 +213,8 @@ void SConfig::SaveCoreSettings(IniFile& ini)
core->Set("SyncGpuMaxDistance", iSyncGpuMaxDistance);
core->Set("SyncGpuMinDistance", iSyncGpuMinDistance);
core->Set("SyncGpuOverclock", fSyncGpuOverclock);
core->Set("FloatExceptions", bFloatExceptions);
core->Set("DivByZeroExceptions", bDivideByZeroExceptions);
core->Set("FPRF", bFPRF);
core->Set("AccurateNaNs", bAccurateNaNs);
core->Set("SelectedLanguage", SelectedLanguage);
@@ -509,6 +511,8 @@ void SConfig::LoadCoreSettings(IniFile& ini)
core->Get("SyncGpuOverclock", &fSyncGpuOverclock, 1.0f);
core->Get("FastDiscSpeed", &bFastDiscSpeed, false);
core->Get("LowDCBZHack", &bLowDCBZHack, false);
core->Get("FloatExceptions", &bFloatExceptions, false);
core->Get("DivByZeroExceptions", &bDivideByZeroExceptions, false);
core->Get("FPRF", &bFPRF, false);
core->Get("AccurateNaNs", &bAccurateNaNs, false);
core->Get("DisableICache", &bDisableICache, false);
@@ -747,6 +751,8 @@ void SConfig::LoadDefaults()
bRunCompareServer = false;
bDSPHLE = true;
bFastmem = true;
bFloatExceptions = false;
bDivideByZeroExceptions = false;
bFPRF = false;
bAccurateNaNs = false;
bDisableICache = false;
@@ -108,6 +108,8 @@ struct SConfig
bool bJITRegisterCacheOff = false;

bool bFastmem;
bool bFloatExceptions = false;
bool bDivideByZeroExceptions = false;
bool bFPRF = false;
bool bAccurateNaNs = false;
bool bDisableICache = false;
@@ -831,6 +831,8 @@ void NetPlayClient::OnStartGame(sf::Packet& packet)
packet >> m_net_settings.m_EFBEmulateFormatChanges;
packet >> m_net_settings.m_SafeTextureCacheColorSamples;
packet >> m_net_settings.m_PerfQueriesEnable;
packet >> m_net_settings.m_FloatExceptions;
packet >> m_net_settings.m_DivideByZeroExceptions;
packet >> m_net_settings.m_FPRF;
packet >> m_net_settings.m_AccurateNaNs;
packet >> m_net_settings.m_DisableICache;
@@ -58,6 +58,8 @@ struct NetSettings
bool m_EFBEmulateFormatChanges;
int m_SafeTextureCacheColorSamples;
bool m_PerfQueriesEnable;
bool m_FloatExceptions;
bool m_DivideByZeroExceptions;
bool m_FPRF;
bool m_AccurateNaNs;
bool m_DisableICache;
@@ -1329,6 +1329,8 @@ bool NetPlayServer::SetupNetSettings()
settings.m_SafeTextureCacheColorSamples =
Config::Get(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES);
settings.m_PerfQueriesEnable = Config::Get(Config::GFX_PERF_QUERIES_ENABLE);
settings.m_FloatExceptions = Config::Get(Config::MAIN_FLOAT_EXCEPTIONS);
settings.m_DivideByZeroExceptions = Config::Get(Config::MAIN_DIVIDE_BY_ZERO_EXCEPTIONS);
settings.m_FPRF = Config::Get(Config::MAIN_FPRF);
settings.m_AccurateNaNs = Config::Get(Config::MAIN_ACCURATE_NANS);
settings.m_DisableICache = Config::Get(Config::MAIN_DISABLE_ICACHE);
@@ -1505,6 +1507,8 @@ bool NetPlayServer::StartGame()
spac << m_settings.m_EFBEmulateFormatChanges;
spac << m_settings.m_SafeTextureCacheColorSamples;
spac << m_settings.m_PerfQueriesEnable;
spac << m_settings.m_FloatExceptions;
spac << m_settings.m_DivideByZeroExceptions;
spac << m_settings.m_FPRF;
spac << m_settings.m_AccurateNaNs;
spac << m_settings.m_DisableICache;
@@ -58,7 +58,7 @@ void CachedInterpreter::Init()
jo.enableBlocklink = false;

m_block_cache.Init();
UpdateMemoryOptions();
UpdateMemoryAndExceptionOptions();

code_block.m_stats = &js.st;
code_block.m_gpa = &js.gpa;
@@ -180,6 +180,17 @@ static bool CheckDSI(u32 data)
return false;
}

static bool CheckProgramException(u32 data)
{
if (PowerPC::ppcState.Exceptions & EXCEPTION_PROGRAM)
{
PowerPC::CheckExceptions();
PowerPC::ppcState.downcount -= data;
return true;
}
return false;
}

static bool CheckBreakpoint(u32 data)
{
PowerPC::CheckBreakPoints();
@@ -267,26 +278,26 @@ void CachedInterpreter::Jit(u32 address)
const bool check_fpu = (op.opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound;
const bool endblock = (op.opinfo->flags & FL_ENDBLOCK) != 0;
const bool memcheck = (op.opinfo->flags & FL_LOADSTORE) && jo.memcheck;
const bool check_program_exception = !endblock && ShouldHandleFPExceptionForInstruction(&op);
const bool idle_loop = op.branchIsIdleLoop;

if (breakpoint)
{
if (breakpoint || check_fpu || endblock || memcheck || check_program_exception)
m_code.emplace_back(WritePC, op.address);

if (breakpoint)
m_code.emplace_back(CheckBreakpoint, js.downcountAmount);
}

if (check_fpu)
{
m_code.emplace_back(WritePC, op.address);
m_code.emplace_back(CheckFPU, js.downcountAmount);
js.firstFPInstructionFound = true;
}

if (endblock || memcheck)
m_code.emplace_back(WritePC, op.address);
m_code.emplace_back(PPCTables::GetInterpreterOp(op.inst), op.inst);
if (memcheck)
m_code.emplace_back(CheckDSI, js.downcountAmount);
if (check_program_exception)
m_code.emplace_back(CheckProgramException, js.downcountAmount);
if (idle_loop)
m_code.emplace_back(CheckIdle, js.blockStart);
if (endblock)
@@ -316,5 +327,5 @@ void CachedInterpreter::ClearCache()
{
m_code.clear();
m_block_cache.Clear();
UpdateMemoryOptions();
UpdateMemoryAndExceptionOptions();
}
@@ -419,11 +419,17 @@ enum FPSCRExceptionFlag : u32
FPSCR_VXSQRT = 1U << (31 - 22),
FPSCR_VXCVI = 1U << (31 - 23),
FPSCR_VE = 1U << (31 - 24),
FPSCR_OE = 1U << (31 - 25),
FPSCR_UE = 1U << (31 - 26),
FPSCR_ZE = 1U << (31 - 27),
FPSCR_XE = 1U << (31 - 28),

FPSCR_VX_ANY = FPSCR_VXSNAN | FPSCR_VXISI | FPSCR_VXIDI | FPSCR_VXZDZ | FPSCR_VXIMZ | FPSCR_VXVC |
FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI,

FPSCR_ANY_X = FPSCR_OX | FPSCR_UX | FPSCR_ZX | FPSCR_XX | FPSCR_VX_ANY,

FPSCR_ANY_E = FPSCR_VE | FPSCR_OE | FPSCR_UE | FPSCR_ZE | FPSCR_XE,
};

// Floating Point Status and Control Register
@@ -7,6 +7,14 @@
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/PowerPC.h"

enum class ProgramExceptionCause : u32
{
FloatingPoint = 1 << (31 - 11),
IllegalInstruction = 1 << (31 - 12),
PrivilegedInstruction = 1 << (31 - 13),
Trap = 1 << (31 - 14),
};

inline void GenerateAlignmentException(u32 address)
{
PowerPC::ppcState.Exceptions |= EXCEPTION_ALIGNMENT;
@@ -19,7 +27,8 @@ inline void GenerateDSIException(u32 address)
PowerPC::ppcState.spr[SPR_DAR] = address;
}

inline void GenerateProgramException()
inline void GenerateProgramException(ProgramExceptionCause cause)
{
PowerPC::ppcState.Exceptions |= EXCEPTION_PROGRAM;
PowerPC::ppcState.spr[SPR_SRR1] = static_cast<u32>(cause);
}
@@ -180,7 +180,7 @@ int Interpreter::SingleStepInner()
{
if (IsInvalidPairedSingleExecution(m_prev_inst))
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::IllegalInstruction);
CheckExceptions();
}
else if (MSR.FP)
@@ -101,7 +101,7 @@ void Interpreter::rfi(UGeckoInstruction inst)
{
if (MSR.PR)
{
GenerateProgramException();
GenerateProgramException(ProgramExceptionCause::PrivilegedInstruction);
return;
}

@@ -11,6 +11,7 @@
#include "Common/CommonTypes.h"
#include "Common/FloatUtils.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/Interpreter/ExceptionUtils.h"
#include "Core/PowerPC/PowerPC.h"

constexpr double PPC_NAN = std::numeric_limits<double>::quiet_NaN();
@@ -24,6 +25,20 @@ enum class FPCC
FU = 1, // ?
};

inline void CheckFPExceptions(UReg_FPSCR fpscr)
{
if (fpscr.FEX && (MSR.FE0 || MSR.FE1))
GenerateProgramException(ProgramExceptionCause::FloatingPoint);
}

inline void UpdateFPExceptionSummary(UReg_FPSCR* fpscr)
{
fpscr->VX = (fpscr->Hex & FPSCR_VX_ANY) != 0;
fpscr->FEX = ((fpscr->Hex >> 22) & (fpscr->Hex & FPSCR_ANY_E)) != 0;

CheckFPExceptions(*fpscr);
}

inline void SetFPException(UReg_FPSCR* fpscr, u32 mask)
{
if ((fpscr->Hex & mask) != mask)
@@ -32,7 +47,7 @@ inline void SetFPException(UReg_FPSCR* fpscr, u32 mask)
}

fpscr->Hex |= mask;
fpscr->VX = (fpscr->Hex & FPSCR_VX_ANY) != 0;
UpdateFPExceptionSummary(fpscr);
}

inline float ForceSingle(const UReg_FPSCR& fpscr, double value)
@@ -123,7 +138,15 @@ inline FPResult NI_div(UReg_FPSCR* fpscr, double a, double b)
{
FPResult result{a / b};

if (std::isnan(result.value))
if (std::isinf(result.value))
{
if (b == 0.0)
{
result.SetException(fpscr, FPSCR_ZX);
return result;
}
}
else if (std::isnan(result.value))
{
if (Common::IsSNAN(a) || Common::IsSNAN(b))
result.SetException(fpscr, FPSCR_VXSNAN);
@@ -142,20 +165,9 @@ inline FPResult NI_div(UReg_FPSCR* fpscr, double a, double b)
}

if (b == 0.0)
{
if (a == 0.0)
{
result.SetException(fpscr, FPSCR_VXZDZ);
}
else
{
result.SetException(fpscr, FPSCR_ZX);
}
}
result.SetException(fpscr, FPSCR_VXZDZ);
else if (std::isinf(a) && std::isinf(b))
{
result.SetException(fpscr, FPSCR_VXIDI);
}

result.value = PPC_NAN;
return result;
@@ -6,6 +6,7 @@
#include "Common/BitUtils.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Core/PowerPC/Interpreter/ExceptionUtils.h"
#include "Core/PowerPC/PowerPC.h"

void Interpreter::Helper_UpdateCR0(u32 value)
@@ -131,7 +132,7 @@ void Interpreter::twi(UGeckoInstruction inst)
if ((a < b && (TO & 0x10) != 0) || (a > b && (TO & 0x08) != 0) || (a == b && (TO & 0x04) != 0) ||
(u32(a) < u32(b) && (TO & 0x02) != 0) || (u32(a) > u32(b) && (TO & 0x01) != 0))
{
PowerPC::ppcState.Exceptions |= EXCEPTION_PROGRAM;
GenerateProgramException(ProgramExceptionCause::Trap);
PowerPC::CheckExceptions();
m_end_block = true; // Dunno about this
}
@@ -339,7 +340,7 @@ void Interpreter::tw(UGeckoInstruction inst)
if ((a < b && (TO & 0x10) != 0) || (a > b && (TO & 0x08) != 0) || (a == b && (TO & 0x04) != 0) ||
((u32(a) < u32(b)) && (TO & 0x02) != 0) || ((u32(a) > u32(b)) && (TO & 0x01) != 0))
{
PowerPC::ppcState.Exceptions |= EXCEPTION_PROGRAM;
GenerateProgramException(ProgramExceptionCause::Trap);
PowerPC::CheckExceptions();
m_end_block = true; // Dunno about this
}

0 comments on commit 71051b7

Please sign in to comment.