mirror of https://github.com/PCSX2/pcsx2.git
x86emitter: Abstract MXCSR into a general FPControlRegister type
This commit is contained in:
parent
86edc0dbe2
commit
308f8c5112
|
@ -69,6 +69,7 @@ target_sources(common PRIVATE
|
|||
Easing.h
|
||||
EnumOps.h
|
||||
Error.h
|
||||
FPControl.h
|
||||
FastJmp.h
|
||||
FileSystem.h
|
||||
General.h
|
||||
|
@ -122,7 +123,6 @@ target_sources(common PRIVATE
|
|||
emitter/legacy_instructions.h
|
||||
emitter/legacy_internal.h
|
||||
emitter/legacy_types.h
|
||||
emitter/tools.h
|
||||
emitter/x86emitter.h
|
||||
emitter/x86types.h
|
||||
Darwin/DarwinMisc.h
|
||||
|
|
|
@ -29,7 +29,11 @@ __forceinline void Threading::SpinWait()
|
|||
{
|
||||
// If this doesn't compile you can just comment it out (it only serves as a
|
||||
// performance hint and isn't required).
|
||||
#if defined(_M_X86)
|
||||
__asm__("pause");
|
||||
#elif defined(_M_ARM64)
|
||||
__asm__ __volatile__("isb");
|
||||
#endif
|
||||
}
|
||||
|
||||
__forceinline void Threading::EnableHiresScheduler()
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
// This file abstracts the floating-point control registers, known as MXCSR on x86, and FPCR on AArch64.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/VectorIntrin.h"
|
||||
|
||||
enum class FPRoundMode : u8
|
||||
{
|
||||
Nearest,
|
||||
NegativeInfinity,
|
||||
PositiveInfinity,
|
||||
ChopZero,
|
||||
|
||||
MaxCount
|
||||
};
|
||||
|
||||
struct FPControlRegister
|
||||
{
|
||||
#ifdef _M_X86
|
||||
u32 bitmask;
|
||||
|
||||
static constexpr u32 EXCEPTION_MASK = (0x3Fu << 7);
|
||||
static constexpr u32 ROUNDING_CONTROL_SHIFT = 13;
|
||||
static constexpr u32 ROUNDING_CONTROL_MASK = 3u;
|
||||
static constexpr u32 ROUNDING_CONTROL_BITS = (ROUNDING_CONTROL_MASK << ROUNDING_CONTROL_SHIFT);
|
||||
static constexpr u32 DENORMALS_ARE_ZERO_BIT = (1u << 6);
|
||||
static constexpr u32 FLUSH_TO_ZERO_BIT = (1u << 15);
|
||||
|
||||
__fi static FPControlRegister GetCurrent()
|
||||
{
|
||||
return FPControlRegister{_mm_getcsr()};
|
||||
}
|
||||
|
||||
__fi static void SetCurrent(FPControlRegister value)
|
||||
{
|
||||
_mm_setcsr(value.bitmask);
|
||||
}
|
||||
|
||||
__fi static constexpr FPControlRegister GetDefault()
|
||||
{
|
||||
// 0x1f80 - all exceptions masked, nearest rounding
|
||||
return FPControlRegister{0x1f80};
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister& EnableExceptions()
|
||||
{
|
||||
bitmask &= ~EXCEPTION_MASK;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister DisableExceptions()
|
||||
{
|
||||
bitmask |= EXCEPTION_MASK;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr FPRoundMode GetRoundMode() const
|
||||
{
|
||||
return static_cast<FPRoundMode>((bitmask >> ROUNDING_CONTROL_SHIFT) & ROUNDING_CONTROL_MASK);
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister& SetRoundMode(FPRoundMode mode)
|
||||
{
|
||||
// These bits match on x86.
|
||||
bitmask = (bitmask & ~ROUNDING_CONTROL_BITS) | ((static_cast<u32>(mode) & ROUNDING_CONTROL_MASK) << ROUNDING_CONTROL_SHIFT);
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr bool GetDenormalsAreZero() const
|
||||
{
|
||||
return ((bitmask & DENORMALS_ARE_ZERO_BIT) != 0);
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister SetDenormalsAreZero(bool daz)
|
||||
{
|
||||
if (daz)
|
||||
bitmask |= DENORMALS_ARE_ZERO_BIT;
|
||||
else
|
||||
bitmask &= ~DENORMALS_ARE_ZERO_BIT;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr bool GetFlushToZero() const
|
||||
{
|
||||
return ((bitmask & FLUSH_TO_ZERO_BIT) != 0);
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister SetFlushToZero(bool ftz)
|
||||
{
|
||||
if (ftz)
|
||||
bitmask |= FLUSH_TO_ZERO_BIT;
|
||||
else
|
||||
bitmask &= ~FLUSH_TO_ZERO_BIT;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr bool operator==(const FPControlRegister& rhs) const { return bitmask == rhs.bitmask; }
|
||||
__fi constexpr bool operator!=(const FPControlRegister& rhs) const { return bitmask != rhs.bitmask; }
|
||||
|
||||
#elif defined(_M_ARM64)
|
||||
u64 bitmask;
|
||||
|
||||
static constexpr u64 FZ_BIT = (0x1ULL << 24);
|
||||
static constexpr u32 RMODE_SHIFT = 22;
|
||||
static constexpr u64 RMODE_MASK = 0x3ULL;
|
||||
static constexpr u64 RMODE_BITS = (RMODE_MASK << RMODE_SHIFT);
|
||||
static constexpr u32 EXCEPTION_MASK = (0x3Fu << 5);
|
||||
|
||||
__fi static FPControlRegister GetCurrent()
|
||||
{
|
||||
u64 value;
|
||||
asm volatile("\tmrs %0, FPCR\n"
|
||||
: "=r"(value));
|
||||
return FPControlRegister{value};
|
||||
}
|
||||
|
||||
__fi static void SetCurrent(FPControlRegister value)
|
||||
{
|
||||
asm volatile("\tmsr FPCR, %0\n" ::"r"(value.bitmask));
|
||||
}
|
||||
|
||||
__fi static constexpr FPControlRegister GetDefault()
|
||||
{
|
||||
// 0x0 - all exceptions masked, nearest rounding
|
||||
return FPControlRegister{0x0};
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister& EnableExceptions()
|
||||
{
|
||||
bitmask |= EXCEPTION_MASK;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister& DisableExceptions()
|
||||
{
|
||||
bitmask &= ~EXCEPTION_MASK;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr FPRoundMode GetRoundMode() const
|
||||
{
|
||||
// Negative/Positive infinity rounding is flipped on A64.
|
||||
const u64 RMode = (bitmask >> RMODE_SHIFT) & RMODE_MASK;
|
||||
return static_cast<FPRoundMode>((RMode == 0b00 || RMode == 0b11) ? RMode : (RMode ^ 0b11));
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister& SetRoundMode(FPRoundMode mode)
|
||||
{
|
||||
const u64 RMode = ((mode == FPRoundMode::Nearest || mode == FPRoundMode::ChopZero) ? static_cast<u64>(mode) : (static_cast<u64>(mode) ^ 0b11));
|
||||
bitmask = (bitmask & ~RMODE_BITS) | ((RMode & RMODE_MASK) << RMODE_SHIFT);
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr bool GetDenormalsAreZero() const
|
||||
{
|
||||
// Without FEAT_AFP, most ARM chips don't have separate DaZ/FtZ. This includes Apple Silicon, which
|
||||
// implements x86-like behavior with a vendor-specific extension that we cannot access from usermode.
|
||||
// The FZ bit causes both inputs and outputs to be flushed to zero.
|
||||
return ((bitmask & FZ_BIT) != 0);
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister SetDenormalsAreZero(bool daz)
|
||||
{
|
||||
if (daz)
|
||||
bitmask |= FZ_BIT;
|
||||
else
|
||||
bitmask &= ~FZ_BIT;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr bool GetFlushToZero() const
|
||||
{
|
||||
// See note in GetDenormalsAreZero().
|
||||
return ((bitmask & FZ_BIT) != 0);
|
||||
}
|
||||
|
||||
__fi constexpr FPControlRegister SetFlushToZero(bool ftz)
|
||||
{
|
||||
if (ftz)
|
||||
bitmask |= FZ_BIT;
|
||||
else
|
||||
bitmask &= ~FZ_BIT;
|
||||
return *this;
|
||||
}
|
||||
|
||||
__fi constexpr bool operator==(const FPControlRegister& rhs) const { return bitmask == rhs.bitmask; }
|
||||
__fi constexpr bool operator!=(const FPControlRegister& rhs) const { return bitmask != rhs.bitmask; }
|
||||
#else
|
||||
#error Unknown architecture.
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Helper to back up/restore FPCR.
|
||||
class FPControlRegisterBackup
|
||||
{
|
||||
public:
|
||||
__fi FPControlRegisterBackup(FPControlRegister new_value)
|
||||
: m_prev_val(FPControlRegister::GetCurrent())
|
||||
{
|
||||
FPControlRegister::SetCurrent(new_value);
|
||||
}
|
||||
__fi ~FPControlRegisterBackup()
|
||||
{
|
||||
FPControlRegister::SetCurrent(m_prev_val);
|
||||
}
|
||||
|
||||
FPControlRegisterBackup(const FPControlRegisterBackup&) = delete;
|
||||
FPControlRegisterBackup& operator=(const FPControlRegisterBackup&) = delete;
|
||||
|
||||
private:
|
||||
FPControlRegister m_prev_val;
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "FastJmp.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#if !defined(_MSC_VER) || defined(__clang__)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define PREFIX "_"
|
||||
|
@ -11,6 +11,8 @@
|
|||
#define PREFIX ""
|
||||
#endif
|
||||
|
||||
#if defined(_M_X86)
|
||||
|
||||
asm(
|
||||
"\t.global " PREFIX "fastjmp_set\n"
|
||||
"\t.global " PREFIX "fastjmp_jmp\n"
|
||||
|
@ -43,4 +45,47 @@ asm(
|
|||
jmp *%rdx
|
||||
)");
|
||||
|
||||
#elif defined(_M_ARM64)
|
||||
|
||||
asm(
|
||||
"\t.global " PREFIX "fastjmp_set\n"
|
||||
"\t.global " PREFIX "fastjmp_jmp\n"
|
||||
"\t.text\n"
|
||||
"\t.align 16\n"
|
||||
"\t" PREFIX "fastjmp_set:" R"(
|
||||
mov x16, sp
|
||||
stp x16, x30, [x0]
|
||||
stp x19, x20, [x0, #16]
|
||||
stp x21, x22, [x0, #32]
|
||||
stp x23, x24, [x0, #48]
|
||||
stp x25, x26, [x0, #64]
|
||||
stp x27, x28, [x0, #80]
|
||||
str x29, [x0, #96]
|
||||
stp d8, d9, [x0, #112]
|
||||
stp d10, d11, [x0, #128]
|
||||
stp d12, d13, [x0, #144]
|
||||
stp d14, d15, [x0, #160]
|
||||
mov w0, wzr
|
||||
br x30
|
||||
)"
|
||||
".align 16\n"
|
||||
"\t" PREFIX "fastjmp_jmp:" R"(
|
||||
ldp x16, x30, [x0]
|
||||
mov sp, x16
|
||||
ldp x19, x20, [x0, #16]
|
||||
ldp x21, x22, [x0, #32]
|
||||
ldp x23, x24, [x0, #48]
|
||||
ldp x25, x26, [x0, #64]
|
||||
ldp x27, x28, [x0, #80]
|
||||
ldr x29, [x0, #96]
|
||||
ldp d8, d9, [x0, #112]
|
||||
ldp d10, d11, [x0, #128]
|
||||
ldp d12, d13, [x0, #144]
|
||||
ldp d14, d15, [x0, #160]
|
||||
mov w0, w1
|
||||
br x30
|
||||
)");
|
||||
|
||||
#endif
|
||||
|
||||
#endif // __WIN32
|
||||
|
|
|
@ -10,6 +10,8 @@ struct fastjmp_buf
|
|||
{
|
||||
#if defined(_WIN32)
|
||||
static constexpr std::size_t BUF_SIZE = 240;
|
||||
#elif defined(_M_ARM64)
|
||||
static constexpr std::size_t BUF_SIZE = 168;
|
||||
#else
|
||||
static constexpr std::size_t BUF_SIZE = 64;
|
||||
#endif
|
||||
|
|
|
@ -152,6 +152,25 @@ namespace HostSys
|
|||
|
||||
/// Removes the page fault handler. handler is only specified to check against the active callback.
|
||||
void RemovePageFaultHandler(PageFaultHandler handler);
|
||||
|
||||
/// JIT write protect for Apple Silicon. Needs to be called prior to writing to any RWX pages.
|
||||
#if !defined(__APPLE__) || !defined(_M_ARM64)
|
||||
// clang-format -off
|
||||
[[maybe_unused]] __fi static void BeginCodeWrite() {}
|
||||
[[maybe_unused]] __fi static void EndCodeWrite() {}
|
||||
// clang-format on
|
||||
#else
|
||||
void BeginCodeWrite();
|
||||
void EndCodeWrite();
|
||||
#endif
|
||||
|
||||
/// Flushes the instruction cache on the host for the specified range.
|
||||
/// Only needed on ARM64, X86 has coherent D/I cache.
|
||||
#ifdef _M_X86
|
||||
[[maybe_unused]] __fi static void FlushInstructionCache(void* address, u32 size) {}
|
||||
#else
|
||||
void FlushInstructionCache(void* address, u32 size);
|
||||
#endif
|
||||
}
|
||||
|
||||
class SharedMemoryMappingArea
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <cstdio>
|
||||
#include <sys/mman.h>
|
||||
|
@ -38,14 +42,23 @@ static PageFaultHandler s_exception_handler_callback;
|
|||
static bool s_in_exception_handler;
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach/task.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_port.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__aarch64__)
|
||||
static struct sigaction s_old_sigbus_action;
|
||||
#else
|
||||
#endif
|
||||
#if !defined(__APPLE__) || defined(__aarch64__)
|
||||
static struct sigaction s_old_sigsegv_action;
|
||||
#endif
|
||||
|
||||
static void CallExistingSignalHandler(int signal, siginfo_t* siginfo, void* ctx)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
#if defined(__aarch64__)
|
||||
const struct sigaction& sa = (signal == SIGBUS) ? s_old_sigbus_action : s_old_sigsegv_action;
|
||||
#elif defined(__APPLE__)
|
||||
const struct sigaction& sa = s_old_sigbus_action;
|
||||
#else
|
||||
const struct sigaction& sa = s_old_sigsegv_action;
|
||||
|
@ -92,6 +105,12 @@ static void SysPageFaultSignalFilter(int signal, siginfo_t* siginfo, void* ctx)
|
|||
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.mc_rip);
|
||||
#elif defined(__x86_64__)
|
||||
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.gregs[REG_RIP]);
|
||||
#elif defined(__aarch64__)
|
||||
#ifndef __APPLE__
|
||||
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.pc);
|
||||
#else
|
||||
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__pc);
|
||||
#endif
|
||||
#else
|
||||
void* const exception_pc = nullptr;
|
||||
#endif
|
||||
|
@ -129,13 +148,18 @@ bool HostSys::InstallPageFaultHandler(PageFaultHandler handler)
|
|||
// Don't block the signal from executing recursively, we want to fire the original handler.
|
||||
sa.sa_flags |= SA_NODEFER;
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
// MacOS uses SIGBUS for memory permission violations
|
||||
#if defined(__APPLE__) || defined(__aarch64__)
|
||||
// MacOS uses SIGBUS for memory permission violations, as well as SIGSEGV on ARM64.
|
||||
if (sigaction(SIGBUS, &sa, &s_old_sigbus_action) != 0)
|
||||
return false;
|
||||
#else
|
||||
#endif
|
||||
#if !defined(__APPLE__) || defined(__aarch64__)
|
||||
if (sigaction(SIGSEGV, &sa, &s_old_sigsegv_action) != 0)
|
||||
return false;
|
||||
#endif
|
||||
#if defined(__APPLE__) && defined(__aarch64__)
|
||||
// Stops LLDB getting in a EXC_BAD_ACCESS loop when passing page faults to PCSX2.
|
||||
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -154,9 +178,10 @@ void HostSys::RemovePageFaultHandler(PageFaultHandler handler)
|
|||
s_exception_handler_callback = nullptr;
|
||||
|
||||
struct sigaction sa;
|
||||
#ifdef __APPLE__
|
||||
#if defined(__APPLE__) || defined(__aarch64__)
|
||||
sigaction(SIGBUS, &s_old_sigbus_action, &sa);
|
||||
#else
|
||||
#endif
|
||||
#if !defined(__APPLE__) || defined(__aarch64__)
|
||||
sigaction(SIGSEGV, &s_old_sigsegv_action, &sa);
|
||||
#endif
|
||||
}
|
||||
|
@ -188,6 +213,11 @@ void* HostSys::Mmap(void* base, size_t size, const PageProtectionMode& mode)
|
|||
if (base)
|
||||
flags |= MAP_FIXED;
|
||||
|
||||
#if defined(__APPLE__) && defined(_M_ARM64)
|
||||
if (mode.CanExecute())
|
||||
flags |= MAP_JIT;
|
||||
#endif
|
||||
|
||||
void* res = mmap(base, size, prot, flags, -1, 0);
|
||||
if (res == MAP_FAILED)
|
||||
return nullptr;
|
||||
|
@ -270,6 +300,15 @@ void HostSys::UnmapSharedMemory(void* baseaddr, size_t size)
|
|||
pxFailRel("Failed to unmap shared memory");
|
||||
}
|
||||
|
||||
#ifdef _M_ARM64
|
||||
|
||||
void HostSys::FlushInstructionCache(void* address, u32 size)
|
||||
{
|
||||
__builtin___clear_cache(reinterpret_cast<char*>(address), reinterpret_cast<char*>(address) + size);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SharedMemoryMappingArea::SharedMemoryMappingArea(u8* base_ptr, size_t size, size_t num_pages)
|
||||
: m_base_ptr(base_ptr)
|
||||
, m_size(size)
|
||||
|
@ -323,3 +362,22 @@ bool SharedMemoryMappingArea::Unmap(void* map_base, size_t map_size)
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_M_ARM64) && defined(__APPLE__)
|
||||
|
||||
static thread_local int s_code_write_depth = 0;
|
||||
|
||||
void HostSys::BeginCodeWrite()
|
||||
{
|
||||
if ((s_code_write_depth++) == 0)
|
||||
pthread_jit_write_protect_np(0);
|
||||
}
|
||||
|
||||
void HostSys::EndCodeWrite()
|
||||
{
|
||||
pxAssert(s_code_write_depth > 0);
|
||||
if ((--s_code_write_depth) == 0)
|
||||
pthread_jit_write_protect_np(1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,10 +28,6 @@
|
|||
#include "common/Threading.h"
|
||||
#include "common/Assertions.h"
|
||||
|
||||
// We wont need this until we actually have this more then just stubbed out, so I'm commenting this out
|
||||
// to remove an unneeded dependency.
|
||||
//#include "x86emitter/tools.h"
|
||||
|
||||
#if !defined(__unix__)
|
||||
|
||||
#pragma message("LnxThreads.cpp should only be compiled by projects or makefiles targeted at Linux/BSD distros.")
|
||||
|
@ -53,7 +49,11 @@ __forceinline void Threading::SpinWait()
|
|||
{
|
||||
// If this doesn't compile you can just comment it out (it only serves as a
|
||||
// performance hint and isn't required).
|
||||
#if defined(_M_X86)
|
||||
__asm__("pause");
|
||||
#elif defined(_M_ARM64)
|
||||
__asm__ __volatile__("isb");
|
||||
#endif
|
||||
}
|
||||
|
||||
__forceinline void Threading::EnableHiresScheduler()
|
||||
|
|
|
@ -138,7 +138,13 @@ namespace Perf
|
|||
pxAssertRel(perf_marker != MAP_FAILED, "Map perf marker");
|
||||
|
||||
JITDUMP_HEADER jh = {};
|
||||
#if defined(_M_X86)
|
||||
jh.elf_mach = EM_X86_64;
|
||||
#elif defined(_M_ARM64)
|
||||
jh.elf_mach = EM_AARCH64;
|
||||
#else
|
||||
#error Unhandled architecture.
|
||||
#endif
|
||||
jh.pid = getpid();
|
||||
jh.timestamp = JitDumpTimestamp();
|
||||
std::fwrite(&jh, sizeof(jh), 1, s_jitdump_file);
|
||||
|
|
|
@ -14,7 +14,16 @@
|
|||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
// Work around us defining _M_ARM64 but fast_float thinking that it means MSVC.
|
||||
#if defined(_M_ARM64) && !defined(_WIN32)
|
||||
#define HAD_M_ARM64 _M_ARM64
|
||||
#undef _M_ARM64
|
||||
#endif
|
||||
#include "fast_float/fast_float.h"
|
||||
#if defined(HAD_M_ARM64) && !defined(_WIN32)
|
||||
#define _M_ARM64 HAD_M_ARM64
|
||||
#undef HAD_M_ARM64
|
||||
#endif
|
||||
|
||||
// Older versions of libstdc++ are missing support for from_chars() with floats, and was only recently
|
||||
// merged in libc++. So, just fall back to stringstream (yuck!) on everywhere except MSVC.
|
||||
|
|
|
@ -34,7 +34,13 @@ long __stdcall SysPageFaultExceptionFilter(EXCEPTION_POINTERS* eps)
|
|||
if (eps->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
#if defined(_M_AMD64)
|
||||
void* const exception_pc = reinterpret_cast<void*>(eps->ContextRecord->Rip);
|
||||
#elif defined(_M_ARM64)
|
||||
void* const exception_pc = reinterpret_cast<void*>(eps->ContextRecord->Pc);
|
||||
#else
|
||||
void* const exception_pc = nullptr;
|
||||
#endif
|
||||
|
||||
const PageFaultInfo pfi{(uptr)exception_pc, (uptr)eps->ExceptionRecord->ExceptionInformation[1]};
|
||||
|
||||
|
@ -161,6 +167,15 @@ void HostSys::UnmapSharedMemory(void* baseaddr, size_t size)
|
|||
pxFail("Failed to unmap shared memory");
|
||||
}
|
||||
|
||||
#ifdef _M_ARM64
|
||||
|
||||
void HostSys::FlushInstructionCache(void* address, u32 size)
|
||||
{
|
||||
::FlushInstructionCache(GetCurrentProcess(), address, size);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SharedMemoryMappingArea::SharedMemoryMappingArea(u8* base_ptr, size_t size, size_t num_pages)
|
||||
: m_base_ptr(base_ptr)
|
||||
, m_size(size)
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include "common/Threading.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/emitter/tools.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include <mmsystem.h>
|
||||
#include <process.h>
|
||||
|
@ -20,7 +19,11 @@ __fi void Threading::Timeslice()
|
|||
// improve performance and reduce cpu power consumption.
|
||||
__fi void Threading::SpinWait()
|
||||
{
|
||||
#ifdef _M_X86
|
||||
_mm_pause();
|
||||
#else
|
||||
YieldProcessor();
|
||||
#endif
|
||||
}
|
||||
|
||||
__fi void Threading::EnableHiresScheduler()
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
<ClInclude Include="Error.h" />
|
||||
<ClInclude Include="FastJmp.h" />
|
||||
<ClInclude Include="FileSystem.h" />
|
||||
<ClInclude Include="FPControl.h" />
|
||||
<ClInclude Include="HashCombine.h" />
|
||||
<ClInclude Include="HeapArray.h" />
|
||||
<ClInclude Include="HeterogeneousContainers.h" />
|
||||
|
@ -151,7 +152,6 @@
|
|||
<ClInclude Include="emitter\legacy_instructions.h" />
|
||||
<ClInclude Include="emitter\legacy_internal.h" />
|
||||
<ClInclude Include="emitter\legacy_types.h" />
|
||||
<ClInclude Include="emitter\tools.h" />
|
||||
<ClInclude Include="emitter\x86emitter.h" />
|
||||
<ClInclude Include="emitter\x86types.h" />
|
||||
<ClInclude Include="emitter\implement\dwshift.h" />
|
||||
|
|
|
@ -234,9 +234,6 @@
|
|||
<ClInclude Include="emitter\implement\test.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="emitter\tools.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="emitter\x86emitter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -349,6 +346,7 @@
|
|||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SingleRegisterTypes.h" />
|
||||
<ClInclude Include="FPControl.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#include "common/emitter/internal.h"
|
||||
#include "common/emitter/tools.h"
|
||||
|
||||
// warning: suggest braces around initialization of subobject [-Wmissing-braces]
|
||||
#ifdef __clang__
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#include "common/emitter/internal.h"
|
||||
#include "common/emitter/tools.h"
|
||||
|
||||
namespace x86Emitter
|
||||
{
|
||||
|
|
|
@ -2,68 +2,8 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#include "common/emitter/internal.h"
|
||||
#include "common/emitter/tools.h"
|
||||
#include "common/VectorIntrin.h"
|
||||
|
||||
const char* EnumToString(SSE_RoundMode sse)
|
||||
{
|
||||
switch (sse)
|
||||
{
|
||||
case SSEround_Nearest:
|
||||
return "Nearest";
|
||||
case SSEround_NegInf:
|
||||
return "NegativeInfinity";
|
||||
case SSEround_PosInf:
|
||||
return "PositiveInfinity";
|
||||
case SSEround_Chop:
|
||||
return "Chop";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
SSE_MXCSR SSE_MXCSR::GetCurrent()
|
||||
{
|
||||
SSE_MXCSR ret;
|
||||
ret.bitmask = _mm_getcsr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SSE_MXCSR::SetCurrent(const SSE_MXCSR& value)
|
||||
{
|
||||
_mm_setcsr(value.bitmask);
|
||||
}
|
||||
|
||||
SSE_RoundMode SSE_MXCSR::GetRoundMode() const
|
||||
{
|
||||
return (SSE_RoundMode)RoundingControl;
|
||||
}
|
||||
|
||||
SSE_MXCSR& SSE_MXCSR::SetRoundMode(SSE_RoundMode mode)
|
||||
{
|
||||
pxAssert((uint)mode < 4);
|
||||
RoundingControl = (u32)mode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SSE_MXCSR& SSE_MXCSR::ClearExceptionFlags()
|
||||
{
|
||||
bitmask &= ~0x3f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SSE_MXCSR& SSE_MXCSR::EnableExceptions()
|
||||
{
|
||||
bitmask &= ~(0x3f << 7);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SSE_MXCSR& SSE_MXCSR::DisableExceptions()
|
||||
{
|
||||
bitmask |= 0x3f << 7;
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace x86Emitter
|
||||
{
|
||||
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
|
||||
enum SSE_RoundMode
|
||||
{
|
||||
SSE_RoundMode_FIRST = 0,
|
||||
SSEround_Nearest = 0,
|
||||
SSEround_NegInf,
|
||||
SSEround_PosInf,
|
||||
SSEround_Chop,
|
||||
SSE_RoundMode_COUNT
|
||||
};
|
||||
|
||||
ImplementEnumOperators(SSE_RoundMode);
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// SSE_MXCSR - Control/Status Register (bitfield)
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Bits 0-5 are exception flags; used only if SSE exceptions have been enabled.
|
||||
// Bits in this field are "sticky" and, once an exception has occured, must be manually
|
||||
// cleared using LDMXCSR or FXRSTOR.
|
||||
//
|
||||
// Bits 7-12 are the masks for disabling the exceptions in bits 0-5. Cleared bits allow
|
||||
// exceptions, set bits mask exceptions from being raised.
|
||||
//
|
||||
union SSE_MXCSR
|
||||
{
|
||||
u32 bitmask;
|
||||
struct
|
||||
{
|
||||
u32
|
||||
InvalidOpFlag : 1,
|
||||
DenormalFlag : 1,
|
||||
DivideByZeroFlag : 1,
|
||||
OverflowFlag : 1,
|
||||
UnderflowFlag : 1,
|
||||
PrecisionFlag : 1,
|
||||
|
||||
DenormalsAreZero : 1,
|
||||
|
||||
InvalidOpMask : 1,
|
||||
DenormalMask : 1,
|
||||
DivideByZeroMask : 1,
|
||||
OverflowMask : 1,
|
||||
UnderflowMask : 1,
|
||||
PrecisionMask : 1,
|
||||
|
||||
RoundingControl : 2,
|
||||
FlushToZero : 1;
|
||||
};
|
||||
|
||||
static SSE_MXCSR GetCurrent();
|
||||
static void SetCurrent(const SSE_MXCSR& value);
|
||||
|
||||
SSE_RoundMode GetRoundMode() const;
|
||||
SSE_MXCSR& SetRoundMode(SSE_RoundMode mode);
|
||||
SSE_MXCSR& ClearExceptionFlags();
|
||||
SSE_MXCSR& EnableExceptions();
|
||||
SSE_MXCSR& DisableExceptions();
|
||||
|
||||
bool operator==(const SSE_MXCSR& right) const
|
||||
{
|
||||
return bitmask == right.bitmask;
|
||||
}
|
||||
|
||||
bool operator!=(const SSE_MXCSR& right) const
|
||||
{
|
||||
return bitmask != right.bitmask;
|
||||
}
|
||||
};
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
#include "common/emitter/internal.h"
|
||||
#include "common/emitter/tools.h"
|
||||
#include <functional>
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "common/emitter/x86types.h"
|
||||
#include "common/emitter/tools.h"
|
||||
#include "common/emitter/instructions.h"
|
||||
|
||||
// Including legacy items for now, but these should be removed eventually,
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common/emitter/tools.h"
|
||||
#include "common/General.h"
|
||||
#include "common/FPControl.h"
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
|
@ -385,11 +385,6 @@ typename std::underlying_type<Enumeration>::type enum_cast(Enumeration E)
|
|||
|
||||
ImplementEnumOperators(GamefixId);
|
||||
|
||||
//------------ DEFAULT sseMXCSR VALUES ---------------
|
||||
#define DEFAULT_sseMXCSR 0xffc0 //FPU rounding > DaZ, FtZ, "chop"
|
||||
#define DEFAULT_sseVUMXCSR 0xffc0 //VU rounding > DaZ, FtZ, "chop"
|
||||
#define SYSTEM_sseMXCSR 0x1f80
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// TraceFiltersEE
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -597,9 +592,9 @@ struct Pcsx2Config
|
|||
{
|
||||
RecompilerOptions Recompiler;
|
||||
|
||||
SSE_MXCSR sseMXCSR;
|
||||
SSE_MXCSR sseVU0MXCSR;
|
||||
SSE_MXCSR sseVU1MXCSR;
|
||||
FPControlRegister FPUFPCR;
|
||||
FPControlRegister VU0FPCR;
|
||||
FPControlRegister VU1FPCR;
|
||||
|
||||
u32 AffinityControlMode;
|
||||
|
||||
|
@ -611,7 +606,7 @@ struct Pcsx2Config
|
|||
|
||||
bool operator==(const CpuOptions& right) const
|
||||
{
|
||||
return OpEqu(sseMXCSR) && OpEqu(sseVU0MXCSR) && OpEqu(sseVU1MXCSR) && OpEqu(AffinityControlMode) && OpEqu(Recompiler);
|
||||
return OpEqu(FPUFPCR) && OpEqu(VU0FPCR) && OpEqu(VU1FPCR) && OpEqu(AffinityControlMode) && OpEqu(Recompiler);
|
||||
}
|
||||
|
||||
bool operator!=(const CpuOptions& right) const
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "GS/GSExtra.h"
|
||||
#include "GS/Renderers/SW/GSScanlineEnvironment.h"
|
||||
#include "System.h"
|
||||
#include "common/emitter/tools.h"
|
||||
|
||||
template <class KEY, class VALUE>
|
||||
class GSFunctionMap
|
||||
|
|
|
@ -114,26 +114,42 @@ void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml:
|
|||
{
|
||||
int eeVal = -1;
|
||||
node["roundModes"]["eeRoundMode"] >> eeVal;
|
||||
gameEntry.eeRoundMode = static_cast<GameDatabaseSchema::RoundMode>(eeVal);
|
||||
if (eeVal >= 0 && eeVal < static_cast<int>(FPRoundMode::MaxCount))
|
||||
gameEntry.eeRoundMode = static_cast<FPRoundMode>(eeVal);
|
||||
else
|
||||
Console.Error(fmt::format("[GameDB] Invalid EE round mode '{}', specified for serial: '{}'.", eeVal, serial));
|
||||
}
|
||||
if (node["roundModes"].has_child("vuRoundMode"))
|
||||
{
|
||||
int vuVal = -1;
|
||||
node["roundModes"]["vuRoundMode"] >> vuVal;
|
||||
gameEntry.vu0RoundMode = static_cast<GameDatabaseSchema::RoundMode>(vuVal);
|
||||
gameEntry.vu1RoundMode = static_cast<GameDatabaseSchema::RoundMode>(vuVal);
|
||||
if (vuVal >= 0 && vuVal < static_cast<int>(FPRoundMode::MaxCount))
|
||||
{
|
||||
gameEntry.vu0RoundMode = static_cast<FPRoundMode>(vuVal);
|
||||
gameEntry.vu1RoundMode = static_cast<FPRoundMode>(vuVal);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error(fmt::format("[GameDB] Invalid VU round mode '{}', specified for serial: '{}'.", vuVal, serial));
|
||||
}
|
||||
}
|
||||
if (node["roundModes"].has_child("vu0RoundMode"))
|
||||
{
|
||||
int vuVal = -1;
|
||||
node["roundModes"]["vu0RoundMode"] >> vuVal;
|
||||
gameEntry.vu0RoundMode = static_cast<GameDatabaseSchema::RoundMode>(vuVal);
|
||||
if (vuVal >= 0 && vuVal < static_cast<int>(FPRoundMode::MaxCount))
|
||||
gameEntry.vu0RoundMode = static_cast<FPRoundMode>(vuVal);
|
||||
else
|
||||
Console.Error(fmt::format("[GameDB] Invalid VU0 round mode '{}', specified for serial: '{}'.", vuVal, serial));
|
||||
}
|
||||
if (node["roundModes"].has_child("vu1RoundMode"))
|
||||
{
|
||||
int vuVal = -1;
|
||||
node["roundModes"]["vu1RoundMode"] >> vuVal;
|
||||
gameEntry.vu1RoundMode = static_cast<GameDatabaseSchema::RoundMode>(vuVal);
|
||||
if (vuVal >= 0 && vuVal < static_cast<int>(FPRoundMode::MaxCount))
|
||||
gameEntry.vu1RoundMode = static_cast<FPRoundMode>(vuVal);
|
||||
else
|
||||
Console.Error(fmt::format("[GameDB] Invalid VU1 round mode '{}', specified for serial: '{}'.", vuVal, serial));
|
||||
}
|
||||
}
|
||||
if (node.has_child("clampModes"))
|
||||
|
@ -327,6 +343,13 @@ void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml:
|
|||
s_game_db.emplace(std::move(serial), std::move(gameEntry));
|
||||
}
|
||||
|
||||
static const char* s_round_modes[static_cast<u32>(FPRoundMode::MaxCount)] = {
|
||||
"Nearest",
|
||||
"NegativeInfinity",
|
||||
"PositiveInfinity",
|
||||
"Chop"
|
||||
};
|
||||
|
||||
static const char* s_gs_hw_fix_names[] = {
|
||||
"autoFlush",
|
||||
"cpuFramebufferConversion",
|
||||
|
@ -410,48 +433,42 @@ void GameDatabaseSchema::GameEntry::applyGameFixes(Pcsx2Config& config, bool app
|
|||
if (!applyAuto)
|
||||
Console.Warning("[GameDB] Game Fixes are disabled");
|
||||
|
||||
if (eeRoundMode != GameDatabaseSchema::RoundMode::Undefined)
|
||||
if (eeRoundMode < FPRoundMode::MaxCount)
|
||||
{
|
||||
const SSE_RoundMode eeRM = (SSE_RoundMode)enum_cast(eeRoundMode);
|
||||
if (EnumIsValid(eeRM))
|
||||
if (applyAuto)
|
||||
{
|
||||
if (applyAuto)
|
||||
{
|
||||
Console.WriteLn("(GameDB) Changing EE/FPU roundmode to %d [%s]", eeRM, EnumToString(eeRM));
|
||||
config.Cpu.sseMXCSR.SetRoundMode(eeRM);
|
||||
}
|
||||
else
|
||||
Console.Warning("[GameDB] Skipping changing EE/FPU roundmode to %d [%s]", eeRM, EnumToString(eeRM));
|
||||
Console.WriteLn("(GameDB) Changing EE/FPU roundmode to %d [%s]", eeRoundMode, s_round_modes[static_cast<u8>(eeRoundMode)]);
|
||||
config.Cpu.FPUFPCR.SetRoundMode(eeRoundMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Warning("[GameDB] Skipping changing EE/FPU roundmode to %d [%s]", eeRoundMode, s_round_modes[static_cast<u8>(eeRoundMode)]);
|
||||
}
|
||||
}
|
||||
|
||||
if (vu0RoundMode != GameDatabaseSchema::RoundMode::Undefined)
|
||||
if (vu0RoundMode < FPRoundMode::MaxCount)
|
||||
{
|
||||
const SSE_RoundMode vuRM = (SSE_RoundMode)enum_cast(vu0RoundMode);
|
||||
if (EnumIsValid(vuRM))
|
||||
if (applyAuto)
|
||||
{
|
||||
if (applyAuto)
|
||||
{
|
||||
Console.WriteLn("(GameDB) Changing VU0 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
|
||||
config.Cpu.sseVU0MXCSR.SetRoundMode(vuRM);
|
||||
}
|
||||
else
|
||||
Console.Warning("[GameDB] Skipping changing VU0 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
|
||||
Console.WriteLn("(GameDB) Changing VU0 roundmode to %d [%s]", vu0RoundMode, s_round_modes[static_cast<u8>(vu0RoundMode)]);
|
||||
config.Cpu.VU0FPCR.SetRoundMode(vu0RoundMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Warning("[GameDB] Skipping changing VU0 roundmode to %d [%s]", vu0RoundMode, s_round_modes[static_cast<u8>(vu0RoundMode)]);
|
||||
}
|
||||
}
|
||||
|
||||
if (vu1RoundMode != GameDatabaseSchema::RoundMode::Undefined)
|
||||
if (vu1RoundMode < FPRoundMode::MaxCount)
|
||||
{
|
||||
const SSE_RoundMode vuRM = (SSE_RoundMode)enum_cast(vu1RoundMode);
|
||||
if (EnumIsValid(vuRM))
|
||||
if (applyAuto)
|
||||
{
|
||||
if (applyAuto)
|
||||
{
|
||||
Console.WriteLn("(GameDB) Changing VU1 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
|
||||
config.Cpu.sseVU1MXCSR.SetRoundMode(vuRM);
|
||||
}
|
||||
else
|
||||
Console.Warning("[GameDB] Skipping changing VU1 roundmode to %d [%s]", vuRM, EnumToString(vuRM));
|
||||
Console.WriteLn("(GameDB) Changing VU1 roundmode to %d [%s]", vu1RoundMode, s_round_modes[static_cast<u8>(vu1RoundMode)]);
|
||||
config.Cpu.VU1FPCR.SetRoundMode(vu1RoundMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Warning("[GameDB] Skipping changing VU1 roundmode to %d [%s]", vu1RoundMode, s_round_modes[static_cast<u8>(vu1RoundMode)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
#include "Config.h"
|
||||
#include "Patch.h"
|
||||
|
||||
#include "common/FPControl.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -27,22 +30,14 @@ namespace GameDatabaseSchema
|
|||
Perfect
|
||||
};
|
||||
|
||||
enum class RoundMode
|
||||
{
|
||||
Undefined = -1,
|
||||
Nearest = 0,
|
||||
NegativeInfinity,
|
||||
PositiveInfinity,
|
||||
ChopZero
|
||||
};
|
||||
|
||||
enum class ClampMode
|
||||
{
|
||||
Undefined = -1,
|
||||
Disabled = 0,
|
||||
Normal,
|
||||
Extra,
|
||||
Full
|
||||
Full,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class GSHWFixId : u32
|
||||
|
@ -96,9 +91,9 @@ namespace GameDatabaseSchema
|
|||
std::string name_en;
|
||||
std::string region;
|
||||
Compatibility compat = Compatibility::Unknown;
|
||||
RoundMode eeRoundMode = RoundMode::Undefined;
|
||||
RoundMode vu0RoundMode = RoundMode::Undefined;
|
||||
RoundMode vu1RoundMode = RoundMode::Undefined;
|
||||
FPRoundMode eeRoundMode = FPRoundMode::MaxCount;
|
||||
FPRoundMode vu0RoundMode = FPRoundMode::MaxCount;
|
||||
FPRoundMode vu1RoundMode = FPRoundMode::MaxCount;
|
||||
ClampMode eeClampMode = ClampMode::Undefined;
|
||||
ClampMode vu0ClampMode = ClampMode::Undefined;
|
||||
ClampMode vu1ClampMode = ClampMode::Undefined;
|
||||
|
|
|
@ -345,8 +345,8 @@ void ImGuiManager::DrawSettingsOverlay()
|
|||
if (EmuConfig.Speedhacks.vuThread)
|
||||
APPEND("MTVU ");
|
||||
|
||||
APPEND("EER={} EEC={} VUR={} VUC={} VQS={} ", static_cast<unsigned>(EmuConfig.Cpu.sseMXCSR.GetRoundMode()),
|
||||
EmuConfig.Cpu.Recompiler.GetEEClampMode(), static_cast<unsigned>(EmuConfig.Cpu.sseVU0MXCSR.GetRoundMode()),
|
||||
APPEND("EER={} EEC={} VUR={} VUC={} VQS={} ", static_cast<unsigned>(EmuConfig.Cpu.FPUFPCR.GetRoundMode()),
|
||||
EmuConfig.Cpu.Recompiler.GetEEClampMode(), static_cast<unsigned>(EmuConfig.Cpu.VU0FPCR.GetRoundMode()),
|
||||
EmuConfig.Cpu.Recompiler.GetVUClampMode(), EmuConfig.GS.VsyncQueueSize);
|
||||
|
||||
if (EmuConfig.EnableCheats || EmuConfig.EnableWideScreenPatches || EmuConfig.EnableNoInterlacingPatches)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "common/SettingsInterface.h"
|
||||
#include "common/SettingsWrapper.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/SmallString.h"
|
||||
#include "Config.h"
|
||||
#include "GS.h"
|
||||
#include "CDVD/CDVDcommon.h"
|
||||
|
@ -21,6 +22,18 @@
|
|||
#include <ShlObj.h>
|
||||
#endif
|
||||
|
||||
// Default EE/VU control registers have exceptions off, DaZ/FTZ, and the rounding mode set to Chop/Zero.
|
||||
static constexpr FPControlRegister DEFAULT_FPU_FP_CONTROL_REGISTER = FPControlRegister::GetDefault()
|
||||
.DisableExceptions()
|
||||
.SetDenormalsAreZero(true)
|
||||
.SetFlushToZero(true)
|
||||
.SetRoundMode(FPRoundMode::ChopZero);
|
||||
static constexpr FPControlRegister DEFAULT_VU_FP_CONTROL_REGISTER = FPControlRegister::GetDefault()
|
||||
.DisableExceptions()
|
||||
.SetDenormalsAreZero(true)
|
||||
.SetFlushToZero(true)
|
||||
.SetRoundMode(FPRoundMode::ChopZero);
|
||||
|
||||
const char* SettingInfo::StringDefaultValue() const
|
||||
{
|
||||
return default_value ? default_value : "";
|
||||
|
@ -389,17 +402,14 @@ bool Pcsx2Config::CpuOptions::CpusChanged(const CpuOptions& right) const
|
|||
|
||||
Pcsx2Config::CpuOptions::CpuOptions()
|
||||
{
|
||||
sseMXCSR.bitmask = DEFAULT_sseMXCSR;
|
||||
sseVU0MXCSR.bitmask = DEFAULT_sseVUMXCSR;
|
||||
sseVU1MXCSR.bitmask = DEFAULT_sseVUMXCSR;
|
||||
FPUFPCR = DEFAULT_FPU_FP_CONTROL_REGISTER;
|
||||
VU0FPCR = DEFAULT_VU_FP_CONTROL_REGISTER;
|
||||
VU1FPCR = DEFAULT_VU_FP_CONTROL_REGISTER;
|
||||
AffinityControlMode = 0;
|
||||
}
|
||||
|
||||
void Pcsx2Config::CpuOptions::ApplySanityCheck()
|
||||
{
|
||||
sseMXCSR.ClearExceptionFlags().DisableExceptions();
|
||||
sseVU0MXCSR.ClearExceptionFlags().DisableExceptions();
|
||||
sseVU1MXCSR.ClearExceptionFlags().DisableExceptions();
|
||||
AffinityControlMode = std::min<u32>(AffinityControlMode, 6);
|
||||
|
||||
Recompiler.ApplySanityCheck();
|
||||
|
@ -409,17 +419,23 @@ void Pcsx2Config::CpuOptions::LoadSave(SettingsWrapper& wrap)
|
|||
{
|
||||
SettingsWrapSection("EmuCore/CPU");
|
||||
|
||||
SettingsWrapBitBoolEx(sseMXCSR.DenormalsAreZero, "FPU.DenormalsAreZero");
|
||||
SettingsWrapBitBoolEx(sseMXCSR.FlushToZero, "FPU.FlushToZero");
|
||||
SettingsWrapBitfieldEx(sseMXCSR.RoundingControl, "FPU.Roundmode");
|
||||
SettingsWrapEntry(AffinityControlMode);
|
||||
const auto read_fpcr = [&wrap, &CURRENT_SETTINGS_SECTION](FPControlRegister& fpcr, std::string_view prefix) {
|
||||
fpcr.SetDenormalsAreZero(wrap.EntryBitBool(CURRENT_SETTINGS_SECTION, TinyString::from_fmt("{}.DenormalsAreZero", prefix),
|
||||
fpcr.GetDenormalsAreZero(), fpcr.GetDenormalsAreZero()));
|
||||
fpcr.SetFlushToZero(wrap.EntryBitBool(CURRENT_SETTINGS_SECTION, TinyString::from_fmt("{}.DenormalsAreZero", prefix),
|
||||
fpcr.GetFlushToZero(), fpcr.GetFlushToZero()));
|
||||
|
||||
SettingsWrapBitBoolEx(sseVU0MXCSR.DenormalsAreZero, "VU0.DenormalsAreZero");
|
||||
SettingsWrapBitBoolEx(sseVU0MXCSR.FlushToZero, "VU0.FlushToZero");
|
||||
SettingsWrapBitfieldEx(sseVU0MXCSR.RoundingControl, "VU0.Roundmode");
|
||||
SettingsWrapBitBoolEx(sseVU1MXCSR.DenormalsAreZero, "VU1.DenormalsAreZero");
|
||||
SettingsWrapBitBoolEx(sseVU1MXCSR.FlushToZero, "VU1.FlushToZero");
|
||||
SettingsWrapBitfieldEx(sseVU1MXCSR.RoundingControl, "VU1.Roundmode");
|
||||
uint round_mode = static_cast<uint>(fpcr.GetRoundMode());
|
||||
wrap.Entry(CURRENT_SETTINGS_SECTION, TinyString::from_fmt("{}.Roundmode", prefix), round_mode, round_mode);
|
||||
round_mode = std::min(round_mode, static_cast<uint>(FPRoundMode::MaxCount) - 1u);
|
||||
fpcr.SetRoundMode(static_cast<FPRoundMode>(round_mode));
|
||||
};
|
||||
|
||||
read_fpcr(FPUFPCR, "FPU");
|
||||
read_fpcr(VU0FPCR, "VU0");
|
||||
read_fpcr(VU1FPCR, "VU1");
|
||||
|
||||
SettingsWrapEntry(AffinityControlMode);
|
||||
|
||||
Recompiler.LoadSave(wrap);
|
||||
}
|
||||
|
@ -461,6 +477,7 @@ const char* Pcsx2Config::GSOptions::GetRendererName(GSRendererType type)
|
|||
{
|
||||
switch (type)
|
||||
{
|
||||
// clang-format off
|
||||
case GSRendererType::Auto: return "Auto";
|
||||
case GSRendererType::DX11: return "Direct3D 11";
|
||||
case GSRendererType::DX12: return "Direct3D 12";
|
||||
|
@ -470,6 +487,7 @@ const char* Pcsx2Config::GSOptions::GetRendererName(GSRendererType type)
|
|||
case GSRendererType::SW: return "Software";
|
||||
case GSRendererType::Null: return "Null";
|
||||
default: return "";
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1093,26 +1111,26 @@ std::string Pcsx2Config::DEV9Options::SaveIPHelper(u8* field)
|
|||
}
|
||||
|
||||
static const char* const tbl_GamefixNames[] =
|
||||
{
|
||||
"FpuMul",
|
||||
"FpuNegDiv",
|
||||
"GoemonTlb",
|
||||
"SoftwareRendererFMV",
|
||||
"SkipMPEG",
|
||||
"OPHFlag",
|
||||
"EETiming",
|
||||
"InstantDMA",
|
||||
"DMABusy",
|
||||
"GIFFIFO",
|
||||
"VIFFIFO",
|
||||
"VIF1Stall",
|
||||
"VuAddSub",
|
||||
"Ibit",
|
||||
"VUSync",
|
||||
"VUOverflow",
|
||||
"XGKick",
|
||||
"BlitInternalFPS",
|
||||
"FullVU0Sync",
|
||||
{
|
||||
"FpuMul",
|
||||
"FpuNegDiv",
|
||||
"GoemonTlb",
|
||||
"SoftwareRendererFMV",
|
||||
"SkipMPEG",
|
||||
"OPHFlag",
|
||||
"EETiming",
|
||||
"InstantDMA",
|
||||
"DMABusy",
|
||||
"GIFFIFO",
|
||||
"VIFFIFO",
|
||||
"VIF1Stall",
|
||||
"VuAddSub",
|
||||
"Ibit",
|
||||
"VUSync",
|
||||
"VUOverflow",
|
||||
"XGKick",
|
||||
"BlitInternalFPS",
|
||||
"FullVU0Sync",
|
||||
};
|
||||
|
||||
const char* EnumToString(GamefixId id)
|
||||
|
@ -1137,6 +1155,7 @@ void Pcsx2Config::GamefixOptions::Set(GamefixId id, bool enabled)
|
|||
pxAssert(EnumIsValid(id));
|
||||
switch (id)
|
||||
{
|
||||
// clang-format off
|
||||
case Fix_VuAddSub: VuAddSubHack = enabled; break;
|
||||
case Fix_FpuMultiply: FpuMulHack = enabled; break;
|
||||
case Fix_FpuNegDiv: FpuNegDivHack = enabled; break;
|
||||
|
@ -1157,6 +1176,7 @@ void Pcsx2Config::GamefixOptions::Set(GamefixId id, bool enabled)
|
|||
case Fix_BlitInternalFPS: BlitInternalFPSHack = enabled; break;
|
||||
case Fix_FullVU0Sync: FullVU0SyncHack = enabled; break;
|
||||
jNO_DEFAULT;
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1165,6 +1185,7 @@ bool Pcsx2Config::GamefixOptions::Get(GamefixId id) const
|
|||
pxAssert(EnumIsValid(id));
|
||||
switch (id)
|
||||
{
|
||||
// clang-format off
|
||||
case Fix_VuAddSub: return VuAddSubHack;
|
||||
case Fix_FpuMultiply: return FpuMulHack;
|
||||
case Fix_FpuNegDiv: return FpuNegDivHack;
|
||||
|
@ -1185,6 +1206,7 @@ bool Pcsx2Config::GamefixOptions::Get(GamefixId id) const
|
|||
case Fix_BlitInternalFPS: return BlitInternalFPSHack;
|
||||
case Fix_FullVU0Sync: return FullVU0SyncHack;
|
||||
jNO_DEFAULT;
|
||||
// clang-format on
|
||||
}
|
||||
return false; // unreachable, but we still need to suppress warnings >_<
|
||||
}
|
||||
|
@ -1497,11 +1519,6 @@ Pcsx2Config::Pcsx2Config()
|
|||
|
||||
void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
|
||||
{
|
||||
// Switch the rounding mode back to the system default for loading settings.
|
||||
// That way, we'll get exactly the same values as what we loaded when we first started.
|
||||
const SSE_MXCSR prev_mxcsr(SSE_MXCSR::GetCurrent());
|
||||
SSE_MXCSR::SetCurrent(SSE_MXCSR{SYSTEM_sseMXCSR});
|
||||
|
||||
SettingsWrapSection("EmuCore");
|
||||
|
||||
SettingsWrapBitBool(CdvdVerboseReads);
|
||||
|
@ -1561,8 +1578,6 @@ void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
|
|||
{
|
||||
CurrentAspectRatio = GS.AspectRatio;
|
||||
}
|
||||
|
||||
SSE_MXCSR::SetCurrent(prev_mxcsr);
|
||||
}
|
||||
|
||||
void Pcsx2Config::LoadSave(SettingsWrapper& wrap)
|
||||
|
@ -1622,6 +1637,8 @@ void Pcsx2Config::CopyRuntimeConfig(Pcsx2Config& cfg)
|
|||
|
||||
void Pcsx2Config::CopyConfiguration(SettingsInterface* dest_si, SettingsInterface& src_si)
|
||||
{
|
||||
FPControlRegisterBackup fpcr_backup(FPControlRegister::GetDefault());
|
||||
|
||||
Pcsx2Config temp;
|
||||
{
|
||||
SettingsLoadWrapper wrapper(src_si);
|
||||
|
@ -1635,6 +1652,8 @@ void Pcsx2Config::CopyConfiguration(SettingsInterface* dest_si, SettingsInterfac
|
|||
|
||||
void Pcsx2Config::ClearConfiguration(SettingsInterface* dest_si)
|
||||
{
|
||||
FPControlRegisterBackup fpcr_backup(FPControlRegister::GetDefault());
|
||||
|
||||
Pcsx2Config temp;
|
||||
SettingsClearWrapper wrapper(*dest_si);
|
||||
temp.LoadSaveCore(wrapper);
|
||||
|
|
|
@ -21,18 +21,10 @@
|
|||
#include "common/Perf.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#ifdef _M_X86
|
||||
#include "common/emitter/tools.h"
|
||||
#endif
|
||||
|
||||
extern R5900cpu GSDumpReplayerCpu;
|
||||
|
||||
Pcsx2Config EmuConfig;
|
||||
|
||||
SSE_MXCSR g_sseMXCSR = {DEFAULT_sseMXCSR};
|
||||
SSE_MXCSR g_sseVU0MXCSR = {DEFAULT_sseVUMXCSR};
|
||||
SSE_MXCSR g_sseVU1MXCSR = {DEFAULT_sseVUMXCSR};
|
||||
|
||||
namespace SysMemory
|
||||
{
|
||||
static u8* TryAllocateVirtualMemory(const char* name, void* file_handle, uptr base, size_t size);
|
||||
|
@ -47,19 +39,6 @@ namespace SysMemory
|
|||
static u8* s_code_memory;
|
||||
} // namespace SysMemory
|
||||
|
||||
// SetCPUState -- for assignment of SSE roundmodes and clampmodes.
|
||||
//
|
||||
void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVU0MXCSR, SSE_MXCSR sseVU1MXCSR)
|
||||
{
|
||||
//Msgbox::Alert("SetCPUState: Config.sseMXCSR = %x; Config.sseVUMXCSR = %x \n", Config.sseMXCSR, Config.sseVUMXCSR);
|
||||
|
||||
g_sseMXCSR = sseMXCSR;
|
||||
g_sseVU0MXCSR = sseVU0MXCSR;
|
||||
g_sseVU1MXCSR = sseVU1MXCSR;
|
||||
|
||||
_mm_setcsr(g_sseMXCSR.bitmask);
|
||||
}
|
||||
|
||||
// This function should be called once during program execution.
|
||||
void SysLogMachineCaps()
|
||||
{
|
||||
|
|
|
@ -156,10 +156,8 @@ public:
|
|||
|
||||
// GetCpuProviders - this function is not implemented by PCSX2 core -- it must be
|
||||
// implemented by the provisioning interface.
|
||||
// TODO: Purge this and the class above
|
||||
extern SysCpuProviderPack& GetCpuProviders();
|
||||
|
||||
extern void SysLogMachineCaps(); // Detects cpu type and fills cpuInfo structs.
|
||||
extern void SysClearExecutionCache(); // clears recompiled execution caches!
|
||||
|
||||
extern void SetCPUState(SSE_MXCSR sseMXCSR, SSE_MXCSR sseVU0MXCSR, SSE_MXCSR sseVU1MXCSR);
|
||||
extern SSE_MXCSR g_sseVU0MXCSR, g_sseVU1MXCSR, g_sseMXCSR;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "common/Console.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/FPControl.h"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/SettingsWrapper.h"
|
||||
#include "common/SmallString.h"
|
||||
|
@ -59,10 +60,6 @@
|
|||
#include <mutex>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef _M_X86
|
||||
#include "common/emitter/tools.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include <objbase.h>
|
||||
|
@ -160,7 +157,6 @@ static bool s_elf_executed = false;
|
|||
static std::string s_elf_override;
|
||||
static std::string s_input_profile_name;
|
||||
static u32 s_frame_advance_count = 0;
|
||||
static u32 s_mxcsr_saved;
|
||||
static bool s_fast_boot_requested = false;
|
||||
static bool s_gs_open_on_initialize = false;
|
||||
|
||||
|
@ -342,6 +338,9 @@ bool VMManager::Internal::CPUThreadInitialize()
|
|||
}
|
||||
#endif
|
||||
|
||||
// Use the default rounding mode, just in case it differs on some platform.
|
||||
FPControlRegister::SetCurrent(FPControlRegister::GetDefault());
|
||||
|
||||
if (!cpuinfo_initialize())
|
||||
Console.Error("cpuinfo_initialize() failed.");
|
||||
|
||||
|
@ -431,6 +430,8 @@ void VMManager::Internal::LoadStartupSettings()
|
|||
void VMManager::SetDefaultSettings(
|
||||
SettingsInterface& si, bool folders, bool core, bool controllers, bool hotkeys, bool ui)
|
||||
{
|
||||
FPControlRegisterBackup fpcr_backup(FPControlRegister::GetDefault());
|
||||
|
||||
if (si.GetUIntValue("UI", "SettingsVersion", 0u) != SETTINGS_VERSION)
|
||||
si.SetUIntValue("UI", "SettingsVersion", SETTINGS_VERSION);
|
||||
|
||||
|
@ -461,6 +462,11 @@ void VMManager::SetDefaultSettings(
|
|||
|
||||
void VMManager::LoadSettings()
|
||||
{
|
||||
// Switch the rounding mode back to the system default for loading settings.
|
||||
// We might have a different mode, because this can be called during setting updates while a VM is active,
|
||||
// and the rounding mode has an impact on the conversion of floating-point values to/from strings.
|
||||
FPControlRegisterBackup fpcr_backup(FPControlRegister::GetDefault());
|
||||
|
||||
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
|
||||
SettingsInterface* si = Host::GetSettingsInterface();
|
||||
LoadCoreSettings(si);
|
||||
|
@ -561,6 +567,7 @@ void VMManager::ApplyCoreSettings()
|
|||
EmuConfig.CopyRuntimeConfig(old_config);
|
||||
|
||||
{
|
||||
FPControlRegisterBackup fpcr_backup(FPControlRegister::GetDefault());
|
||||
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
|
||||
LoadCoreSettings(Host::GetSettingsInterface());
|
||||
WarnAboutUnsafeSettings();
|
||||
|
@ -1226,7 +1233,7 @@ bool VMManager::Initialize(VMBootParameters boot_params)
|
|||
|
||||
s_cpu_implementation_changed = false;
|
||||
s_cpu_provider_pack->ApplyConfig();
|
||||
SetCPUState(EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVU0MXCSR, EmuConfig.Cpu.sseVU1MXCSR);
|
||||
FPControlRegister::SetCurrent(EmuConfig.Cpu.FPUFPCR);
|
||||
SysClearExecutionCache();
|
||||
memBindConditionalHandlers();
|
||||
SysMemory::Reset();
|
||||
|
@ -1324,12 +1331,6 @@ bool VMManager::Initialize(VMBootParameters boot_params)
|
|||
close_cdvd_files.Cancel();
|
||||
close_state.Cancel();
|
||||
|
||||
#if defined(_M_X86)
|
||||
s_mxcsr_saved = _mm_getcsr();
|
||||
#elif defined(_M_ARM64)
|
||||
s_mxcsr_saved = static_cast<u32>(a64_getfpcr());
|
||||
#endif
|
||||
|
||||
hwReset();
|
||||
|
||||
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
|
||||
|
@ -1393,11 +1394,7 @@ void VMManager::Shutdown(bool save_resume_state)
|
|||
|
||||
UpdateGameSettingsLayer();
|
||||
|
||||
#ifdef _M_X86
|
||||
_mm_setcsr(s_mxcsr_saved);
|
||||
#elif defined(_M_ARM64)
|
||||
a64_setfpcr(s_mxcsr_saved);
|
||||
#endif
|
||||
FPControlRegister::SetCurrent(FPControlRegister::GetDefault());
|
||||
|
||||
Patch::UnloadPatches();
|
||||
R3000A::ioman::reset();
|
||||
|
@ -2293,7 +2290,7 @@ void VMManager::CheckForCPUConfigChanges(const Pcsx2Config& old_config)
|
|||
}
|
||||
|
||||
Console.WriteLn("Updating CPU configuration...");
|
||||
SetCPUState(EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVU0MXCSR, EmuConfig.Cpu.sseVU1MXCSR);
|
||||
FPControlRegister::SetCurrent(EmuConfig.Cpu.FPUFPCR);
|
||||
SysClearExecutionCache();
|
||||
memBindConditionalHandlers();
|
||||
|
||||
|
@ -2600,7 +2597,7 @@ void VMManager::WarnAboutUnsafeSettings()
|
|||
TRANSLATE_SV(
|
||||
"VMManager", "Hardware Download Mode is not set to Accurate, this may break rendering in some games."));
|
||||
}
|
||||
if (EmuConfig.Cpu.sseMXCSR.GetRoundMode() != SSEround_Chop)
|
||||
if (EmuConfig.Cpu.FPUFPCR.GetRoundMode() != FPRoundMode::ChopZero)
|
||||
{
|
||||
append(ICON_FA_MICROCHIP,
|
||||
TRANSLATE_SV("VMManager", "EE FPU Round Mode is not set to default, this may break some games."));
|
||||
|
@ -2611,11 +2608,15 @@ void VMManager::WarnAboutUnsafeSettings()
|
|||
append(ICON_FA_MICROCHIP,
|
||||
TRANSLATE_SV("VMManager", "EE FPU Clamp Mode is not set to default, this may break some games."));
|
||||
}
|
||||
if (EmuConfig.Cpu.sseVU0MXCSR.GetRoundMode() != SSEround_Chop ||
|
||||
EmuConfig.Cpu.sseVU1MXCSR.GetRoundMode() != SSEround_Chop)
|
||||
if (EmuConfig.Cpu.VU0FPCR.GetRoundMode() != FPRoundMode::ChopZero)
|
||||
{
|
||||
append(ICON_FA_MICROCHIP,
|
||||
TRANSLATE_SV("VMManager", "VU Round Mode is not set to default, this may break some games."));
|
||||
TRANSLATE_SV("VMManager", "VU0 Round Mode is not set to default, this may break some games."));
|
||||
}
|
||||
if (EmuConfig.Cpu.VU1FPCR.GetRoundMode() != FPRoundMode::ChopZero)
|
||||
{
|
||||
append(ICON_FA_MICROCHIP,
|
||||
TRANSLATE_SV("VMManager", "VU1 Round Mode is not set to default, this may break some games."));
|
||||
}
|
||||
if (!EmuConfig.Cpu.Recompiler.vu0Overflow || EmuConfig.Cpu.Recompiler.vu0ExtraOverflow ||
|
||||
EmuConfig.Cpu.Recompiler.vu0SignOverflow || !EmuConfig.Cpu.Recompiler.vu1Overflow ||
|
||||
|
|
|
@ -248,8 +248,7 @@ void InterpVU0::Step()
|
|||
|
||||
void InterpVU0::Execute(u32 cycles)
|
||||
{
|
||||
const int originalRounding = fegetround();
|
||||
fesetround(g_sseVU0MXCSR.RoundingControl << 8);
|
||||
const FPControlRegisterBackup fpcr_backup(EmuConfig.Cpu.VU0FPCR);
|
||||
|
||||
VU0.VI[REG_TPC].UL <<= 3;
|
||||
VU0.flags &= ~VUFLAG_MFLAGSET;
|
||||
|
@ -302,7 +301,6 @@ void InterpVU0::Execute(u32 cycles)
|
|||
}
|
||||
VU0.cycle += cycle_change;
|
||||
}
|
||||
fesetround(originalRounding);
|
||||
|
||||
VU0.nextBlockCycles = (VU0.cycle - cpuRegs.cycle) + 1;
|
||||
}
|
||||
|
|
|
@ -258,8 +258,7 @@ void InterpVU1::Step()
|
|||
|
||||
void InterpVU1::Execute(u32 cycles)
|
||||
{
|
||||
const int originalRounding = fegetround();
|
||||
fesetround(g_sseVU1MXCSR.RoundingControl << 8);
|
||||
const FPControlRegisterBackup fpcr_backup(EmuConfig.Cpu.VU1FPCR);
|
||||
|
||||
VU1.VI[REG_TPC].UL <<= 3;
|
||||
u32 startcycles = VU1.cycle;
|
||||
|
@ -279,5 +278,4 @@ void InterpVU1::Execute(u32 cycles)
|
|||
}
|
||||
VU1.VI[REG_TPC].UL >>= 3;
|
||||
VU1.nextBlockCycles = (VU1.cycle - cpuRegs.cycle) + 1;
|
||||
fesetround(originalRounding);
|
||||
}
|
||||
|
|
|
@ -1093,7 +1093,7 @@ void recDIVhelper2(int regd, int regt) // Doesn't sets flags
|
|||
ClampValues(regd);
|
||||
}
|
||||
|
||||
alignas(16) static SSE_MXCSR roundmode_nearest, roundmode_neg;
|
||||
alignas(16) static FPControlRegister roundmode_nearest, roundmode_neg;
|
||||
|
||||
void recDIV_S_xmm(int info)
|
||||
{
|
||||
|
@ -1104,26 +1104,26 @@ void recDIV_S_xmm(int info)
|
|||
|
||||
if (CHECK_FPUNEGDIVHACK)
|
||||
{
|
||||
if (g_sseMXCSR.GetRoundMode() != SSEround_NegInf)
|
||||
if (EmuConfig.Cpu.FPUFPCR.GetRoundMode() != FPRoundMode::NegativeInfinity)
|
||||
{
|
||||
// Set roundmode to nearest since it isn't already
|
||||
//Console.WriteLn("div to negative inf");
|
||||
|
||||
roundmode_neg = g_sseMXCSR;
|
||||
roundmode_neg.SetRoundMode(SSEround_NegInf);
|
||||
roundmode_neg = EmuConfig.Cpu.FPUFPCR;
|
||||
roundmode_neg.SetRoundMode(FPRoundMode::NegativeInfinity);
|
||||
xLDMXCSR(ptr32[&roundmode_neg.bitmask]);
|
||||
roundmodeFlag = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest)
|
||||
if (EmuConfig.Cpu.FPUFPCR.GetRoundMode() != FPRoundMode::Nearest)
|
||||
{
|
||||
// Set roundmode to nearest since it isn't already
|
||||
//Console.WriteLn("div to nearest");
|
||||
|
||||
roundmode_nearest = g_sseMXCSR;
|
||||
roundmode_nearest.SetRoundMode(SSEround_Nearest);
|
||||
roundmode_nearest = EmuConfig.Cpu.FPUFPCR;
|
||||
roundmode_nearest.SetRoundMode(FPRoundMode::Nearest);
|
||||
xLDMXCSR(ptr32[&roundmode_nearest.bitmask]);
|
||||
roundmodeFlag = true;
|
||||
}
|
||||
|
@ -1191,7 +1191,7 @@ void recDIV_S_xmm(int info)
|
|||
break;
|
||||
}
|
||||
if (roundmodeFlag)
|
||||
xLDMXCSR(ptr32[&g_sseMXCSR.bitmask]);
|
||||
xLDMXCSR(ptr32[&EmuConfig.Cpu.FPUFPCR.bitmask]);
|
||||
_freeXMMreg(t0reg);
|
||||
}
|
||||
|
||||
|
@ -1768,12 +1768,12 @@ void recSQRT_S_xmm(int info)
|
|||
bool roundmodeFlag = false;
|
||||
//Console.WriteLn("FPU: SQRT");
|
||||
|
||||
if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest)
|
||||
if (EmuConfig.Cpu.FPUFPCR.GetRoundMode() != FPRoundMode::Nearest)
|
||||
{
|
||||
// Set roundmode to nearest if it isn't already
|
||||
//Console.WriteLn("sqrt to nearest");
|
||||
roundmode_nearest = g_sseMXCSR;
|
||||
roundmode_nearest.SetRoundMode(SSEround_Nearest);
|
||||
roundmode_nearest = EmuConfig.Cpu.FPUFPCR;
|
||||
roundmode_nearest.SetRoundMode(FPRoundMode::Nearest);
|
||||
xLDMXCSR(ptr32[&roundmode_nearest.bitmask]);
|
||||
roundmodeFlag = true;
|
||||
}
|
||||
|
@ -1805,7 +1805,7 @@ void recSQRT_S_xmm(int info)
|
|||
ClampValues(EEREC_D);
|
||||
|
||||
if (roundmodeFlag)
|
||||
xLDMXCSR(ptr32[&g_sseMXCSR.bitmask]);
|
||||
xLDMXCSR(ptr32[&EmuConfig.Cpu.FPUFPCR.bitmask]);
|
||||
}
|
||||
|
||||
FPURECOMPILE_CONSTCODE(SQRT_S, XMMINFO_WRITED | XMMINFO_READT);
|
||||
|
|
|
@ -652,7 +652,7 @@ void recDIVhelper2(int regd, int regt) // Doesn't sets flags
|
|||
ToPS2FPU(regd, false, regt, false);
|
||||
}
|
||||
|
||||
alignas(16) static SSE_MXCSR roundmode_nearest, roundmode_neg;
|
||||
alignas(16) static FPControlRegister roundmode_nearest, roundmode_neg;
|
||||
|
||||
void recDIV_S_xmm(int info)
|
||||
{
|
||||
|
@ -662,26 +662,26 @@ void recDIV_S_xmm(int info)
|
|||
|
||||
if (CHECK_FPUNEGDIVHACK)
|
||||
{
|
||||
if (g_sseMXCSR.GetRoundMode() != SSEround_NegInf)
|
||||
if (EmuConfig.Cpu.FPUFPCR.GetRoundMode() != FPRoundMode::NegativeInfinity)
|
||||
{
|
||||
// Set roundmode to nearest since it isn't already
|
||||
//Console.WriteLn("div to negative inf");
|
||||
|
||||
roundmode_neg = g_sseMXCSR;
|
||||
roundmode_neg.SetRoundMode(SSEround_NegInf);
|
||||
roundmode_neg = EmuConfig.Cpu.FPUFPCR;
|
||||
roundmode_neg.SetRoundMode(FPRoundMode::NegativeInfinity);
|
||||
xLDMXCSR(ptr32[&roundmode_neg.bitmask]);
|
||||
roundmodeFlag = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest)
|
||||
if (EmuConfig.Cpu.FPUFPCR.GetRoundMode() != FPRoundMode::Nearest)
|
||||
{
|
||||
// Set roundmode to nearest since it isn't already
|
||||
//Console.WriteLn("div to nearest");
|
||||
|
||||
roundmode_nearest = g_sseMXCSR;
|
||||
roundmode_nearest.SetRoundMode(SSEround_Nearest);
|
||||
roundmode_nearest = EmuConfig.Cpu.FPUFPCR;
|
||||
roundmode_nearest.SetRoundMode(FPRoundMode::Nearest);
|
||||
xLDMXCSR(ptr32[&roundmode_nearest.bitmask]);
|
||||
roundmodeFlag = true;
|
||||
}
|
||||
|
@ -699,7 +699,7 @@ void recDIV_S_xmm(int info)
|
|||
xMOVSS(xRegisterSSE(EEREC_D), xRegisterSSE(sreg));
|
||||
|
||||
if (roundmodeFlag)
|
||||
xLDMXCSR(ptr32[&g_sseMXCSR]);
|
||||
xLDMXCSR(ptr32[&EmuConfig.Cpu.FPUFPCR.bitmask]);
|
||||
_freeXMMreg(sreg); _freeXMMreg(treg);
|
||||
}
|
||||
|
||||
|
@ -952,12 +952,12 @@ void recSQRT_S_xmm(int info)
|
|||
const int t1reg = _allocTempXMMreg(XMMT_FPS);
|
||||
//Console.WriteLn("FPU: SQRT");
|
||||
|
||||
if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest)
|
||||
if (EmuConfig.Cpu.FPUFPCR.GetRoundMode() != FPRoundMode::Nearest)
|
||||
{
|
||||
// Set roundmode to nearest if it isn't already
|
||||
//Console.WriteLn("sqrt to nearest");
|
||||
roundmode_nearest = g_sseMXCSR;
|
||||
roundmode_nearest.SetRoundMode(SSEround_Nearest);
|
||||
roundmode_nearest = EmuConfig.Cpu.FPUFPCR;
|
||||
roundmode_nearest.SetRoundMode(FPRoundMode::Nearest);
|
||||
xLDMXCSR(ptr32[&roundmode_nearest.bitmask]);
|
||||
roundmodeFlag = 1;
|
||||
}
|
||||
|
@ -989,7 +989,7 @@ void recSQRT_S_xmm(int info)
|
|||
ToPS2FPU(EEREC_D, false, t1reg, false);
|
||||
|
||||
if (roundmodeFlag == 1)
|
||||
xLDMXCSR(ptr32[&g_sseMXCSR.bitmask]);
|
||||
xLDMXCSR(ptr32[&EmuConfig.Cpu.FPUFPCR.bitmask]);
|
||||
|
||||
_freeXMMreg(t1reg);
|
||||
}
|
||||
|
@ -1074,12 +1074,12 @@ void recRSQRT_S_xmm(int info)
|
|||
// behavior for both recs? --air
|
||||
|
||||
bool roundmodeFlag = false;
|
||||
if (g_sseMXCSR.GetRoundMode() != SSEround_Nearest)
|
||||
if (EmuConfig.Cpu.FPUFPCR.GetRoundMode() != FPRoundMode::Nearest)
|
||||
{
|
||||
// Set roundmode to nearest if it isn't already
|
||||
//Console.WriteLn("sqrt to nearest");
|
||||
roundmode_nearest = g_sseMXCSR;
|
||||
roundmode_nearest.SetRoundMode(SSEround_Nearest);
|
||||
roundmode_nearest = EmuConfig.Cpu.FPUFPCR;
|
||||
roundmode_nearest.SetRoundMode(FPRoundMode::Nearest);
|
||||
xLDMXCSR(ptr32[&roundmode_nearest.bitmask]);
|
||||
roundmodeFlag = true;
|
||||
}
|
||||
|
@ -1096,7 +1096,7 @@ void recRSQRT_S_xmm(int info)
|
|||
_freeXMMreg(treg); _freeXMMreg(sreg);
|
||||
|
||||
if (roundmodeFlag)
|
||||
xLDMXCSR(ptr32[&g_sseMXCSR.bitmask]);
|
||||
xLDMXCSR(ptr32[&EmuConfig.Cpu.FPUFPCR.bitmask]);
|
||||
}
|
||||
|
||||
FPURECOMPILE_CONSTCODE(RSQRT_S, XMMINFO_WRITED | XMMINFO_READS | XMMINFO_READT);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Config.h"
|
||||
#include "cpuinfo.h"
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
@ -15,7 +16,7 @@ static bool mvuNeedsFPCRUpdate(mV)
|
|||
return true;
|
||||
|
||||
// otherwise only emit when it's different to the EE
|
||||
return g_sseMXCSR.bitmask != (isVU0 ? g_sseVU0MXCSR.bitmask : g_sseVU1MXCSR.bitmask);
|
||||
return EmuConfig.Cpu.FPUFPCR.bitmask != (isVU0 ? EmuConfig.Cpu.VU0FPCR.bitmask : EmuConfig.Cpu.VU1FPCR.bitmask);
|
||||
}
|
||||
|
||||
// Generates the code for entering/exit recompiled blocks
|
||||
|
@ -32,7 +33,7 @@ void mVUdispatcherAB(mV)
|
|||
|
||||
// Load VU's MXCSR state
|
||||
if (mvuNeedsFPCRUpdate(mVU))
|
||||
xLDMXCSR(ptr32[isVU0 ? &g_sseVU0MXCSR.bitmask : &g_sseVU1MXCSR.bitmask]);
|
||||
xLDMXCSR(ptr32[isVU0 ? &EmuConfig.Cpu.VU0FPCR.bitmask : &EmuConfig.Cpu.VU1FPCR.bitmask]);
|
||||
|
||||
// Load Regs
|
||||
xMOVAPS (xmmT1, ptr128[&mVU.regs().VI[REG_P].UL]);
|
||||
|
@ -72,7 +73,7 @@ void mVUdispatcherAB(mV)
|
|||
|
||||
// Load EE's MXCSR state
|
||||
if (mvuNeedsFPCRUpdate(mVU))
|
||||
xLDMXCSR(ptr32[&g_sseMXCSR.bitmask]);
|
||||
xLDMXCSR(ptr32[&EmuConfig.Cpu.FPUFPCR.bitmask]);
|
||||
|
||||
// = The first two DWORD or smaller arguments are passed in ECX and EDX registers;
|
||||
// all other arguments are passed right to left.
|
||||
|
@ -96,7 +97,7 @@ void mVUdispatcherCD(mV)
|
|||
|
||||
// Load VU's MXCSR state
|
||||
if (mvuNeedsFPCRUpdate(mVU))
|
||||
xLDMXCSR(ptr32[isVU0 ? &g_sseVU0MXCSR.bitmask : &g_sseVU1MXCSR.bitmask]);
|
||||
xLDMXCSR(ptr32[isVU0 ? &EmuConfig.Cpu.VU0FPCR.bitmask : &EmuConfig.Cpu.VU1FPCR.bitmask]);
|
||||
|
||||
mVUrestoreRegs(mVU);
|
||||
xMOV(gprF0, ptr32[&mVU.regs().micro_statusflags[0]]);
|
||||
|
@ -117,7 +118,7 @@ void mVUdispatcherCD(mV)
|
|||
|
||||
// Load EE's MXCSR state
|
||||
if (mvuNeedsFPCRUpdate(mVU))
|
||||
xLDMXCSR(ptr32[&g_sseMXCSR.bitmask]);
|
||||
xLDMXCSR(ptr32[&EmuConfig.Cpu.FPUFPCR.bitmask]);
|
||||
}
|
||||
|
||||
xRET();
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "pcsx2/GS/GSBlock.h"
|
||||
#include "pcsx2/GS/GSClut.h"
|
||||
#include "pcsx2/GS/MultiISA.h"
|
||||
#include "common/emitter/tools.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -12,8 +11,6 @@
|
|||
|
||||
#ifdef MULTI_ISA_UNSHARED_COMPILATION
|
||||
|
||||
#include "common/emitter/tools.h"
|
||||
|
||||
enum class TestISA
|
||||
{
|
||||
isa_sse4,
|
||||
|
|
Loading…
Reference in New Issue