mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
DSPJit: Rework overflow and carry handling
This commit is contained in:
@@ -228,6 +228,7 @@ private:
|
||||
void get_long_prod(Gen::X64Reg long_prod = Gen::RAX);
|
||||
void get_long_prod_round_prodl(Gen::X64Reg long_prod = Gen::RAX);
|
||||
void set_long_prod();
|
||||
void dsp_convert_long_acc(Gen::X64Reg long_acc); // s64 -> s40
|
||||
void round_long_acc(Gen::X64Reg long_acc = Gen::EAX);
|
||||
void set_long_acc(int _reg, Gen::X64Reg acc = Gen::EAX);
|
||||
void get_acc_h(int _reg, Gen::X64Reg acc = Gen::EAX, bool sign = true);
|
||||
@@ -246,7 +247,16 @@ private:
|
||||
|
||||
// CC helpers
|
||||
void Update_SR_Register64(Gen::X64Reg val = Gen::EAX, Gen::X64Reg scratch = Gen::EDX);
|
||||
void Update_SR_Register64_Carry(Gen::X64Reg val, Gen::X64Reg carry_ovfl, bool carry_eq = false);
|
||||
void UpdateSR64AddSub(Gen::X64Reg val1, Gen::X64Reg val2, Gen::X64Reg result, Gen::X64Reg scratch,
|
||||
bool subtract);
|
||||
void UpdateSR64Add(Gen::X64Reg val1, Gen::X64Reg val2, Gen::X64Reg result, Gen::X64Reg scratch)
|
||||
{
|
||||
UpdateSR64AddSub(val1, val2, result, scratch, false);
|
||||
}
|
||||
void UpdateSR64Sub(Gen::X64Reg val1, Gen::X64Reg val2, Gen::X64Reg result, Gen::X64Reg scratch)
|
||||
{
|
||||
UpdateSR64AddSub(val1, val2, result, scratch, true);
|
||||
}
|
||||
void Update_SR_Register16(Gen::X64Reg val = Gen::EAX);
|
||||
void Update_SR_Register16_OverS32(Gen::X64Reg val = Gen::EAX);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,45 +65,52 @@ void DSPEmitter::Update_SR_Register64(Gen::X64Reg val, Gen::X64Reg scratch)
|
||||
Update_SR_Register(val, scratch);
|
||||
}
|
||||
|
||||
// In: (val): s64 _Value
|
||||
// In: (carry_ovfl): 1 = carry, 2 = overflow
|
||||
// Clobbers RDX
|
||||
void DSPEmitter::Update_SR_Register64_Carry(X64Reg val, X64Reg carry_ovfl, bool carry_eq)
|
||||
// Updates SR based on a 64-bit value computed by result = val1 + val2 or result = val1 - val2
|
||||
// Clobbers scratch
|
||||
void DSPEmitter::UpdateSR64AddSub(Gen::X64Reg val1, Gen::X64Reg val2, Gen::X64Reg result,
|
||||
Gen::X64Reg scratch, bool subtract)
|
||||
{
|
||||
const OpArg sr_reg = m_gpr.GetReg(DSP_REG_SR);
|
||||
// g_dsp.r[DSP_REG_SR] &= ~SR_CMP_MASK;
|
||||
// g_dsp.r[DSP_REG_SR] &= ~SR_CMP_MASK;
|
||||
AND(16, sr_reg, Imm16(~SR_CMP_MASK));
|
||||
|
||||
CMP(64, R(carry_ovfl), R(val));
|
||||
CMP(64, R(val1), R(result));
|
||||
// x86 ZF set if val1 == result
|
||||
// x86 CF set if val1 < result
|
||||
// Note that x86 uses a different definition of carry than the DSP
|
||||
|
||||
// 0x01
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_CARRY;
|
||||
// Carry = (acc>res)
|
||||
// Carry2 = (acc>=res)
|
||||
FixupBranch noCarry = J_CC(carry_eq ? CC_B : CC_BE);
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_CARRY;
|
||||
// isCarryAdd = (val1 > result) => skip setting if (val <= result) => jump if ZF or CF => use JBE
|
||||
// isCarrySubtract = (val1 >= result) => skip setting if (val < result) => jump if CF => use JB
|
||||
FixupBranch noCarry = J_CC(subtract ? CC_B : CC_BE);
|
||||
OR(16, sr_reg, Imm16(SR_CARRY));
|
||||
SetJumpTarget(noCarry);
|
||||
|
||||
// 0x02 and 0x80
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW;
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW_STICKY;
|
||||
// Overflow = ((acc ^ res) & (ax ^ res)) < 0
|
||||
XOR(64, R(carry_ovfl), R(val));
|
||||
XOR(64, R(RDX), R(val));
|
||||
TEST(64, R(carry_ovfl), R(RDX));
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW;
|
||||
// g_dsp.r[DSP_REG_SR] |= SR_OVERFLOW_STICKY;
|
||||
// Overflow (add) = ((val1 ^ res) & (val2 ^ res)) < 0
|
||||
// Overflow (sub) = ((val1 ^ res) & (-val2 ^ res)) < 0
|
||||
MOV(64, R(scratch), R(val1));
|
||||
XOR(64, R(scratch), R(result));
|
||||
|
||||
if (subtract)
|
||||
NEG(64, R(val2));
|
||||
XOR(64, R(result), R(val2));
|
||||
|
||||
TEST(64, R(scratch), R(result)); // Test scratch & value
|
||||
FixupBranch noOverflow = J_CC(CC_GE);
|
||||
OR(16, sr_reg, Imm16(SR_OVERFLOW | SR_OVERFLOW_STICKY));
|
||||
SetJumpTarget(noOverflow);
|
||||
|
||||
// Restore result and val2 -- TODO: does this really matter?
|
||||
XOR(64, R(result), R(val2));
|
||||
if (subtract)
|
||||
NEG(64, R(val2));
|
||||
|
||||
m_gpr.PutReg(DSP_REG_SR);
|
||||
if (carry_eq)
|
||||
{
|
||||
Update_SR_Register();
|
||||
}
|
||||
else
|
||||
{
|
||||
Update_SR_Register(val);
|
||||
}
|
||||
Update_SR_Register(result, scratch);
|
||||
}
|
||||
|
||||
// In: RAX: s64 _Value
|
||||
|
||||
@@ -259,13 +259,14 @@ void DSPEmitter::addpaxz(const UDSPInstruction opc)
|
||||
// s64 oldprod = dsp_get_long_prod();
|
||||
// dsp_set_long_acc(dreg, res);
|
||||
// res = dsp_get_long_acc(dreg);
|
||||
// Update_SR_Register64(res, isCarry(oldprod, res), false);
|
||||
// Update_SR_Register64(res, isCarryAdd(oldprod, res), false);
|
||||
if (FlagsNeeded())
|
||||
{
|
||||
get_long_prod(RDX);
|
||||
MOV(64, R(RCX), R(RAX));
|
||||
set_long_acc(dreg, RCX);
|
||||
Update_SR_Register64_Carry(EAX, tmp1);
|
||||
// TODO: Why does this not set the overflow bit? (And thus, why can't it use UpdateSR64Add?)
|
||||
Update_SR_Register64(EAX, tmp1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -690,7 +690,15 @@ void DSPEmitter::set_long_prod()
|
||||
m_gpr.PutReg(DSP_REG_PROD_64, true);
|
||||
}
|
||||
|
||||
// Returns s64 in RAX
|
||||
// s64 -> s40 in long_acc
|
||||
void DSPEmitter::dsp_convert_long_acc(Gen::X64Reg long_acc)
|
||||
{
|
||||
// return ((long_acc << (64 - 40)) >> (64 - 40))
|
||||
SHL(64, R(long_acc), Imm8(64 - 40)); // sign extend
|
||||
SAR(64, R(long_acc), Imm8(64 - 40));
|
||||
}
|
||||
|
||||
// Returns s64 in long_acc
|
||||
void DSPEmitter::round_long_acc(X64Reg long_acc)
|
||||
{
|
||||
// if (prod & 0x10000) prod = (prod + 0x8000) & ~0xffff;
|
||||
|
||||
Reference in New Issue
Block a user