CPU/PGXP: Purge psx_value from first half of instructions

This commit is contained in:
Stenzek 2024-08-20 16:27:39 +10:00
parent 9a6de196a3
commit 302652df22
No known key found for this signature in database
1 changed files with 78 additions and 82 deletions

View File

@ -55,6 +55,11 @@ enum : u32
VALID_ALL = (VALID_X | VALID_Y | VALID_Z), VALID_ALL = (VALID_X | VALID_Y | VALID_Z),
}; };
#define LOWORD_U16(val) (static_cast<u16>(val))
#define HIWORD_U16(val) (static_cast<u16>(static_cast<u32>(val) >> 16))
#define LOWORD_S16(val) (static_cast<s16>(static_cast<u16>(val)))
#define HIWORD_S16(val) (static_cast<s16>(static_cast<u16>(static_cast<u32>(val) >> 16)))
union psx_value union psx_value
{ {
u32 d; u32 d;
@ -104,7 +109,7 @@ static void WriteMem(u32 addr, const PGXP_value& value);
static void WriteMem16(u32 addr, const PGXP_value& value); static void WriteMem16(u32 addr, const PGXP_value& value);
static void CopyZIfMissing(PGXP_value& dst, const PGXP_value& src); static void CopyZIfMissing(PGXP_value& dst, const PGXP_value& src);
static void SelectZ(PGXP_value& dst, const PGXP_value& src1, const PGXP_value& src2); static void SelectZ(float& dst_z, u32& dst_flags, const PGXP_value& src1, const PGXP_value& src2);
#ifdef LOG_VALUES #ifdef LOG_VALUES
static void LogInstruction(u32 pc, Instruction instr); static void LogInstruction(u32 pc, Instruction instr);
@ -418,14 +423,15 @@ ALWAYS_INLINE_RELEASE void CPU::PGXP::CopyZIfMissing(PGXP_value& dst, const PGXP
dst.flags |= (src.flags & VALID_Z); dst.flags |= (src.flags & VALID_Z);
} }
ALWAYS_INLINE_RELEASE void CPU::PGXP::SelectZ(PGXP_value& dst, const PGXP_value& src1, const PGXP_value& src2) ALWAYS_INLINE_RELEASE void CPU::PGXP::SelectZ(float& dst_z, u32& dst_flags, const PGXP_value& src1,
const PGXP_value& src2)
{ {
// Prefer src2 if src1 is missing Z, or is potentially an imprecise value, when src2 is precise. // Prefer src2 if src1 is missing Z, or is potentially an imprecise value, when src2 is precise.
dst.z = (!(src1.flags & VALID_Z) || dst_z = (!(src1.flags & VALID_Z) ||
(src1.flags & VALID_TAINTED_Z && (src2.flags & (VALID_Z | VALID_TAINTED_Z)) == VALID_Z)) ? (src1.flags & VALID_TAINTED_Z && (src2.flags & (VALID_Z | VALID_TAINTED_Z)) == VALID_Z)) ?
src2.z : src2.z :
src1.z; src1.z;
dst.flags |= ((src1.flags | src2.flags) & VALID_Z); dst_flags |= ((src1.flags | src2.flags) & VALID_Z);
} }
#ifdef LOG_VALUES #ifdef LOG_VALUES
@ -767,38 +773,37 @@ void CPU::PGXP::CPU_ADDI(Instruction instr, u32 rsVal)
// Rt = Rs + Imm (signed) // Rt = Rs + Imm (signed)
PGXP_value& prsVal = ValidateAndGetRsValue(instr, rsVal); PGXP_value& prsVal = ValidateAndGetRsValue(instr, rsVal);
psx_value imm; const u32 immVal = instr.i.imm_sext32();
imm.d = instr.i.imm_sext32();
PGXP_value& prtVal = GetRtValue(instr); PGXP_value& prtVal = GetRtValue(instr);
prtVal = prsVal; prtVal = prsVal;
if (imm.d == 0) if (immVal == 0)
return; return;
if (rsVal == 0) if (rsVal == 0)
{ {
// x is low precision value // x is low precision value
prtVal.x = static_cast<float>(imm.sw.l); prtVal.x = static_cast<float>(LOWORD_S16(immVal));
prtVal.y = static_cast<float>(imm.sw.h); prtVal.y = static_cast<float>(HIWORD_S16(immVal));
prtVal.flags |= VALID_X | VALID_Y | VALID_TAINTED_Z; prtVal.flags |= VALID_X | VALID_Y | VALID_TAINTED_Z;
prtVal.value = imm.d; prtVal.value = immVal;
return; return;
} }
prtVal.x = (float)f16Unsign(prtVal.x); prtVal.x = static_cast<float>(f16Unsign(prtVal.x));
prtVal.x += (float)imm.w.l; prtVal.x += static_cast<float>(LOWORD_U16(immVal));
// carry on over/underflow // carry on over/underflow
float of = (prtVal.x > USHRT_MAX) ? 1.f : (prtVal.x < 0) ? -1.f : 0.f; float of = (prtVal.x > USHRT_MAX) ? 1.f : (prtVal.x < 0) ? -1.f : 0.f;
prtVal.x = (float)f16Sign(prtVal.x); prtVal.x = static_cast<float>(f16Sign(prtVal.x));
// ret.x -= of * (USHRT_MAX + 1); // ret.x -= of * (USHRT_MAX + 1);
prtVal.y += imm.sw.h + of; prtVal.y += HIWORD_S16(immVal) + of;
// truncate on overflow/underflow // truncate on overflow/underflow
prtVal.y += (prtVal.y > SHRT_MAX) ? -(USHRT_MAX + 1) : (prtVal.y < SHRT_MIN) ? USHRT_MAX + 1 : 0.f; prtVal.y += (prtVal.y > SHRT_MAX) ? -(USHRT_MAX + 1) : (prtVal.y < SHRT_MIN) ? USHRT_MAX + 1 : 0.f;
prtVal.value = rsVal + imm.d; prtVal.value = rsVal + immVal;
prtVal.flags |= VALID_TAINTED_Z; prtVal.flags |= VALID_TAINTED_Z;
} }
@ -811,33 +816,37 @@ void CPU::PGXP::CPU_ANDI(Instruction instr, u32 rsVal)
const u32 imm = instr.i.imm_zext32(); const u32 imm = instr.i.imm_zext32();
const u32 rtVal = rsVal & imm; const u32 rtVal = rsVal & imm;
PGXP_value& prsVal = ValidateAndGetRsValue(instr, rsVal); PGXP_value& prsVal = ValidateAndGetRsValue(instr, rsVal);
psx_value vRt;
vRt.d = rtVal;
PGXP_value& prtVal = GetRtValue(instr); PGXP_value& prtVal = GetRtValue(instr);
prtVal = prsVal;
prtVal.y = 0.0f; // remove upper 16-bits
prtVal.z = prsVal.z;
prtVal.value = rtVal; prtVal.value = rtVal;
prtVal.y = 0.f; // remove upper 16-bits prtVal.flags = prsVal.flags | VALID_Y | VALID_TAINTED_Z;
prtVal.SetValid(COMP_Y);
prtVal.flags |= VALID_TAINTED_Z;
switch (imm) switch (imm)
{ {
case 0: case 0:
{
// if 0 then x == 0 // if 0 then x == 0
prtVal.x = 0.0f; prtVal.x = 0.0f;
prtVal.SetValid(COMP_X); prtVal.SetValid(COMP_X);
break; }
case 0xFFFF: break;
case 0xFFFFu:
{
// if saturated then x == x // if saturated then x == x
break; prtVal.x = prsVal.x;
}
break;
default: default:
{
// otherwise x is low precision value // otherwise x is low precision value
prtVal.x = vRt.sw.l; prtVal.x = static_cast<float>(LOWORD_S16(rtVal));
prtVal.SetValid(COMP_X); prtVal.SetValid(COMP_X);
break; }
break;
} }
} }
@ -854,20 +863,15 @@ void CPU::PGXP::CPU_ORI(Instruction instr, u32 rsVal)
pRtVal = pRsVal; pRtVal = pRsVal;
pRtVal.value = rtVal; pRtVal.value = rtVal;
psx_value vRt; if (imm == 0) [[unlikely]]
vRt.d = rtVal;
switch (imm)
{ {
case 0: // if 0 then x == x
// if 0 then x == x }
break; else
default: {
// otherwise x is low precision value // otherwise x is low precision value
pRtVal.x = vRt.sw.l; pRtVal.x = static_cast<float>(LOWORD_S16(rtVal));
pRtVal.SetValid(COMP_X); pRtVal.flags |= VALID_X | VALID_TAINTED_Z;
pRtVal.flags |= VALID_TAINTED_Z;
break;
} }
} }
@ -884,20 +888,15 @@ void CPU::PGXP::CPU_XORI(Instruction instr, u32 rsVal)
pRtVal = pRsVal; pRtVal = pRsVal;
pRtVal.value = rtVal; pRtVal.value = rtVal;
psx_value vRt; if (imm == 0) [[unlikely]]
vRt.d = rtVal;
switch (imm)
{ {
case 0: // if 0 then x == x
// if 0 then x == x }
break; else
default: {
// otherwise x is low precision value // otherwise x is low precision value
pRtVal.x = vRt.sw.l; pRtVal.x = static_cast<float>(LOWORD_S16(rtVal));
pRtVal.SetValid(COMP_X); pRtVal.flags |= VALID_X | VALID_TAINTED_Z;
pRtVal.flags |= VALID_TAINTED_Z;
break;
} }
} }
@ -996,7 +995,7 @@ void CPU::PGXP::CPU_ADD(Instruction instr, u32 rsVal, u32 rtVal)
// valid x/y only if one side had a valid x/y // valid x/y only if one side had a valid x/y
prdVal.flags = prsVal.flags | (prtVal.flags & VALID_XY) | VALID_TAINTED_Z; prdVal.flags = prsVal.flags | (prtVal.flags & VALID_XY) | VALID_TAINTED_Z;
SelectZ(prdVal, prsVal, prtVal); SelectZ(prdVal.z, prdVal.flags, prsVal, prtVal);
} }
} }
@ -1032,7 +1031,7 @@ void CPU::PGXP::CPU_SUB(Instruction instr, u32 rsVal, u32 rtVal)
// valid x/y only if one side had a valid x/y // valid x/y only if one side had a valid x/y
prdVal.flags = prsVal.flags | (prtVal.flags & VALID_XY) | VALID_TAINTED_Z; prdVal.flags = prsVal.flags | (prtVal.flags & VALID_XY) | VALID_TAINTED_Z;
SelectZ(prdVal, prsVal, prtVal); SelectZ(prdVal.z, prdVal.flags, prsVal, prtVal);
} }
} }
@ -1042,36 +1041,33 @@ ALWAYS_INLINE_RELEASE void CPU::PGXP::CPU_BITWISE(Instruction instr, u32 rdVal,
PGXP_value& prsVal = ValidateAndGetRsValue(instr, rsVal); PGXP_value& prsVal = ValidateAndGetRsValue(instr, rsVal);
PGXP_value& prtVal = ValidateAndGetRtValue(instr, rtVal); PGXP_value& prtVal = ValidateAndGetRtValue(instr, rtVal);
psx_value vald, vals, valt; float x, y;
vald.d = rdVal; if (LOWORD_U16(rdVal) == 0)
vals.d = rsVal; x = 0.0f;
valt.d = rtVal; else if (LOWORD_U16(rdVal) == LOWORD_U16(rsVal))
x = prsVal.GetValidX(rsVal);
PGXP_value ret; else if (LOWORD_U16(rdVal) == LOWORD_U16(rtVal))
ret.flags = ((prsVal.flags | prtVal.flags) & VALID_XY) ? (VALID_XY | VALID_TAINTED_Z) : 0; x = prtVal.GetValidX(rtVal);
if (vald.w.l == 0)
ret.x = 0.f;
else if (vald.w.l == vals.w.l)
ret.x = prsVal.GetValidX(rsVal);
else if (vald.w.l == valt.w.l)
ret.x = prtVal.GetValidX(rtVal);
else else
ret.x = static_cast<float>(vald.sw.l); x = static_cast<float>(LOWORD_S16(rdVal));
if (vald.w.h == 0) if (HIWORD_U16(rdVal) == 0)
ret.y = 0.f; y = 0.0f;
else if (vald.w.h == vals.w.h) else if (HIWORD_U16(rdVal) == HIWORD_U16(rsVal))
ret.y = prsVal.GetValidY(rsVal); y = prsVal.GetValidY(rsVal);
else if (vald.w.h == valt.w.h) else if (HIWORD_U16(rdVal) == HIWORD_U16(rtVal))
ret.y = prtVal.GetValidY(rtVal); y = prtVal.GetValidY(rtVal);
else else
ret.y = static_cast<float>(vald.sw.h); y = static_cast<float>(HIWORD_S16(rdVal));
SelectZ(ret, prsVal, prtVal); // Why not write directly to prdVal? Because it might be the same as the source.
u32 flags = ((prsVal.flags | prtVal.flags) & VALID_XY) ? (VALID_XY | VALID_TAINTED_Z) : 0;
ret.value = rdVal; PGXP_value& prdVal = GetRdValue(instr);
GetRdValue(instr) = ret; SelectZ(prdVal.z, flags, prsVal, prtVal);
prdVal.x = x;
prdVal.y = y;
prdVal.flags = flags;
prdVal.value = rdVal;
} }
void CPU::PGXP::CPU_AND_(Instruction instr, u32 rsVal, u32 rtVal) void CPU::PGXP::CPU_AND_(Instruction instr, u32 rsVal, u32 rtVal)