2023-09-03 04:30:26 +00:00
|
|
|
// SPDX-FileCopyrightText: 2016 iCatButler, 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
#include "cpu_pgxp.h"
|
2021-05-25 10:01:00 +00:00
|
|
|
#include "bus.h"
|
|
|
|
#include "cpu_core.h"
|
2024-05-17 07:15:38 +00:00
|
|
|
#include "cpu_disasm.h"
|
2020-08-01 14:25:07 +00:00
|
|
|
#include "settings.h"
|
2023-09-03 04:30:26 +00:00
|
|
|
|
2024-05-17 07:15:38 +00:00
|
|
|
#include "util/gpu_device.h"
|
|
|
|
|
2023-11-28 15:55:54 +00:00
|
|
|
#include "common/assert.h"
|
2023-09-03 04:30:26 +00:00
|
|
|
#include "common/log.h"
|
|
|
|
|
2020-08-19 15:21:36 +00:00
|
|
|
#include <climits>
|
2020-09-02 12:44:52 +00:00
|
|
|
#include <cmath>
|
2023-09-03 04:30:26 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
Log_SetChannel(CPU::PGXP);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2024-05-17 07:15:38 +00:00
|
|
|
// #define LOG_VALUES 1
|
|
|
|
// #define LOG_LOOKUPS 1
|
|
|
|
|
2024-05-17 09:39:23 +00:00
|
|
|
// TODO: Get rid of all the rs/rt subscripting.
|
2024-07-08 07:00:04 +00:00
|
|
|
// TODO: Don't update flags on Validate(), instead return it.
|
2024-05-17 09:39:23 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
namespace CPU::PGXP {
|
2023-11-18 06:21:51 +00:00
|
|
|
namespace {
|
2021-06-12 12:02:28 +00:00
|
|
|
|
|
|
|
enum : u32
|
|
|
|
{
|
2024-05-24 16:06:40 +00:00
|
|
|
VERTEX_CACHE_WIDTH = 2048,
|
|
|
|
VERTEX_CACHE_HEIGHT = 2048,
|
2021-06-12 12:02:28 +00:00
|
|
|
VERTEX_CACHE_SIZE = VERTEX_CACHE_WIDTH * VERTEX_CACHE_HEIGHT,
|
2023-11-06 08:09:27 +00:00
|
|
|
PGXP_MEM_SIZE = (static_cast<u32>(Bus::RAM_8MB_SIZE) + static_cast<u32>(CPU::SCRATCHPAD_SIZE)) / 4,
|
2024-05-17 09:39:23 +00:00
|
|
|
PGXP_MEM_SCRATCH_OFFSET = Bus::RAM_8MB_SIZE / 4,
|
2021-06-12 12:02:28 +00:00
|
|
|
};
|
|
|
|
|
2024-05-17 09:39:23 +00:00
|
|
|
enum : u32
|
|
|
|
{
|
|
|
|
COMP_X,
|
|
|
|
COMP_Y,
|
|
|
|
COMP_Z,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum : u32
|
|
|
|
{
|
|
|
|
VALID_X = (1u << 0),
|
|
|
|
VALID_Y = (1u << 1),
|
|
|
|
VALID_Z = (1u << 2),
|
2024-05-17 12:25:02 +00:00
|
|
|
VALID_LOWZ = (1u << 16), // Valid Z from the low part of a 32-bit value.
|
|
|
|
VALID_HIGHZ = (1u << 17), // Valid Z from the high part of a 32-bit value.
|
|
|
|
VALID_TAINTED_Z = (1u << 31), // X/Y has been changed, Z may not be accurate.
|
2024-05-17 11:18:39 +00:00
|
|
|
|
2024-05-17 09:39:23 +00:00
|
|
|
VALID_XY = (VALID_X | VALID_Y),
|
|
|
|
VALID_XYZ = (VALID_X | VALID_Y | VALID_Z),
|
|
|
|
VALID_ALL = (VALID_X | VALID_Y | VALID_Z),
|
|
|
|
};
|
2021-06-12 12:02:28 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
union psx_value
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2023-12-06 09:54:45 +00:00
|
|
|
u32 d;
|
|
|
|
s32 sd;
|
2020-08-01 14:25:07 +00:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
u16 l, h;
|
|
|
|
} w;
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
s16 l, h;
|
|
|
|
} sw;
|
2023-12-06 06:45:17 +00:00
|
|
|
};
|
2023-11-18 06:21:51 +00:00
|
|
|
} // namespace
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2024-05-24 16:06:40 +00:00
|
|
|
static void CacheVertex(u32 value, const PGXP_value& vertex);
|
|
|
|
static PGXP_value* GetCachedVertex(u32 value);
|
2023-11-18 06:21:51 +00:00
|
|
|
|
|
|
|
static float TruncateVertexPosition(float p);
|
|
|
|
static bool IsWithinTolerance(float precise_x, float precise_y, int int_x, int int_y);
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
static void MakeValid(PGXP_value* pV, u32 psxV);
|
2023-12-06 06:45:17 +00:00
|
|
|
|
2020-08-01 14:25:07 +00:00
|
|
|
static void Validate(PGXP_value* pV, u32 psxV);
|
|
|
|
static void MaskValidate(PGXP_value* pV, u32 psxV, u32 mask, u32 validMask);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
static double f16Sign(double in);
|
|
|
|
static double f16Unsign(double in);
|
|
|
|
static double f16Overflow(double in);
|
|
|
|
|
2020-08-01 14:25:07 +00:00
|
|
|
static PGXP_value* GetPtr(u32 addr);
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
static void ValidateAndCopyMem(PGXP_value* dest, u32 addr, u32 value);
|
|
|
|
static void ValidateAndCopyMem16(PGXP_value* dest, u32 addr, u32 value, bool sign);
|
|
|
|
|
|
|
|
static void CPU_MTC2_int(const PGXP_value& value, u32 reg);
|
2023-11-18 06:21:51 +00:00
|
|
|
static void CPU_BITWISE(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
static void WriteMem(const PGXP_value* value, u32 addr);
|
|
|
|
static void WriteMem16(const PGXP_value* src, u32 addr);
|
|
|
|
|
2024-05-17 09:00:25 +00:00
|
|
|
static void CopyZIfMissing(PGXP_value& dst, const PGXP_value& src);
|
|
|
|
static void SelectZ(PGXP_value& dst, const PGXP_value& src1, const PGXP_value& src2);
|
|
|
|
|
2024-05-17 07:15:38 +00:00
|
|
|
#ifdef LOG_VALUES
|
|
|
|
static void LogInstruction(u32 pc, u32 instr);
|
|
|
|
static void LogValue(const char* name, u32 rval, const PGXP_value* val);
|
|
|
|
static void LogValueStr(SmallStringBase& str, const char* name, u32 rval, const PGXP_value* val);
|
|
|
|
|
|
|
|
// clang-format off
|
|
|
|
#define LOG_VALUES_NV() do { LogInstruction(CPU::g_state.current_instruction_pc, instr); } while (0)
|
|
|
|
#define LOG_VALUES_1(name, rval, val) do { LogInstruction(CPU::g_state.current_instruction_pc, instr); LogValue(name, rval, val); } while (0)
|
|
|
|
#define LOG_VALUES_C1(rnum, rval) do { LogInstruction(CPU::g_state.current_instruction_pc,instr); LogValue(CPU::GetRegName(static_cast<CPU::Reg>(rnum)), rval, &g_state.pgxp_gpr[static_cast<u32>(rnum)]); } while(0)
|
|
|
|
#define LOG_VALUES_C2(r1num, r1val, r2num, r2val) do { LogInstruction(CPU::g_state.current_instruction_pc,instr); LogValue(CPU::GetRegName(static_cast<CPU::Reg>(r1num)), r1val, &g_state.pgxp_gpr[static_cast<u32>(r1num)]); LogValue(CPU::GetRegName(static_cast<CPU::Reg>(r2num)), r2val, &g_state.pgxp_gpr[static_cast<u32>(r2num)]); } while(0)
|
|
|
|
#define LOG_VALUES_LOAD(addr, val) do { LogInstruction(CPU::g_state.current_instruction_pc,instr); LogValue(TinyString::from_format("MEM[{:08X}]", addr).c_str(), val, GetPtr(addr)); } while(0)
|
|
|
|
#define LOG_VALUES_STORE(rnum, rval, addr) do { LOG_VALUES_C1(rnum, rval); std::fprintf(s_log, " addr=%08X", addr); } while(0)
|
|
|
|
#else
|
|
|
|
#define LOG_VALUES_NV() (void)0
|
|
|
|
#define LOG_VALUES_1(name, rval, val) (void)0
|
|
|
|
#define LOG_VALUES_C1(rnum, rval) (void)0
|
|
|
|
#define LOG_VALUES_C2(r1num, r1val, r2num, r2val) (void)0
|
|
|
|
#define LOG_VALUES_LOAD(addr, val) (void)0
|
|
|
|
#define LOG_VALUES_STORE(rnum, rval, addr) (void)0
|
|
|
|
#endif
|
|
|
|
// clang-format on
|
|
|
|
|
2024-05-17 09:39:23 +00:00
|
|
|
static constexpr PGXP_value PGXP_value_invalid = {0.f, 0.f, 0.f, 0, 0};
|
2024-05-17 11:18:39 +00:00
|
|
|
static constexpr PGXP_value PGXP_value_zero = {0.f, 0.f, 0.f, 0, VALID_XY};
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
static PGXP_value* s_mem = nullptr;
|
|
|
|
static PGXP_value* s_vertex_cache = nullptr;
|
2024-05-17 07:15:38 +00:00
|
|
|
|
|
|
|
#ifdef LOG_VALUES
|
|
|
|
static std::FILE* s_log;
|
|
|
|
#endif
|
2023-12-06 06:45:17 +00:00
|
|
|
} // namespace CPU::PGXP
|
2020-09-02 12:44:52 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::Initialize()
|
|
|
|
{
|
|
|
|
std::memset(g_state.pgxp_gpr, 0, sizeof(g_state.pgxp_gpr));
|
|
|
|
std::memset(g_state.pgxp_cop0, 0, sizeof(g_state.pgxp_cop0));
|
|
|
|
std::memset(g_state.pgxp_gte, 0, sizeof(g_state.pgxp_gte));
|
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
if (!s_mem)
|
2023-12-06 06:45:17 +00:00
|
|
|
{
|
2023-12-06 09:54:45 +00:00
|
|
|
s_mem = static_cast<PGXP_value*>(std::calloc(PGXP_MEM_SIZE, sizeof(PGXP_value)));
|
|
|
|
if (!s_mem)
|
2023-12-06 06:45:17 +00:00
|
|
|
Panic("Failed to allocate PGXP memory");
|
|
|
|
}
|
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
if (g_settings.gpu_pgxp_vertex_cache && !s_vertex_cache)
|
2023-12-06 06:45:17 +00:00
|
|
|
{
|
2023-12-06 09:54:45 +00:00
|
|
|
s_vertex_cache = static_cast<PGXP_value*>(std::calloc(VERTEX_CACHE_SIZE, sizeof(PGXP_value)));
|
|
|
|
if (!s_vertex_cache)
|
2023-12-06 06:45:17 +00:00
|
|
|
{
|
2024-05-23 10:55:28 +00:00
|
|
|
ERROR_LOG("Failed to allocate memory for vertex cache, disabling.");
|
2023-12-06 06:45:17 +00:00
|
|
|
g_settings.gpu_pgxp_vertex_cache = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
if (s_vertex_cache)
|
|
|
|
std::memset(s_vertex_cache, 0, sizeof(PGXP_value) * VERTEX_CACHE_SIZE);
|
2023-12-06 06:45:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPU::PGXP::Reset()
|
|
|
|
{
|
|
|
|
std::memset(g_state.pgxp_gpr, 0, sizeof(g_state.pgxp_gpr));
|
|
|
|
std::memset(g_state.pgxp_cop0, 0, sizeof(g_state.pgxp_cop0));
|
|
|
|
std::memset(g_state.pgxp_gte, 0, sizeof(g_state.pgxp_gte));
|
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
if (s_mem)
|
|
|
|
std::memset(s_mem, 0, sizeof(PGXP_value) * PGXP_MEM_SIZE);
|
2023-12-06 06:45:17 +00:00
|
|
|
|
2024-05-24 16:06:40 +00:00
|
|
|
if (g_settings.gpu_pgxp_vertex_cache && s_vertex_cache)
|
2023-12-06 09:54:45 +00:00
|
|
|
std::memset(s_vertex_cache, 0, sizeof(PGXP_value) * VERTEX_CACHE_SIZE);
|
2023-12-06 06:45:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CPU::PGXP::Shutdown()
|
|
|
|
{
|
2023-12-06 09:54:45 +00:00
|
|
|
if (s_vertex_cache)
|
2023-12-06 06:45:17 +00:00
|
|
|
{
|
2023-12-06 09:54:45 +00:00
|
|
|
std::free(s_vertex_cache);
|
|
|
|
s_vertex_cache = nullptr;
|
2023-12-06 06:45:17 +00:00
|
|
|
}
|
2023-12-06 09:54:45 +00:00
|
|
|
if (s_mem)
|
2023-12-06 06:45:17 +00:00
|
|
|
{
|
2023-12-06 09:54:45 +00:00
|
|
|
std::free(s_mem);
|
|
|
|
s_mem = nullptr;
|
2023-12-06 06:45:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::memset(g_state.pgxp_gte, 0, sizeof(g_state.pgxp_gte));
|
|
|
|
std::memset(g_state.pgxp_gpr, 0, sizeof(g_state.pgxp_gpr));
|
|
|
|
std::memset(g_state.pgxp_cop0, 0, sizeof(g_state.pgxp_cop0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Instruction register decoding
|
|
|
|
#define op(_instr) (_instr >> 26) // The op part of the instruction register
|
2024-05-17 07:15:38 +00:00
|
|
|
#define func(_instr) ((_instr) & 0x3F) // The funct part of the instruction register
|
2023-12-06 06:45:17 +00:00
|
|
|
#define sa(_instr) ((_instr >> 6) & 0x1F) // The sa part of the instruction register
|
|
|
|
#define rd(_instr) ((_instr >> 11) & 0x1F) // The rd part of the instruction register
|
|
|
|
#define rt(_instr) ((_instr >> 16) & 0x1F) // The rt part of the instruction register
|
|
|
|
#define rs(_instr) ((_instr >> 21) & 0x1F) // The rs part of the instruction register
|
|
|
|
#define imm(_instr) (_instr & 0xFFFF) // The immediate part of the instruction register
|
|
|
|
#define cop2idx(_instr) (((_instr >> 11) & 0x1F) | ((_instr >> 17) & 0x20))
|
|
|
|
|
|
|
|
#define SX0 (g_state.pgxp_gte[12].x)
|
|
|
|
#define SY0 (g_state.pgxp_gte[12].y)
|
|
|
|
#define SX1 (g_state.pgxp_gte[13].x)
|
|
|
|
#define SY1 (g_state.pgxp_gte[13].y)
|
|
|
|
#define SX2 (g_state.pgxp_gte[14].x)
|
|
|
|
#define SY2 (g_state.pgxp_gte[14].y)
|
|
|
|
|
|
|
|
#define SXY0 (g_state.pgxp_gte[12])
|
|
|
|
#define SXY1 (g_state.pgxp_gte[13])
|
|
|
|
#define SXY2 (g_state.pgxp_gte[14])
|
|
|
|
#define SXYP (g_state.pgxp_gte[15])
|
|
|
|
|
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::MakeValid(PGXP_value* pV, u32 psxV)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 09:39:23 +00:00
|
|
|
if ((pV->flags & VALID_XY) == VALID_XY)
|
2023-12-06 09:54:45 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
pV->x = static_cast<float>(static_cast<s16>(Truncate16(psxV)));
|
|
|
|
pV->y = static_cast<float>(static_cast<s16>(Truncate16(psxV >> 16)));
|
|
|
|
pV->z = 0.0f;
|
2024-05-17 11:18:39 +00:00
|
|
|
pV->flags = VALID_XY | VALID_TAINTED_Z;
|
2023-12-06 09:54:45 +00:00
|
|
|
pV->value = psxV;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::Validate(PGXP_value* pV, u32 psxV)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2024-05-17 10:52:21 +00:00
|
|
|
pV->flags = (pV->value == psxV) ? pV->flags : 0;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::MaskValidate(PGXP_value* pV, u32 psxV, u32 mask, u32 validMask)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2024-05-17 10:52:21 +00:00
|
|
|
pV->flags = ((pV->value & mask) == (psxV & mask)) ? pV->flags : (pV->flags & ~validMask);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE double CPU::PGXP::f16Sign(double in)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2023-11-20 10:56:21 +00:00
|
|
|
const s32 s = static_cast<s32>(static_cast<s64>(in * (USHRT_MAX + 1)));
|
2023-10-31 15:32:29 +00:00
|
|
|
return static_cast<double>(s) / static_cast<double>(USHRT_MAX + 1);
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE double CPU::PGXP::f16Unsign(double in)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2023-10-31 15:32:29 +00:00
|
|
|
return (in >= 0) ? in : (in + (USHRT_MAX + 1));
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE double CPU::PGXP::f16Overflow(double in)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
|
|
|
double out = 0;
|
|
|
|
s64 v = ((s64)in) >> 16;
|
|
|
|
out = (double)v;
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE CPU::PGXP_value* CPU::PGXP::GetPtr(u32 addr)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
#if 0
|
|
|
|
if ((addr & CPU::PHYSICAL_MEMORY_ADDRESS_MASK) >= 0x0017A2B4 &&
|
|
|
|
(addr & CPU::PHYSICAL_MEMORY_ADDRESS_MASK) <= 0x0017A2B4)
|
|
|
|
__debugbreak();
|
|
|
|
#endif
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
if ((addr & SCRATCHPAD_ADDR_MASK) == SCRATCHPAD_ADDR)
|
2023-12-06 09:54:45 +00:00
|
|
|
return &s_mem[PGXP_MEM_SCRATCH_OFFSET + ((addr & SCRATCHPAD_OFFSET_MASK) >> 2)];
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
const u32 paddr = (addr & PHYSICAL_MEMORY_ADDRESS_MASK);
|
2021-05-25 10:01:00 +00:00
|
|
|
if (paddr < Bus::RAM_MIRROR_END)
|
2023-12-06 09:54:45 +00:00
|
|
|
return &s_mem[(paddr & Bus::g_ram_mask) >> 2];
|
2021-05-25 10:01:00 +00:00
|
|
|
else
|
|
|
|
return nullptr;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::ValidateAndCopyMem(PGXP_value* dest, u32 addr, u32 value)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
PGXP_value* pMem = GetPtr(addr);
|
2023-12-06 09:54:45 +00:00
|
|
|
if (!pMem)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2023-12-06 09:54:45 +00:00
|
|
|
*dest = PGXP_value_invalid;
|
2020-08-01 14:25:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
Validate(pMem, value);
|
|
|
|
*dest = *pMem;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::ValidateAndCopyMem16(PGXP_value* dest, u32 addr, u32 value, bool sign)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
PGXP_value* pMem = GetPtr(addr);
|
2023-12-06 09:54:45 +00:00
|
|
|
if (!pMem)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2023-12-06 09:54:45 +00:00
|
|
|
*dest = PGXP_value_invalid;
|
|
|
|
return;
|
|
|
|
}
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
psx_value val{0}, mask{0};
|
|
|
|
u32 valid_mask = 0;
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
// determine if high or low word
|
|
|
|
const bool hiword = ((addr & 2) != 0);
|
|
|
|
if (hiword)
|
|
|
|
{
|
|
|
|
val.w.h = static_cast<u16>(value);
|
|
|
|
mask.w.h = 0xFFFF;
|
2024-05-17 09:39:23 +00:00
|
|
|
valid_mask = VALID_Y;
|
2023-12-06 09:54:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
val.w.l = static_cast<u16>(value);
|
|
|
|
mask.w.l = 0xFFFF;
|
2024-05-17 09:39:23 +00:00
|
|
|
valid_mask = VALID_X;
|
2023-12-06 09:54:45 +00:00
|
|
|
}
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
// validate and copy whole value
|
|
|
|
MaskValidate(pMem, val.d, mask.d, valid_mask);
|
|
|
|
*dest = *pMem;
|
2023-12-05 08:20:54 +00:00
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
// if high word then shift
|
|
|
|
if (hiword)
|
|
|
|
{
|
|
|
|
dest->x = dest->y;
|
2024-05-17 09:39:23 +00:00
|
|
|
dest->SetValid(COMP_X, dest->HasValid(COMP_Y));
|
2023-12-06 09:54:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// only set y as valid if x is also valid.. don't want to make fake values
|
2024-05-17 09:39:23 +00:00
|
|
|
if (dest->HasValid(COMP_X))
|
2023-12-06 09:54:45 +00:00
|
|
|
{
|
|
|
|
dest->y = (dest->x < 0) ? -1.f * sign : 0.f;
|
2024-05-17 09:39:23 +00:00
|
|
|
dest->SetValid(COMP_Y);
|
2023-12-06 09:54:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest->y = 0.0f;
|
2024-05-17 09:39:23 +00:00
|
|
|
dest->SetValid(COMP_Y, false);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
dest->value = value;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::WriteMem(const PGXP_value* value, u32 addr)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
PGXP_value* pMem = GetPtr(addr);
|
|
|
|
|
|
|
|
if (pMem)
|
2024-05-17 12:25:02 +00:00
|
|
|
{
|
2020-08-01 14:25:07 +00:00
|
|
|
*pMem = *value;
|
2024-05-17 12:25:02 +00:00
|
|
|
pMem->flags |= VALID_LOWZ | VALID_HIGHZ;
|
|
|
|
}
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::WriteMem16(const PGXP_value* src, u32 addr)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
PGXP_value* dest = GetPtr(addr);
|
2023-12-06 09:54:45 +00:00
|
|
|
if (!dest)
|
|
|
|
return;
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
// determine if high or low word
|
|
|
|
const bool hiword = ((addr & 2) != 0);
|
|
|
|
if (hiword)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2023-12-06 09:54:45 +00:00
|
|
|
dest->y = src->x;
|
2024-05-17 09:39:23 +00:00
|
|
|
dest->SetValid(COMP_Y, src->HasValid(COMP_X));
|
2023-12-06 09:54:45 +00:00
|
|
|
dest->value = (dest->value & UINT32_C(0x0000FFFF)) | (src->value << 16);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest->x = src->x;
|
2024-05-17 09:39:23 +00:00
|
|
|
dest->SetValid(COMP_X, src->HasValid(COMP_X));
|
2023-12-06 09:54:45 +00:00
|
|
|
dest->value = (dest->value & UINT32_C(0xFFFF0000)) | (src->value & UINT32_C(0x0000FFFF));
|
|
|
|
}
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2023-12-06 09:54:45 +00:00
|
|
|
// overwrite z/w if valid
|
2024-05-17 09:39:23 +00:00
|
|
|
// TODO: Check modified
|
|
|
|
if (src->HasValid(COMP_Z))
|
2023-12-06 09:54:45 +00:00
|
|
|
{
|
|
|
|
dest->z = src->z;
|
2024-05-17 09:39:23 +00:00
|
|
|
dest->SetValid(COMP_Z);
|
2024-05-17 12:25:02 +00:00
|
|
|
dest->flags |= hiword ? VALID_HIGHZ : VALID_LOWZ;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest->flags &= hiword ? ~VALID_HIGHZ : ~VALID_LOWZ;
|
|
|
|
if (dest->flags & VALID_Z && !(dest->flags & (VALID_HIGHZ | VALID_LOWZ)))
|
|
|
|
dest->flags &= ~VALID_Z;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-17 09:00:25 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::CopyZIfMissing(PGXP_value& dst, const PGXP_value& src)
|
|
|
|
{
|
2024-05-17 11:18:39 +00:00
|
|
|
dst.z = dst.HasValid(COMP_Z) ? dst.z : src.z;
|
2024-05-17 09:39:23 +00:00
|
|
|
dst.flags |= (src.flags & VALID_Z);
|
2024-05-17 09:00:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::SelectZ(PGXP_value& dst, const PGXP_value& src1, const PGXP_value& src2)
|
|
|
|
{
|
2024-05-17 11:18:39 +00:00
|
|
|
// Prefer src2 if src1 is missing Z, or is potentially an imprecise value, when src2 is precise.
|
|
|
|
dst.z = (!(src1.flags & VALID_Z) ||
|
|
|
|
(src1.flags & VALID_TAINTED_Z && (src2.flags & (VALID_Z | VALID_TAINTED_Z)) == VALID_Z)) ?
|
|
|
|
src2.z :
|
|
|
|
src1.z;
|
2024-05-17 09:39:23 +00:00
|
|
|
dst.flags |= ((src1.flags | src2.flags) & VALID_Z);
|
2024-05-17 09:00:25 +00:00
|
|
|
}
|
|
|
|
|
2024-05-17 07:15:38 +00:00
|
|
|
#ifdef LOG_VALUES
|
|
|
|
void CPU::PGXP::LogInstruction(u32 pc, u32 instr)
|
|
|
|
{
|
|
|
|
if (!s_log) [[unlikely]]
|
|
|
|
{
|
|
|
|
s_log = std::fopen("pgxp.log", "wb");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::fflush(s_log);
|
|
|
|
std::fputc('\n', s_log);
|
|
|
|
}
|
|
|
|
|
|
|
|
SmallString str;
|
|
|
|
DisassembleInstruction(&str, pc, instr);
|
|
|
|
std::fprintf(s_log, "%08X %08X %-20s", pc, instr, str.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPU::PGXP::LogValue(const char* name, u32 rval, const PGXP_value* val)
|
|
|
|
{
|
|
|
|
if (!s_log) [[unlikely]]
|
|
|
|
return;
|
|
|
|
|
|
|
|
SmallString str;
|
|
|
|
LogValueStr(str, name, rval, val);
|
|
|
|
std::fprintf(s_log, " %s", str.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPU::PGXP::LogValueStr(SmallStringBase& str, const char* name, u32 rval, const PGXP_value* val)
|
|
|
|
{
|
|
|
|
str.append_format("{}=[{:08X}", name, rval);
|
|
|
|
if (!val)
|
|
|
|
{
|
|
|
|
str.append(", NULL]");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (val->value != rval)
|
|
|
|
str.append_format(", PGXP{:08X}", val->value);
|
|
|
|
|
|
|
|
str.append_format(", {{{},{},{}}}", val->x, val->y, val->z);
|
|
|
|
|
2024-05-17 11:18:39 +00:00
|
|
|
if (val->flags & VALID_ALL)
|
2024-05-17 07:15:38 +00:00
|
|
|
{
|
|
|
|
str.append(", valid=");
|
2024-05-17 09:39:23 +00:00
|
|
|
if (val->flags & VALID_X)
|
2024-05-17 07:15:38 +00:00
|
|
|
str.append('X');
|
2024-05-17 09:39:23 +00:00
|
|
|
if (val->flags & VALID_Y)
|
2024-05-17 07:15:38 +00:00
|
|
|
str.append('Y');
|
2024-05-17 09:39:23 +00:00
|
|
|
if (val->flags & VALID_Z)
|
2024-05-17 07:15:38 +00:00
|
|
|
str.append('Z');
|
|
|
|
}
|
|
|
|
|
2024-05-17 11:18:39 +00:00
|
|
|
// if (val->flags & VALID_TAINTED_Z)
|
|
|
|
// str.append(", tainted");
|
|
|
|
|
2024-05-17 07:15:38 +00:00
|
|
|
str.append(']');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2024-05-24 16:06:40 +00:00
|
|
|
void CPU::PGXP::GTE_RTPS(float x, float y, float z, u32 value)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
// push values down FIFO
|
|
|
|
SXY0 = SXY1;
|
|
|
|
SXY1 = SXY2;
|
|
|
|
|
2021-06-12 12:02:28 +00:00
|
|
|
SXY2.x = x;
|
|
|
|
SXY2.y = y;
|
|
|
|
SXY2.z = z;
|
2024-05-24 16:06:40 +00:00
|
|
|
SXY2.value = value;
|
2020-08-01 14:25:07 +00:00
|
|
|
SXY2.flags = VALID_ALL;
|
|
|
|
|
|
|
|
if (g_settings.gpu_pgxp_vertex_cache)
|
2024-05-24 16:06:40 +00:00
|
|
|
CacheVertex(value, SXY2);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define VX(n) (psxRegs.CP2D.p[n << 1].sw.l)
|
|
|
|
#define VY(n) (psxRegs.CP2D.p[n << 1].sw.h)
|
|
|
|
#define VZ(n) (psxRegs.CP2D.p[(n << 1) + 1].sw.l)
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
int CPU::PGXP::GTE_NCLIP_valid(u32 sxy0, u32 sxy1, u32 sxy2)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
Validate(&SXY0, sxy0);
|
|
|
|
Validate(&SXY1, sxy1);
|
|
|
|
Validate(&SXY2, sxy2);
|
2023-11-28 16:00:22 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
// Don't use accurate clipping for game-constructed values, which don't have a valid Z.
|
2024-05-17 09:39:23 +00:00
|
|
|
return (((SXY0.flags & SXY1.flags & SXY2.flags & VALID_XYZ) == VALID_XYZ));
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
float CPU::PGXP::GTE_NCLIP()
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
float nclip = ((SX0 * SY1) + (SX1 * SY2) + (SX2 * SY0) - (SX0 * SY2) - (SX1 * SY0) - (SX2 * SY1));
|
|
|
|
|
|
|
|
// ensure fractional values are not incorrectly rounded to 0
|
|
|
|
float nclipAbs = std::abs(nclip);
|
|
|
|
if ((0.1f < nclipAbs) && (nclipAbs < 1.f))
|
|
|
|
nclip += (nclip < 0.f ? -1 : 1);
|
|
|
|
|
|
|
|
// float AX = SX1 - SX0;
|
|
|
|
// float AY = SY1 - SY0;
|
|
|
|
|
|
|
|
// float BX = SX2 - SX0;
|
|
|
|
// float BY = SY2 - SY0;
|
|
|
|
|
|
|
|
//// normalise A and B
|
|
|
|
// float mA = sqrt((AX*AX) + (AY*AY));
|
|
|
|
// float mB = sqrt((BX*BX) + (BY*BY));
|
|
|
|
|
|
|
|
//// calculate AxB to get Z component of C
|
|
|
|
// float CZ = ((AX * BY) - (AY * BX)) * (1 << 12);
|
|
|
|
|
|
|
|
return nclip;
|
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::CPU_MTC2_int(const PGXP_value& value, u32 reg)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
switch (reg)
|
|
|
|
{
|
|
|
|
case 15:
|
|
|
|
// push FIFO
|
|
|
|
SXY0 = SXY1;
|
|
|
|
SXY1 = SXY2;
|
|
|
|
SXY2 = value;
|
|
|
|
SXYP = SXY2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 31:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
g_state.pgxp_gte[reg] = value;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
|
|
// Data transfer tracking
|
|
|
|
////////////////////////////////////
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_MFC2(u32 instr, u32 rdVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
// CPU[Rt] = GTE_D[Rd]
|
2023-08-15 13:12:21 +00:00
|
|
|
const u32 idx = cop2idx(instr);
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_1(CPU::GetGTERegisterName(idx), rdVal, &g_state.pgxp_gte[idx]);
|
2023-12-06 06:45:17 +00:00
|
|
|
Validate(&g_state.pgxp_gte[idx], rdVal);
|
|
|
|
g_state.pgxp_gpr[rt(instr)] = g_state.pgxp_gte[idx];
|
|
|
|
g_state.pgxp_gpr[rt(instr)].value = rdVal;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_MTC2(u32 instr, u32 rtVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
// GTE_D[Rd] = CPU[Rt]
|
2023-08-15 13:12:21 +00:00
|
|
|
const u32 idx = cop2idx(instr);
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rt(instr), rtVal);
|
2023-12-06 06:45:17 +00:00
|
|
|
Validate(&g_state.pgxp_gpr[rt(instr)], rtVal);
|
|
|
|
CPU_MTC2_int(g_state.pgxp_gpr[rt(instr)], idx);
|
|
|
|
g_state.pgxp_gte[idx].value = rtVal;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
|
|
// Memory Access
|
|
|
|
////////////////////////////////////
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_LWC2(u32 instr, u32 addr, u32 rtVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
// GTE_D[Rt] = Mem[addr]
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_LOAD(addr, rtVal);
|
2020-08-01 14:25:07 +00:00
|
|
|
PGXP_value val;
|
|
|
|
ValidateAndCopyMem(&val, addr, rtVal);
|
2023-12-06 06:45:17 +00:00
|
|
|
CPU_MTC2_int(val, rt(instr));
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SWC2(u32 instr, u32 addr, u32 rtVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
// Mem[addr] = GTE_D[Rt]
|
2024-05-17 07:15:38 +00:00
|
|
|
const u32 idx = rt(instr);
|
|
|
|
#ifdef LOG_VALUES
|
|
|
|
LOG_VALUES_1(CPU::GetGTERegisterName(idx), rtVal, &g_state.pgxp_gte[idx]);
|
|
|
|
std::fprintf(s_log, " addr=%08X", addr);
|
|
|
|
#endif
|
|
|
|
Validate(&g_state.pgxp_gte[idx], rtVal);
|
|
|
|
WriteMem(&g_state.pgxp_gte[idx], addr);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2024-05-24 16:06:40 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::CacheVertex(u32 value, const PGXP_value& vertex)
|
2020-09-02 12:44:52 +00:00
|
|
|
{
|
2024-05-24 16:06:40 +00:00
|
|
|
const s16 sx = static_cast<s16>(value & 0xFFFFu);
|
|
|
|
const s16 sy = static_cast<s16>(value >> 16);
|
|
|
|
DebugAssert(sx >= -1024 && sx <= 1023 && sy >= -1024 && sy <= 1023);
|
|
|
|
s_vertex_cache[(sy + 1024) * VERTEX_CACHE_WIDTH + (sx + 1024)] = vertex;
|
2020-09-02 12:44:52 +00:00
|
|
|
}
|
|
|
|
|
2024-05-24 16:06:40 +00:00
|
|
|
ALWAYS_INLINE_RELEASE CPU::PGXP_value* CPU::PGXP::GetCachedVertex(u32 value)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2024-05-24 16:06:40 +00:00
|
|
|
const s16 sx = static_cast<s16>(value & 0xFFFFu);
|
|
|
|
const s16 sy = static_cast<s16>(value >> 16);
|
|
|
|
return (sx >= -1024 && sx <= 1023 && sy >= -1024 && sy <= 1013) ?
|
|
|
|
&s_vertex_cache[(sy + 1024) * VERTEX_CACHE_WIDTH + (sx + 1024)] :
|
|
|
|
nullptr;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE float CPU::PGXP::TruncateVertexPosition(float p)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
const s32 int_part = static_cast<s32>(p);
|
|
|
|
const float int_part_f = static_cast<float>(int_part);
|
|
|
|
return static_cast<float>(static_cast<s16>(int_part << 5) >> 5) + (p - int_part_f);
|
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE bool CPU::PGXP::IsWithinTolerance(float precise_x, float precise_y, int int_x, int int_y)
|
2020-11-26 14:02:13 +00:00
|
|
|
{
|
|
|
|
const float tolerance = g_settings.gpu_pgxp_tolerance;
|
|
|
|
if (tolerance < 0.0f)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return (std::abs(precise_x - static_cast<float>(int_x)) <= tolerance &&
|
|
|
|
std::abs(precise_y - static_cast<float>(int_y)) <= tolerance);
|
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
bool CPU::PGXP::GetPreciseVertex(u32 addr, u32 value, int x, int y, int xOffs, int yOffs, float* out_x, float* out_y,
|
|
|
|
float* out_w)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2023-12-06 06:45:17 +00:00
|
|
|
const PGXP_value* vert = GetPtr(addr);
|
2024-05-17 09:39:23 +00:00
|
|
|
if (vert && ((vert->flags & VALID_XY) == VALID_XY) && (vert->value == value))
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
// There is a value here with valid X and Y coordinates
|
|
|
|
*out_x = TruncateVertexPosition(vert->x) + static_cast<float>(xOffs);
|
|
|
|
*out_y = TruncateVertexPosition(vert->y) + static_cast<float>(yOffs);
|
|
|
|
*out_w = vert->z / 32768.0f;
|
2021-06-12 12:02:28 +00:00
|
|
|
|
2024-05-17 07:15:38 +00:00
|
|
|
#ifdef LOG_LOOKUPS
|
|
|
|
GL_INS_FMT("0x{:08X} {},{} => {},{} ({},{},{}) ({},{})", addr, x, y, *out_x, *out_y,
|
|
|
|
TruncateVertexPosition(vert->x), TruncateVertexPosition(vert->y), vert->z, std::abs(*out_x - x),
|
|
|
|
std::abs(*out_y - y));
|
|
|
|
#endif
|
|
|
|
|
2020-11-26 14:02:13 +00:00
|
|
|
if (IsWithinTolerance(*out_x, *out_y, x, y))
|
|
|
|
{
|
|
|
|
// check validity of z component
|
2024-05-17 09:39:23 +00:00
|
|
|
return ((vert->flags & VALID_Z) == VALID_Z);
|
2020-11-26 14:02:13 +00:00
|
|
|
}
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
2021-06-12 12:02:28 +00:00
|
|
|
|
|
|
|
if (g_settings.gpu_pgxp_vertex_cache)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2024-05-24 16:06:40 +00:00
|
|
|
vert = GetCachedVertex(value);
|
2024-05-17 09:39:23 +00:00
|
|
|
if (vert && (vert->flags & VALID_XY) == VALID_XY)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
*out_x = TruncateVertexPosition(vert->x) + static_cast<float>(xOffs);
|
|
|
|
*out_y = TruncateVertexPosition(vert->y) + static_cast<float>(yOffs);
|
|
|
|
*out_w = vert->z / 32768.0f;
|
2020-11-26 14:02:13 +00:00
|
|
|
|
|
|
|
if (IsWithinTolerance(*out_x, *out_y, x, y))
|
2021-06-12 12:02:28 +00:00
|
|
|
return false;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-26 14:02:13 +00:00
|
|
|
|
|
|
|
// no valid value can be found anywhere, use the native PSX data
|
|
|
|
*out_x = static_cast<float>(x);
|
|
|
|
*out_y = static_cast<float>(y);
|
|
|
|
*out_w = 1.0f;
|
|
|
|
return false;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Instruction register decoding
|
2023-09-03 04:30:26 +00:00
|
|
|
#define op(_instr) (_instr >> 26) // The op part of the instruction register
|
2024-05-17 07:15:38 +00:00
|
|
|
#define func(_instr) ((_instr) & 0x3F) // The funct part of the instruction register
|
2023-09-03 04:30:26 +00:00
|
|
|
#define sa(_instr) ((_instr >> 6) & 0x1F) // The sa part of the instruction register
|
2020-08-01 14:25:07 +00:00
|
|
|
#define rd(_instr) ((_instr >> 11) & 0x1F) // The rd part of the instruction register
|
|
|
|
#define rt(_instr) ((_instr >> 16) & 0x1F) // The rt part of the instruction register
|
|
|
|
#define rs(_instr) ((_instr >> 21) & 0x1F) // The rs part of the instruction register
|
2023-09-03 04:30:26 +00:00
|
|
|
#define imm(_instr) (_instr & 0xFFFF) // The immediate part of the instruction register
|
2021-02-17 15:45:32 +00:00
|
|
|
#define imm_sext(_instr) \
|
|
|
|
static_cast<s32>(static_cast<s16>(_instr & 0xFFFF)) // The immediate part of the instruction register
|
2020-08-01 14:25:07 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_LW(u32 instr, u32 addr, u32 rtVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
// Rt = Mem[Rs + Im]
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_LOAD(addr, rtVal);
|
2023-12-06 06:45:17 +00:00
|
|
|
ValidateAndCopyMem(&g_state.pgxp_gpr[rt(instr)], addr, rtVal);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_LBx(u32 instr, u32 addr, u32 rtVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_LOAD(addr, rtVal);
|
2023-12-06 06:45:17 +00:00
|
|
|
g_state.pgxp_gpr[rt(instr)] = PGXP_value_invalid;
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_LH(u32 instr, u32 addr, u32 rtVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2023-09-03 04:30:26 +00:00
|
|
|
// Rt = Mem[Rs + Im] (sign extended)
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_LOAD(addr, rtVal);
|
2023-12-06 06:45:17 +00:00
|
|
|
ValidateAndCopyMem16(&g_state.pgxp_gpr[rt(instr)], addr, rtVal, true);
|
2023-09-03 04:30:26 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_LHU(u32 instr, u32 addr, u32 rtVal)
|
2023-09-03 04:30:26 +00:00
|
|
|
{
|
|
|
|
// Rt = Mem[Rs + Im] (zero extended)
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_LOAD(addr, rtVal);
|
2023-12-06 06:45:17 +00:00
|
|
|
ValidateAndCopyMem16(&g_state.pgxp_gpr[rt(instr)], addr, rtVal, false);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SB(u32 instr, u32 addr, u32 rtVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_STORE(rt(instr), rtVal, addr);
|
2021-06-12 12:02:28 +00:00
|
|
|
WriteMem(&PGXP_value_invalid, addr);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SH(u32 instr, u32 addr, u32 rtVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_STORE(rt(instr), rtVal, addr);
|
2023-12-06 06:45:17 +00:00
|
|
|
PGXP_value* val = &g_state.pgxp_gpr[rt(instr)];
|
2023-12-05 08:20:54 +00:00
|
|
|
Validate(val, rtVal);
|
2021-06-24 06:52:38 +00:00
|
|
|
WriteMem16(val, addr);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SW(u32 instr, u32 addr, u32 rtVal)
|
2020-08-01 14:25:07 +00:00
|
|
|
{
|
|
|
|
// Mem[Rs + Im] = Rt
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_STORE(rt(instr), rtVal, addr);
|
2023-12-06 06:45:17 +00:00
|
|
|
PGXP_value* val = &g_state.pgxp_gpr[rt(instr)];
|
2021-06-24 06:52:38 +00:00
|
|
|
Validate(val, rtVal);
|
|
|
|
WriteMem(val, addr);
|
2020-08-01 14:25:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 10:56:24 +00:00
|
|
|
void CPU::PGXP::CPU_MOVE_Packed(u32 rd_and_rs, u32 rsVal)
|
2020-10-04 12:13:33 +00:00
|
|
|
{
|
|
|
|
const u32 Rs = (rd_and_rs & 0xFFu);
|
2023-12-13 10:56:24 +00:00
|
|
|
const u32 Rd = (rd_and_rs >> 8);
|
|
|
|
CPU_MOVE(Rd, Rs, rsVal);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPU::PGXP::CPU_MOVE(u32 Rd, u32 Rs, u32 rsVal)
|
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
#ifdef LOG_VALUES
|
|
|
|
const u32 instr = 0;
|
|
|
|
LOG_VALUES_C1(Rs, rsVal);
|
|
|
|
#endif
|
2023-12-06 06:45:17 +00:00
|
|
|
Validate(&g_state.pgxp_gpr[Rs], rsVal);
|
2023-12-13 10:56:24 +00:00
|
|
|
g_state.pgxp_gpr[Rd] = g_state.pgxp_gpr[Rs];
|
2020-10-04 12:13:33 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_ADDI(u32 instr, u32 rsVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rs(instr), rsVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rt = Rs + Imm (signed)
|
2024-05-17 09:39:23 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
2023-10-31 15:32:29 +00:00
|
|
|
|
|
|
|
psx_value tempImm;
|
|
|
|
tempImm.d = SignExtend32(static_cast<u16>(imm(instr)));
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:39:23 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
prtVal = prsVal;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:39:23 +00:00
|
|
|
if (tempImm.d == 0)
|
|
|
|
return;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 09:07:17 +00:00
|
|
|
if (rsVal == 0)
|
|
|
|
{
|
|
|
|
// x is low precision value
|
|
|
|
prtVal.x = static_cast<float>(tempImm.sw.l);
|
|
|
|
prtVal.y = static_cast<float>(tempImm.sw.h);
|
|
|
|
prtVal.flags |= VALID_X | VALID_Y | VALID_TAINTED_Z;
|
|
|
|
prtVal.value = tempImm.d;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-05-17 09:39:23 +00:00
|
|
|
prtVal.x = (float)f16Unsign(prtVal.x);
|
|
|
|
prtVal.x += (float)tempImm.w.l;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:39:23 +00:00
|
|
|
// carry on over/underflow
|
|
|
|
float of = (prtVal.x > USHRT_MAX) ? 1.f : (prtVal.x < 0) ? -1.f : 0.f;
|
|
|
|
prtVal.x = (float)f16Sign(prtVal.x);
|
|
|
|
// ret.x -= of * (USHRT_MAX + 1);
|
|
|
|
prtVal.y += tempImm.sw.h + of;
|
|
|
|
|
|
|
|
// truncate on overflow/underflow
|
|
|
|
prtVal.y += (prtVal.y > SHRT_MAX) ? -(USHRT_MAX + 1) : (prtVal.y < SHRT_MIN) ? USHRT_MAX + 1 : 0.f;
|
|
|
|
|
|
|
|
prtVal.value = rsVal + tempImm.d;
|
2024-05-17 11:18:39 +00:00
|
|
|
|
|
|
|
prtVal.flags |= VALID_TAINTED_Z;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_ANDI(u32 instr, u32 rsVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rs(instr), rsVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rt = Rs & Imm
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rtVal = rsVal & imm(instr);
|
2024-05-17 09:39:23 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:45:44 +00:00
|
|
|
psx_value vRt;
|
2020-08-19 13:26:57 +00:00
|
|
|
vRt.d = rtVal;
|
|
|
|
|
2024-05-17 09:39:23 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
prtVal = prsVal;
|
|
|
|
|
|
|
|
prtVal.value = rtVal;
|
|
|
|
prtVal.y = 0.f; // remove upper 16-bits
|
|
|
|
prtVal.SetValid(COMP_Y);
|
2024-05-17 11:18:39 +00:00
|
|
|
prtVal.flags |= VALID_TAINTED_Z;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
switch (imm(instr))
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
// if 0 then x == 0
|
2024-05-17 11:18:39 +00:00
|
|
|
prtVal.x = 0.0f;
|
|
|
|
prtVal.SetValid(COMP_X);
|
2020-08-19 13:26:57 +00:00
|
|
|
break;
|
|
|
|
case 0xFFFF:
|
|
|
|
// if saturated then x == x
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// otherwise x is low precision value
|
2024-05-17 09:39:23 +00:00
|
|
|
prtVal.x = vRt.sw.l;
|
|
|
|
prtVal.SetValid(COMP_X);
|
|
|
|
break;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_ORI(u32 instr, u32 rsVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rs(instr), rsVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rt = Rs | Imm
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rtVal = rsVal | imm(instr);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
Validate(&g_state.pgxp_gpr[rs(instr)], rsVal);
|
2024-05-17 09:45:44 +00:00
|
|
|
PGXP_value ret = g_state.pgxp_gpr[rs(instr)];
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:45:44 +00:00
|
|
|
psx_value vRt;
|
2020-08-19 13:26:57 +00:00
|
|
|
vRt.d = rtVal;
|
|
|
|
|
|
|
|
switch (imm(instr))
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
// if 0 then x == x
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// otherwise x is low precision value
|
|
|
|
ret.x = vRt.sw.l;
|
2024-05-17 09:39:23 +00:00
|
|
|
ret.SetValid(COMP_X);
|
2024-05-17 11:18:39 +00:00
|
|
|
ret.flags |= VALID_TAINTED_Z;
|
2024-05-17 09:39:23 +00:00
|
|
|
break;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret.value = rtVal;
|
2023-12-06 06:45:17 +00:00
|
|
|
g_state.pgxp_gpr[rt(instr)] = ret;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_XORI(u32 instr, u32 rsVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rs(instr), rsVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rt = Rs ^ Imm
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rtVal = rsVal ^ imm(instr);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
Validate(&g_state.pgxp_gpr[rs(instr)], rsVal);
|
2024-05-17 09:45:44 +00:00
|
|
|
PGXP_value ret = g_state.pgxp_gpr[rs(instr)];
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:45:44 +00:00
|
|
|
psx_value vRt;
|
2020-08-19 13:26:57 +00:00
|
|
|
vRt.d = rtVal;
|
|
|
|
|
|
|
|
switch (imm(instr))
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
// if 0 then x == x
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// otherwise x is low precision value
|
|
|
|
ret.x = vRt.sw.l;
|
2024-05-17 09:39:23 +00:00
|
|
|
ret.SetValid(COMP_X);
|
2024-05-17 11:18:39 +00:00
|
|
|
ret.flags |= VALID_TAINTED_Z;
|
2024-05-17 09:39:23 +00:00
|
|
|
break;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret.value = rtVal;
|
2023-12-06 06:45:17 +00:00
|
|
|
g_state.pgxp_gpr[rt(instr)] = ret;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SLTI(u32 instr, u32 rsVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rs(instr), rsVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rt = Rs < Imm (signed)
|
2024-07-09 10:14:07 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 10:14:07 +00:00
|
|
|
const float fimmx = static_cast<float>(static_cast<s16>(imm(instr)));
|
|
|
|
const float fimmy = fimmx < 0.0f ? -1.0f : 0.0f;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 10:14:07 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
prtVal.x = (prsVal.GetValidY(rsVal) < fimmy || prsVal.GetValidX(rsVal) < fimmx) ? 1.f : 0.f;
|
|
|
|
prtVal.y = 0.0f;
|
|
|
|
prtVal.z = prsVal.z;
|
|
|
|
prtVal.flags = prsVal.flags | VALID_X | VALID_Y | VALID_TAINTED_Z;
|
|
|
|
prtVal.value = BoolToUInt32(static_cast<s32>(rsVal) < imm_sext(instr));
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SLTIU(u32 instr, u32 rsVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rs(instr), rsVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rt = Rs < Imm (Unsigned)
|
2024-07-09 10:14:07 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
2023-12-06 06:45:17 +00:00
|
|
|
Validate(&g_state.pgxp_gpr[rs(instr)], rsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 10:14:07 +00:00
|
|
|
const float fimmx = static_cast<float>(static_cast<s16>(imm(instr)));
|
|
|
|
const float fimmy = fimmx < 0.0f ? -1.0f : 0.0f;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 10:14:07 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
prtVal.x =
|
|
|
|
(f16Unsign(prsVal.GetValidY(rsVal)) < f16Unsign(fimmy) || f16Unsign(prsVal.GetValidX(rsVal)) < fimmx) ? 1.0f : 0.0f;
|
|
|
|
prtVal.y = 0.f;
|
|
|
|
prtVal.z = prsVal.z;
|
|
|
|
prtVal.flags = prsVal.flags | VALID_X | VALID_Y | VALID_TAINTED_Z;
|
|
|
|
prtVal.value = BoolToUInt32(rsVal < imm(instr));
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
|
|
// Load Upper
|
|
|
|
////////////////////////////////////
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_LUI(u32 instr)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_NV();
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rt = Imm << 16
|
2023-12-06 06:45:17 +00:00
|
|
|
g_state.pgxp_gpr[rt(instr)] = PGXP_value_zero;
|
|
|
|
g_state.pgxp_gpr[rt(instr)].y = (float)(s16)imm(instr);
|
|
|
|
g_state.pgxp_gpr[rt(instr)].value = static_cast<u32>(imm(instr)) << 16;
|
2024-05-17 09:39:23 +00:00
|
|
|
g_state.pgxp_gpr[rt(instr)].flags = VALID_XY;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
|
|
// Register Arithmetic
|
|
|
|
////////////////////////////////////
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_ADD(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
2024-07-10 10:30:35 +00:00
|
|
|
PGXP_value& prdVal = g_state.pgxp_gpr[rd(instr)];
|
2024-07-08 07:00:04 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rs + Rt (signed)
|
2024-07-09 09:10:02 +00:00
|
|
|
Validate(&prsVal, rsVal);
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2023-09-03 04:30:26 +00:00
|
|
|
if (rtVal == 0)
|
|
|
|
{
|
2024-07-10 10:30:35 +00:00
|
|
|
prdVal = prsVal;
|
|
|
|
CopyZIfMissing(prdVal, prtVal);
|
2023-09-03 04:30:26 +00:00
|
|
|
}
|
|
|
|
else if (rsVal == 0)
|
|
|
|
{
|
2024-07-10 10:30:35 +00:00
|
|
|
prdVal = prtVal;
|
|
|
|
CopyZIfMissing(prdVal, prsVal);
|
2023-09-03 04:30:26 +00:00
|
|
|
}
|
|
|
|
else
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-07-10 10:30:35 +00:00
|
|
|
const double x = f16Unsign(prsVal.GetValidX(rsVal)) + f16Unsign(prtVal.GetValidX(rtVal));
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2020-12-03 15:07:34 +00:00
|
|
|
// carry on over/underflow
|
2024-07-10 10:30:35 +00:00
|
|
|
const float of = (x > USHRT_MAX) ? 1.f : (x < 0) ? -1.f : 0.f;
|
|
|
|
prdVal.x = static_cast<float>(f16Sign(x));
|
|
|
|
// prdVal.x -= of * (USHRT_MAX + 1);
|
|
|
|
prdVal.y = prsVal.GetValidY(rsVal) + prtVal.GetValidY(rtVal) + of;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2020-12-03 15:07:34 +00:00
|
|
|
// truncate on overflow/underflow
|
2024-07-10 10:30:35 +00:00
|
|
|
prdVal.y += (prdVal.y > SHRT_MAX) ? -(USHRT_MAX + 1) : (prdVal.y < SHRT_MIN) ? USHRT_MAX + 1 : 0.f;
|
|
|
|
|
|
|
|
prdVal.value = rsVal + rtVal;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-08 07:00:04 +00:00
|
|
|
// valid x/y only if one side had a valid x/y
|
2024-07-10 10:30:35 +00:00
|
|
|
prdVal.flags = prsVal.flags | (prtVal.flags & VALID_XY) | VALID_TAINTED_Z;
|
2024-07-08 07:00:04 +00:00
|
|
|
|
2024-07-10 10:30:35 +00:00
|
|
|
SelectZ(prdVal, prsVal, prtVal);
|
2023-11-28 16:00:22 +00:00
|
|
|
}
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SUB(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
2024-07-10 10:30:35 +00:00
|
|
|
PGXP_value& prdVal = g_state.pgxp_gpr[rd(instr)];
|
2024-07-08 07:00:04 +00:00
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rs - Rt (signed)
|
2024-07-09 09:10:02 +00:00
|
|
|
Validate(&prsVal, rsVal);
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 11:13:12 +00:00
|
|
|
if (rtVal == 0)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-07-10 10:30:35 +00:00
|
|
|
prdVal = prsVal;
|
|
|
|
CopyZIfMissing(prdVal, prtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
2024-05-17 11:13:12 +00:00
|
|
|
else
|
|
|
|
{
|
2024-07-10 10:30:35 +00:00
|
|
|
const double x = f16Unsign(prsVal.GetValidX(rsVal)) - f16Unsign(prtVal.GetValidX(rtVal));
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 11:13:12 +00:00
|
|
|
// carry on over/underflow
|
2024-07-10 10:30:35 +00:00
|
|
|
const float of = (x > USHRT_MAX) ? 1.f : (x < 0) ? -1.f : 0.f;
|
|
|
|
prdVal.x = static_cast<float>(f16Sign(x));
|
|
|
|
// prdVal.x -= of * (USHRT_MAX + 1);
|
|
|
|
prdVal.y = prsVal.GetValidY(rsVal) - (prtVal.GetValidY(rtVal) - of);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 11:13:12 +00:00
|
|
|
// truncate on overflow/underflow
|
2024-07-10 10:30:35 +00:00
|
|
|
prdVal.y += (prdVal.y > SHRT_MAX) ? -(USHRT_MAX + 1) : (prdVal.y < SHRT_MIN) ? USHRT_MAX + 1 : 0.f;
|
|
|
|
|
|
|
|
prdVal.value = rsVal - rtVal;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-08 07:00:04 +00:00
|
|
|
// valid x/y only if one side had a valid x/y
|
2024-07-10 10:30:35 +00:00
|
|
|
prdVal.flags = prsVal.flags | (prtVal.flags & VALID_XY) | VALID_TAINTED_Z;
|
2024-07-08 07:00:04 +00:00
|
|
|
|
2024-07-10 10:30:35 +00:00
|
|
|
SelectZ(prdVal, prsVal, prtVal);
|
2024-05-17 11:13:12 +00:00
|
|
|
}
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
ALWAYS_INLINE_RELEASE void CPU::PGXP::CPU_BITWISE(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
|
|
|
// Rd = Rs & Rt
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:45:44 +00:00
|
|
|
psx_value vald, vals, valt;
|
2020-08-19 13:26:57 +00:00
|
|
|
vald.d = rdVal;
|
|
|
|
vals.d = rsVal;
|
|
|
|
valt.d = rtVal;
|
|
|
|
|
2024-05-17 09:45:44 +00:00
|
|
|
PGXP_value ret;
|
2024-07-10 09:10:56 +00:00
|
|
|
ret.flags = ((prsVal.flags | prtVal.flags) & VALID_XY) ? (VALID_XY | VALID_TAINTED_Z) : 0;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
if (vald.w.l == 0)
|
|
|
|
ret.x = 0.f;
|
|
|
|
else if (vald.w.l == vals.w.l)
|
2024-07-10 09:10:56 +00:00
|
|
|
ret.x = prsVal.GetValidX(rsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
else if (vald.w.l == valt.w.l)
|
2024-07-10 09:10:56 +00:00
|
|
|
ret.x = prtVal.GetValidX(rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
else
|
2024-07-10 09:10:56 +00:00
|
|
|
ret.x = static_cast<float>(vald.sw.l);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
if (vald.w.h == 0)
|
|
|
|
ret.y = 0.f;
|
|
|
|
else if (vald.w.h == vals.w.h)
|
2024-07-10 09:10:56 +00:00
|
|
|
ret.y = prsVal.GetValidY(rsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
else if (vald.w.h == valt.w.h)
|
2024-07-10 09:10:56 +00:00
|
|
|
ret.y = prtVal.GetValidY(rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
else
|
2024-07-10 09:10:56 +00:00
|
|
|
ret.y = static_cast<float>(vald.sw.h);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-10 09:10:56 +00:00
|
|
|
SelectZ(ret, prsVal, prtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
ret.value = rdVal;
|
2023-12-06 06:45:17 +00:00
|
|
|
g_state.pgxp_gpr[rd(instr)] = ret;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_AND_(u32 instr, u32 rsVal, u32 rtVal)
|
2021-02-17 15:45:32 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2021-02-17 15:45:32 +00:00
|
|
|
// Rd = Rs & Rt
|
|
|
|
const u32 rdVal = rsVal & rtVal;
|
|
|
|
CPU_BITWISE(instr, rdVal, rsVal, rtVal);
|
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_OR_(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rs | Rt
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rdVal = rsVal | rtVal;
|
|
|
|
CPU_BITWISE(instr, rdVal, rsVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_XOR_(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rs ^ Rt
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rdVal = rsVal ^ rtVal;
|
|
|
|
CPU_BITWISE(instr, rdVal, rsVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_NOR(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rs NOR Rt
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rdVal = ~(rsVal | rtVal);
|
|
|
|
CPU_BITWISE(instr, rdVal, rsVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SLT(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rs < Rt (signed)
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value ret = prsVal;
|
2024-07-09 09:28:32 +00:00
|
|
|
ret.x = (prsVal.GetValidY(rsVal) < prtVal.GetValidY(rtVal) ||
|
|
|
|
f16Unsign(prsVal.GetValidX(rsVal)) < f16Unsign(prtVal.GetValidX(rtVal))) ?
|
|
|
|
1.f :
|
|
|
|
0.f;
|
2020-08-19 13:26:57 +00:00
|
|
|
ret.y = 0.f;
|
2024-07-09 09:28:32 +00:00
|
|
|
ret.flags |= VALID_TAINTED_Z | VALID_X | VALID_Y;
|
2021-02-17 15:45:32 +00:00
|
|
|
ret.value = BoolToUInt32(static_cast<s32>(rsVal) < static_cast<s32>(rtVal));
|
2023-12-06 06:45:17 +00:00
|
|
|
g_state.pgxp_gpr[rd(instr)] = ret;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SLTU(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rs < Rt (unsigned)
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value ret = prsVal;
|
2024-07-09 09:28:32 +00:00
|
|
|
ret.x = (f16Unsign(prsVal.GetValidY(rsVal)) < f16Unsign(prtVal.GetValidY(rtVal)) ||
|
|
|
|
f16Unsign(prsVal.GetValidX(rsVal)) < f16Unsign(prtVal.GetValidX(rtVal))) ?
|
|
|
|
1.f :
|
|
|
|
0.f;
|
2020-08-19 13:26:57 +00:00
|
|
|
ret.y = 0.f;
|
2024-07-09 09:28:32 +00:00
|
|
|
ret.flags |= VALID_TAINTED_Z | VALID_X | VALID_Y;
|
2021-02-17 15:45:32 +00:00
|
|
|
ret.value = BoolToUInt32(rsVal < rtVal);
|
2023-12-06 06:45:17 +00:00
|
|
|
g_state.pgxp_gpr[rd(instr)] = ret;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
|
|
// Register mult/div
|
|
|
|
////////////////////////////////////
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_MULT(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Hi/Lo = Rs * Rt (signed)
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& ploVal = g_state.pgxp_gpr[static_cast<u8>(Reg::lo)];
|
|
|
|
PGXP_value& phiVal = g_state.pgxp_gpr[static_cast<u8>(Reg::hi)];
|
|
|
|
ploVal = prsVal;
|
|
|
|
CopyZIfMissing(ploVal, prsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:00:25 +00:00
|
|
|
// Z/valid is the same
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal = ploVal;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
double xx, xy, yx, yy;
|
|
|
|
double lx = 0, ly = 0, hx = 0, hy = 0;
|
|
|
|
|
2024-07-09 09:21:47 +00:00
|
|
|
const float rsx = prsVal.GetValidX(rsVal);
|
|
|
|
const float rsy = prsVal.GetValidY(rsVal);
|
|
|
|
const float rtx = prtVal.GetValidX(rtVal);
|
|
|
|
const float rty = prtVal.GetValidY(rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Multiply out components
|
2024-07-09 09:21:47 +00:00
|
|
|
xx = f16Unsign(rsx) * f16Unsign(rtx);
|
|
|
|
xy = f16Unsign(rsx) * (rty);
|
|
|
|
yx = (rsy)*f16Unsign(rtx);
|
|
|
|
yy = (rsy) * (rty);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
// Split values into outputs
|
|
|
|
lx = xx;
|
|
|
|
|
|
|
|
ly = f16Overflow(xx);
|
|
|
|
ly += xy + yx;
|
|
|
|
|
|
|
|
hx = f16Overflow(ly);
|
|
|
|
hx += yy;
|
|
|
|
|
|
|
|
hy = f16Overflow(hx);
|
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
ploVal.x = (float)f16Sign(lx);
|
|
|
|
ploVal.y = (float)f16Sign(ly);
|
2024-07-09 09:21:47 +00:00
|
|
|
ploVal.flags |= VALID_TAINTED_Z | (prtVal.flags & VALID_XY);
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal.x = (float)f16Sign(hx);
|
|
|
|
phiVal.y = (float)f16Sign(hy);
|
2024-07-09 09:21:47 +00:00
|
|
|
phiVal.flags |= VALID_TAINTED_Z | (prtVal.flags & VALID_XY);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2021-02-17 14:37:17 +00:00
|
|
|
// compute PSX value
|
|
|
|
const u64 result = static_cast<u64>(static_cast<s64>(SignExtend64(rsVal)) * static_cast<s64>(SignExtend64(rtVal)));
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal.value = Truncate32(result >> 32);
|
|
|
|
ploVal.value = Truncate32(result);
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_MULTU(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Hi/Lo = Rs * Rt (unsigned)
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& ploVal = g_state.pgxp_gpr[static_cast<u8>(Reg::lo)];
|
|
|
|
PGXP_value& phiVal = g_state.pgxp_gpr[static_cast<u8>(Reg::hi)];
|
|
|
|
ploVal = prsVal;
|
|
|
|
CopyZIfMissing(ploVal, prsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:00:25 +00:00
|
|
|
// Z/valid is the same
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal = ploVal;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
double xx, xy, yx, yy;
|
|
|
|
double lx = 0, ly = 0, hx = 0, hy = 0;
|
|
|
|
|
2024-07-09 09:21:47 +00:00
|
|
|
const float rsx = prsVal.GetValidX(rsVal);
|
|
|
|
const float rsy = prsVal.GetValidY(rsVal);
|
|
|
|
const float rtx = prtVal.GetValidX(rtVal);
|
|
|
|
const float rty = prtVal.GetValidY(rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Multiply out components
|
2024-07-09 09:21:47 +00:00
|
|
|
xx = f16Unsign(rsx) * f16Unsign(rtx);
|
|
|
|
xy = f16Unsign(rsx) * f16Unsign(rty);
|
|
|
|
yx = f16Unsign(rsy) * f16Unsign(rtx);
|
|
|
|
yy = f16Unsign(rsy) * f16Unsign(rty);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
// Split values into outputs
|
|
|
|
lx = xx;
|
|
|
|
|
|
|
|
ly = f16Overflow(xx);
|
|
|
|
ly += xy + yx;
|
|
|
|
|
|
|
|
hx = f16Overflow(ly);
|
|
|
|
hx += yy;
|
|
|
|
|
|
|
|
hy = f16Overflow(hx);
|
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
ploVal.x = (float)f16Sign(lx);
|
|
|
|
ploVal.y = (float)f16Sign(ly);
|
2024-07-09 09:21:47 +00:00
|
|
|
ploVal.flags |= VALID_TAINTED_Z | (prtVal.flags & VALID_XY);
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal.x = (float)f16Sign(hx);
|
|
|
|
phiVal.y = (float)f16Sign(hy);
|
2024-07-09 09:21:47 +00:00
|
|
|
phiVal.flags |= VALID_TAINTED_Z | (prtVal.flags & VALID_XY);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2021-02-17 14:37:17 +00:00
|
|
|
// compute PSX value
|
|
|
|
const u64 result = ZeroExtend64(rsVal) * ZeroExtend64(rtVal);
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal.value = Truncate32(result >> 32);
|
|
|
|
ploVal.value = Truncate32(result);
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_DIV(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Lo = Rs / Rt (signed)
|
|
|
|
// Hi = Rs % Rt (signed)
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& ploVal = g_state.pgxp_gpr[static_cast<u8>(Reg::lo)];
|
|
|
|
PGXP_value& phiVal = g_state.pgxp_gpr[static_cast<u8>(Reg::hi)];
|
|
|
|
ploVal = prsVal;
|
|
|
|
CopyZIfMissing(ploVal, prsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:00:25 +00:00
|
|
|
// Z/valid is the same
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal = ploVal;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 09:21:47 +00:00
|
|
|
double vs = f16Unsign(prsVal.GetValidX(rsVal)) + prsVal.GetValidY(rsVal) * (double)(1 << 16);
|
|
|
|
double vt = f16Unsign(prtVal.GetValidX(rtVal)) + prtVal.GetValidY(rtVal) * (double)(1 << 16);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
double lo = vs / vt;
|
2024-07-09 09:10:02 +00:00
|
|
|
ploVal.y = (float)f16Sign(f16Overflow(lo));
|
|
|
|
ploVal.x = (float)f16Sign(lo);
|
2024-07-09 09:21:47 +00:00
|
|
|
ploVal.flags |= VALID_TAINTED_Z | (prtVal.flags & VALID_XY);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
double hi = fmod(vs, vt);
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal.y = (float)f16Sign(f16Overflow(hi));
|
|
|
|
phiVal.x = (float)f16Sign(hi);
|
2024-07-09 09:21:47 +00:00
|
|
|
phiVal.flags |= VALID_TAINTED_Z | (prtVal.flags & VALID_XY);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2021-02-17 14:37:17 +00:00
|
|
|
// compute PSX value
|
|
|
|
if (static_cast<s32>(rtVal) == 0)
|
|
|
|
{
|
|
|
|
// divide by zero
|
2024-07-09 09:10:02 +00:00
|
|
|
ploVal.value = (static_cast<s32>(rsVal) >= 0) ? UINT32_C(0xFFFFFFFF) : UINT32_C(1);
|
|
|
|
phiVal.value = static_cast<u32>(static_cast<s32>(rsVal));
|
2021-02-17 14:37:17 +00:00
|
|
|
}
|
|
|
|
else if (rsVal == UINT32_C(0x80000000) && static_cast<s32>(rtVal) == -1)
|
|
|
|
{
|
|
|
|
// unrepresentable
|
2024-07-09 09:10:02 +00:00
|
|
|
ploVal.value = UINT32_C(0x80000000);
|
|
|
|
phiVal.value = 0;
|
2021-02-17 14:37:17 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-07-09 09:10:02 +00:00
|
|
|
ploVal.value = static_cast<u32>(static_cast<s32>(rsVal) / static_cast<s32>(rtVal));
|
|
|
|
phiVal.value = static_cast<u32>(static_cast<s32>(rsVal) % static_cast<s32>(rtVal));
|
2021-02-17 14:37:17 +00:00
|
|
|
}
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_DIVU(u32 instr, u32 rsVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rs(instr), rsVal, rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Lo = Rs / Rt (unsigned)
|
|
|
|
// Hi = Rs % Rt (unsigned)
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prsVal, rsVal);
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 09:10:02 +00:00
|
|
|
PGXP_value& ploVal = g_state.pgxp_gpr[static_cast<u8>(Reg::lo)];
|
|
|
|
PGXP_value& phiVal = g_state.pgxp_gpr[static_cast<u8>(Reg::hi)];
|
|
|
|
ploVal = prsVal;
|
|
|
|
CopyZIfMissing(ploVal, prsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 09:00:25 +00:00
|
|
|
// Z/valid is the same
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal = ploVal;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-07-09 09:21:47 +00:00
|
|
|
double vs = f16Unsign(prsVal.GetValidX(rsVal)) + f16Unsign(prsVal.GetValidY(rsVal)) * (double)(1 << 16);
|
|
|
|
double vt = f16Unsign(prtVal.GetValidX(rtVal)) + f16Unsign(prtVal.GetValidY(rtVal)) * (double)(1 << 16);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
double lo = vs / vt;
|
2024-07-09 09:10:02 +00:00
|
|
|
ploVal.y = (float)f16Sign(f16Overflow(lo));
|
|
|
|
ploVal.x = (float)f16Sign(lo);
|
2024-07-09 09:21:47 +00:00
|
|
|
ploVal.flags |= VALID_TAINTED_Z | (prtVal.flags & VALID_XY);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
double hi = fmod(vs, vt);
|
2024-07-09 09:10:02 +00:00
|
|
|
phiVal.y = (float)f16Sign(f16Overflow(hi));
|
|
|
|
phiVal.x = (float)f16Sign(hi);
|
2024-07-09 09:21:47 +00:00
|
|
|
phiVal.flags |= VALID_TAINTED_Z | (prtVal.flags & VALID_XY);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2021-02-17 14:37:17 +00:00
|
|
|
if (rtVal == 0)
|
|
|
|
{
|
|
|
|
// divide by zero
|
2024-07-09 09:10:02 +00:00
|
|
|
ploVal.value = UINT32_C(0xFFFFFFFF);
|
|
|
|
phiVal.value = rsVal;
|
2021-02-17 14:37:17 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-07-09 09:10:02 +00:00
|
|
|
ploVal.value = rsVal / rtVal;
|
|
|
|
phiVal.value = rsVal % rtVal;
|
2021-02-17 14:37:17 +00:00
|
|
|
}
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
|
|
// Shift operations (sa)
|
|
|
|
////////////////////////////////////
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SLL(u32 instr, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rt << Sa
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rdVal = rtVal << sa(instr);
|
2024-05-17 09:45:44 +00:00
|
|
|
const u32 sh = sa(instr);
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
// TODO: Shift flags
|
2024-05-17 10:41:55 +00:00
|
|
|
double x = f16Unsign(prtVal.x);
|
|
|
|
double y = f16Unsign(prtVal.y);
|
2020-08-19 13:26:57 +00:00
|
|
|
if (sh >= 32)
|
|
|
|
{
|
|
|
|
x = 0.f;
|
|
|
|
y = 0.f;
|
|
|
|
}
|
|
|
|
else if (sh == 16)
|
|
|
|
{
|
|
|
|
y = f16Sign(x);
|
|
|
|
x = 0.f;
|
|
|
|
}
|
|
|
|
else if (sh >= 16)
|
|
|
|
{
|
|
|
|
y = x * (1 << (sh - 16));
|
|
|
|
y = f16Sign(y);
|
|
|
|
x = 0.f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x = x * (1 << sh);
|
|
|
|
y = y * (1 << sh);
|
|
|
|
y += f16Overflow(x);
|
|
|
|
x = f16Sign(x);
|
|
|
|
y = f16Sign(y);
|
|
|
|
}
|
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prdVal = g_state.pgxp_gpr[rd(instr)];
|
|
|
|
prdVal = prtVal;
|
|
|
|
prdVal.x = static_cast<float>(x);
|
|
|
|
prdVal.y = static_cast<float>(y);
|
|
|
|
prdVal.value = rdVal;
|
2024-05-17 11:18:39 +00:00
|
|
|
prdVal.flags |= VALID_TAINTED_Z;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SRL(u32 instr, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rt >> Sa
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rdVal = rtVal >> sa(instr);
|
2024-05-17 09:45:44 +00:00
|
|
|
const u32 sh = sa(instr);
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
double x = prtVal.x;
|
|
|
|
double y = f16Unsign(prtVal.y);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
psx_value iX;
|
|
|
|
iX.d = rtVal;
|
|
|
|
psx_value iY;
|
|
|
|
iY.d = rtVal;
|
|
|
|
|
|
|
|
iX.sd = (iX.sd << 16) >> 16; // remove Y
|
|
|
|
iY.sw.l = iX.sw.h; // overwrite x with sign(x)
|
|
|
|
|
|
|
|
// Shift test values
|
|
|
|
psx_value dX;
|
|
|
|
dX.sd = iX.sd >> sh;
|
|
|
|
psx_value dY;
|
|
|
|
dY.d = iY.d >> sh;
|
|
|
|
|
|
|
|
if (dX.sw.l != iX.sw.h)
|
|
|
|
x = x / (1 << sh);
|
|
|
|
else
|
|
|
|
x = dX.sw.l; // only sign bits left
|
|
|
|
|
|
|
|
if (dY.sw.l != iX.sw.h)
|
|
|
|
{
|
|
|
|
if (sh == 16)
|
|
|
|
{
|
|
|
|
x = y;
|
|
|
|
}
|
|
|
|
else if (sh < 16)
|
|
|
|
{
|
|
|
|
x += y * (1 << (16 - sh));
|
2023-12-06 06:45:17 +00:00
|
|
|
if (g_state.pgxp_gpr[rt(instr)].x < 0)
|
2020-08-19 13:26:57 +00:00
|
|
|
x += 1 << (16 - sh);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x += y / (1 << (sh - 16));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dY.sw.h == 0) || (dY.sw.h == -1))
|
|
|
|
y = dY.sw.h;
|
|
|
|
else
|
|
|
|
y = y / (1 << sh);
|
|
|
|
|
|
|
|
x = f16Sign(x);
|
|
|
|
y = f16Sign(y);
|
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prdVal = g_state.pgxp_gpr[rd(instr)];
|
|
|
|
prdVal = prtVal;
|
|
|
|
prdVal.x = static_cast<float>(x);
|
|
|
|
prdVal.y = static_cast<float>(y);
|
|
|
|
prdVal.value = rdVal;
|
2024-05-17 11:18:39 +00:00
|
|
|
prdVal.flags |= VALID_TAINTED_Z;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SRA(u32 instr, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rt >> Sa
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rdVal = static_cast<u32>(static_cast<s32>(rtVal) >> sa(instr));
|
2024-05-17 09:45:44 +00:00
|
|
|
const u32 sh = sa(instr);
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
Validate(&prtVal, rtVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
double x = prtVal.x;
|
|
|
|
double y = prtVal.y;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
psx_value iX;
|
|
|
|
iX.d = rtVal;
|
|
|
|
psx_value iY;
|
|
|
|
iY.d = rtVal;
|
|
|
|
|
|
|
|
iX.sd = (iX.sd << 16) >> 16; // remove Y
|
|
|
|
iY.sw.l = iX.sw.h; // overwrite x with sign(x)
|
|
|
|
|
|
|
|
// Shift test values
|
|
|
|
psx_value dX;
|
|
|
|
dX.sd = iX.sd >> sh;
|
|
|
|
psx_value dY;
|
|
|
|
dY.sd = iY.sd >> sh;
|
|
|
|
|
|
|
|
if (dX.sw.l != iX.sw.h)
|
|
|
|
x = x / (1 << sh);
|
|
|
|
else
|
|
|
|
x = dX.sw.l; // only sign bits left
|
|
|
|
|
|
|
|
if (dY.sw.l != iX.sw.h)
|
|
|
|
{
|
|
|
|
if (sh == 16)
|
|
|
|
{
|
|
|
|
x = y;
|
|
|
|
}
|
|
|
|
else if (sh < 16)
|
|
|
|
{
|
|
|
|
x += y * (1 << (16 - sh));
|
2023-12-06 06:45:17 +00:00
|
|
|
if (g_state.pgxp_gpr[rt(instr)].x < 0)
|
2020-08-19 13:26:57 +00:00
|
|
|
x += 1 << (16 - sh);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x += y / (1 << (sh - 16));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dY.sw.h == 0) || (dY.sw.h == -1))
|
|
|
|
y = dY.sw.h;
|
|
|
|
else
|
|
|
|
y = y / (1 << sh);
|
|
|
|
|
|
|
|
x = f16Sign(x);
|
|
|
|
y = f16Sign(y);
|
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prdVal = g_state.pgxp_gpr[rd(instr)];
|
|
|
|
prdVal = prtVal;
|
|
|
|
prdVal.x = static_cast<float>(x);
|
|
|
|
prdVal.y = static_cast<float>(y);
|
|
|
|
prdVal.value = rdVal;
|
2024-05-17 11:18:39 +00:00
|
|
|
prdVal.flags |= VALID_TAINTED_Z;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2023-12-05 08:20:54 +00:00
|
|
|
// Use low precision/rounded values when we're not shifting an entire component,
|
|
|
|
// and it's not originally from a 3D value. Too many false positives in P2/etc.
|
|
|
|
// What we probably should do is not set the valid flag on non-3D values to begin
|
|
|
|
// with, only letting them become valid when used in another expression.
|
2024-05-17 10:41:55 +00:00
|
|
|
if (!(prdVal.flags & VALID_Z) && sh < 16)
|
2023-12-05 08:20:54 +00:00
|
|
|
{
|
2024-05-17 10:41:55 +00:00
|
|
|
prdVal.flags = 0;
|
|
|
|
MakeValid(&prdVal, rdVal);
|
2023-12-05 08:20:54 +00:00
|
|
|
}
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////
|
|
|
|
// Shift operations variable
|
|
|
|
////////////////////////////////////
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SLLV(u32 instr, u32 rtVal, u32 rsVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rt(instr), rtVal, rs(instr), rsVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rt << Rs
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rdVal = rtVal << rsVal;
|
2024-05-17 09:45:44 +00:00
|
|
|
const u32 sh = rsVal & 0x1F;
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
Validate(&prtVal, rtVal);
|
|
|
|
Validate(&prsVal, rsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
double x = f16Unsign(prtVal.x);
|
|
|
|
double y = f16Unsign(prtVal.y);
|
2020-08-19 13:26:57 +00:00
|
|
|
if (sh >= 32)
|
|
|
|
{
|
|
|
|
x = 0.f;
|
|
|
|
y = 0.f;
|
|
|
|
}
|
|
|
|
else if (sh == 16)
|
|
|
|
{
|
|
|
|
y = f16Sign(x);
|
|
|
|
x = 0.f;
|
|
|
|
}
|
|
|
|
else if (sh >= 16)
|
|
|
|
{
|
|
|
|
y = x * (1 << (sh - 16));
|
|
|
|
y = f16Sign(y);
|
|
|
|
x = 0.f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x = x * (1 << sh);
|
|
|
|
y = y * (1 << sh);
|
|
|
|
y += f16Overflow(x);
|
|
|
|
x = f16Sign(x);
|
|
|
|
y = f16Sign(y);
|
|
|
|
}
|
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prdVal = g_state.pgxp_gpr[rd(instr)];
|
|
|
|
prdVal = prtVal;
|
|
|
|
prdVal.x = static_cast<float>(x);
|
|
|
|
prdVal.y = static_cast<float>(y);
|
|
|
|
prdVal.value = rdVal;
|
2024-05-17 11:18:39 +00:00
|
|
|
prdVal.flags |= VALID_TAINTED_Z;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SRLV(u32 instr, u32 rtVal, u32 rsVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rt(instr), rtVal, rs(instr), rsVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rt >> Sa
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rdVal = rtVal >> rsVal;
|
2024-05-17 09:45:44 +00:00
|
|
|
const u32 sh = rsVal & 0x1F;
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
Validate(&prtVal, rtVal);
|
|
|
|
Validate(&prsVal, rsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
double x = prtVal.x;
|
|
|
|
double y = f16Unsign(prtVal.y);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
psx_value iX;
|
|
|
|
iX.d = rtVal;
|
|
|
|
psx_value iY;
|
|
|
|
iY.d = rtVal;
|
|
|
|
|
|
|
|
iX.sd = (iX.sd << 16) >> 16; // remove Y
|
|
|
|
iY.sw.l = iX.sw.h; // overwrite x with sign(x)
|
|
|
|
|
|
|
|
// Shift test values
|
|
|
|
psx_value dX;
|
|
|
|
dX.sd = iX.sd >> sh;
|
|
|
|
psx_value dY;
|
|
|
|
dY.d = iY.d >> sh;
|
|
|
|
|
|
|
|
if (dX.sw.l != iX.sw.h)
|
|
|
|
x = x / (1 << sh);
|
|
|
|
else
|
|
|
|
x = dX.sw.l; // only sign bits left
|
|
|
|
|
|
|
|
if (dY.sw.l != iX.sw.h)
|
|
|
|
{
|
|
|
|
if (sh == 16)
|
|
|
|
{
|
|
|
|
x = y;
|
|
|
|
}
|
|
|
|
else if (sh < 16)
|
|
|
|
{
|
|
|
|
x += y * (1 << (16 - sh));
|
2023-12-06 06:45:17 +00:00
|
|
|
if (g_state.pgxp_gpr[rt(instr)].x < 0)
|
2020-08-19 13:26:57 +00:00
|
|
|
x += 1 << (16 - sh);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x += y / (1 << (sh - 16));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dY.sw.h == 0) || (dY.sw.h == -1))
|
|
|
|
y = dY.sw.h;
|
|
|
|
else
|
|
|
|
y = y / (1 << sh);
|
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prdVal = g_state.pgxp_gpr[rd(instr)];
|
|
|
|
prdVal = prtVal;
|
|
|
|
prdVal.x = static_cast<float>(f16Sign(x));
|
|
|
|
prdVal.y = static_cast<float>(f16Sign(y));
|
|
|
|
prdVal.value = rdVal;
|
2024-05-17 11:18:39 +00:00
|
|
|
prdVal.flags |= VALID_TAINTED_Z;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_SRAV(u32 instr, u32 rtVal, u32 rsVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C2(rt(instr), rtVal, rs(instr), rsVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// Rd = Rt >> Sa
|
2021-02-17 15:45:32 +00:00
|
|
|
const u32 rdVal = static_cast<u32>(static_cast<s32>(rtVal) >> rsVal);
|
2024-05-17 09:45:44 +00:00
|
|
|
const u32 sh = rsVal & 0x1F;
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prtVal = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
PGXP_value& prsVal = g_state.pgxp_gpr[rs(instr)];
|
|
|
|
Validate(&prtVal, rtVal);
|
|
|
|
Validate(&prsVal, rsVal);
|
2020-08-19 13:26:57 +00:00
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
double x = prtVal.x;
|
|
|
|
double y = prtVal.y;
|
2020-08-19 13:26:57 +00:00
|
|
|
|
|
|
|
psx_value iX;
|
|
|
|
iX.d = rtVal;
|
|
|
|
psx_value iY;
|
|
|
|
iY.d = rtVal;
|
|
|
|
|
|
|
|
iX.sd = (iX.sd << 16) >> 16; // remove Y
|
|
|
|
iY.sw.l = iX.sw.h; // overwrite x with sign(x)
|
|
|
|
|
|
|
|
// Shift test values
|
|
|
|
psx_value dX;
|
|
|
|
dX.sd = iX.sd >> sh;
|
|
|
|
psx_value dY;
|
|
|
|
dY.sd = iY.sd >> sh;
|
|
|
|
|
|
|
|
if (dX.sw.l != iX.sw.h)
|
|
|
|
x = x / (1 << sh);
|
|
|
|
else
|
|
|
|
x = dX.sw.l; // only sign bits left
|
|
|
|
|
|
|
|
if (dY.sw.l != iX.sw.h)
|
|
|
|
{
|
|
|
|
if (sh == 16)
|
|
|
|
{
|
|
|
|
x = y;
|
|
|
|
}
|
|
|
|
else if (sh < 16)
|
|
|
|
{
|
|
|
|
x += y * (1 << (16 - sh));
|
2023-12-06 06:45:17 +00:00
|
|
|
if (g_state.pgxp_gpr[rt(instr)].x < 0)
|
2020-08-19 13:26:57 +00:00
|
|
|
x += 1 << (16 - sh);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x += y / (1 << (sh - 16));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dY.sw.h == 0) || (dY.sw.h == -1))
|
|
|
|
y = dY.sw.h;
|
|
|
|
else
|
|
|
|
y = y / (1 << sh);
|
|
|
|
|
2024-05-17 10:41:55 +00:00
|
|
|
PGXP_value& prdVal = g_state.pgxp_gpr[rd(instr)];
|
|
|
|
prdVal = prtVal;
|
|
|
|
prdVal.x = static_cast<float>(f16Sign(x));
|
|
|
|
prdVal.y = static_cast<float>(f16Sign(y));
|
|
|
|
prdVal.value = rdVal;
|
2024-05-17 11:18:39 +00:00
|
|
|
prdVal.flags |= VALID_TAINTED_Z;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_MFC0(u32 instr, u32 rdVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_1(TinyString::from_format("cop0_{}", rd(instr)).c_str(), rdVal, &g_state.pgxp_cop0[rd(instr)]);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// CPU[Rt] = CP0[Rd]
|
2023-12-06 06:45:17 +00:00
|
|
|
Validate(&g_state.pgxp_cop0[rd(instr)], rdVal);
|
|
|
|
g_state.pgxp_gpr[rt(instr)] = g_state.pgxp_cop0[rd(instr)];
|
|
|
|
g_state.pgxp_gpr[rt(instr)].value = rdVal;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|
|
|
|
|
2023-12-06 06:45:17 +00:00
|
|
|
void CPU::PGXP::CPU_MTC0(u32 instr, u32 rdVal, u32 rtVal)
|
2020-08-19 13:26:57 +00:00
|
|
|
{
|
2024-05-17 07:15:38 +00:00
|
|
|
LOG_VALUES_C1(rt(instr), rtVal);
|
|
|
|
|
2020-08-19 13:26:57 +00:00
|
|
|
// CP0[Rd] = CPU[Rt]
|
2023-12-06 06:45:17 +00:00
|
|
|
Validate(&g_state.pgxp_gpr[rt(instr)], rtVal);
|
|
|
|
g_state.pgxp_cop0[rd(instr)] = g_state.pgxp_gpr[rt(instr)];
|
|
|
|
g_state.pgxp_cop0[rd(instr)].value = rdVal;
|
2020-08-19 13:26:57 +00:00
|
|
|
}
|