Core/DSP/Jit: Implement Saturation/Limiting when reading ACM* in 40 bit mode

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7336 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
pierre 2011-03-12 17:50:19 +00:00
parent 26b6a9af61
commit ca67d025e2
4 changed files with 165 additions and 34 deletions

View File

@ -116,6 +116,7 @@ public:
void dsp_conditional_extend_accum(int reg);
void dsp_conditional_extend_accum_imm(int reg, u16 val);
void dsp_op_read_reg(int reg, Gen::X64Reg host_dreg, DSPJitSignExtend extend = NONE);
void dsp_op_read_reg_and_saturate(int reg, Gen::X64Reg host_dreg, DSPJitSignExtend extend = NONE);
// Commands
void dar(const UDSPInstruction opc);

View File

@ -68,7 +68,11 @@ void DSPEmitter::mv(const UDSPInstruction opc)
{
u8 sreg = (opc & 0x3) + DSP_REG_ACL0;
u8 dreg = ((opc >> 2) & 0x3);
pushExtValueFromReg(dreg + DSP_REG_AXL0, sreg);
if (sreg >= DSP_REG_ACM0) {
dsp_op_read_reg_and_saturate(sreg, RBX, ZERO);
storeIndex = dreg + DSP_REG_AXL0;
} else
pushExtValueFromReg(dreg + DSP_REG_AXL0, sreg);
}
// S @$arD, $acS.S
@ -85,7 +89,10 @@ void DSPEmitter::s(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
dsp_op_read_reg(sreg, tmp1, ZERO);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg, tmp1, ZERO);
else
dsp_op_read_reg(sreg, tmp1, ZERO);
// u16 val = g_dsp.r[src];
dmem_write(tmp1);
@ -107,7 +114,10 @@ void DSPEmitter::sn(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
dsp_op_read_reg(sreg, tmp1, ZERO);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg, tmp1, ZERO);
else
dsp_op_read_reg(sreg, tmp1, ZERO);
dmem_write(tmp1);
gpr.putXReg(tmp1);
@ -177,7 +187,10 @@ void DSPEmitter::ls(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
get_acc_m(sreg, tmp1, false);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg + DSP_REG_ACM0, tmp1, ZERO);
else
dsp_op_read_reg(sreg + DSP_REG_ACM0, tmp1, ZERO);
dmem_write(tmp1);
gpr.putXReg(tmp1);
@ -204,7 +217,10 @@ void DSPEmitter::lsn(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
get_acc_m(sreg, tmp1, false);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg + DSP_REG_ACM0, tmp1, ZERO);
else
dsp_op_read_reg(sreg + DSP_REG_ACM0, tmp1, ZERO);
dmem_write(tmp1);
gpr.putXReg(tmp1);
@ -230,7 +246,10 @@ void DSPEmitter::lsm(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
get_acc_m(sreg, tmp1, false);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg + DSP_REG_ACM0, tmp1, ZERO);
else
dsp_op_read_reg(sreg + DSP_REG_ACM0, tmp1, ZERO);
dmem_write(tmp1);
gpr.putXReg(tmp1);
@ -257,7 +276,10 @@ void DSPEmitter::lsnm(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
get_acc_m(sreg, tmp1, false);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg + DSP_REG_ACM0, tmp1, ZERO);
else
dsp_op_read_reg(sreg + DSP_REG_ACM0, tmp1, ZERO);
dmem_write(tmp1);
gpr.putXReg(tmp1);
@ -282,7 +304,10 @@ void DSPEmitter::sl(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
get_acc_m(sreg, tmp1, false);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg + DSP_REG_ACM0, tmp1, ZERO);
else
dsp_op_read_reg(sreg + DSP_REG_ACM0, tmp1, ZERO);
dmem_write(tmp1);
gpr.putXReg(tmp1);
@ -308,7 +333,10 @@ void DSPEmitter::sln(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
get_acc_m(sreg, tmp1, false);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg + DSP_REG_ACM0, tmp1, ZERO);
else
dsp_op_read_reg(sreg + DSP_REG_ACM0, tmp1, ZERO);
dmem_write(tmp1);
gpr.putXReg(tmp1);
@ -334,7 +362,10 @@ void DSPEmitter::slm(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
get_acc_m(sreg, tmp1, false);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg + DSP_REG_ACM0, tmp1, ZERO);
else
dsp_op_read_reg(sreg + DSP_REG_ACM0, tmp1, ZERO);
dmem_write(tmp1);
gpr.putXReg(tmp1);
@ -360,7 +391,10 @@ void DSPEmitter::slnm(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
get_acc_m(sreg, tmp1, false);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg + DSP_REG_ACM0, tmp1, ZERO);
else
dsp_op_read_reg(sreg + DSP_REG_ACM0, tmp1, ZERO);
dmem_write(tmp1);
gpr.putXReg(tmp1);

View File

@ -39,7 +39,10 @@ void DSPEmitter::srs(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
dsp_op_read_reg(reg, tmp1, ZERO);
if (reg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(reg, tmp1, ZERO);
else
dsp_op_read_reg(reg, tmp1, ZERO);
dsp_op_read_reg(DSP_REG_CR, RAX, ZERO);
SHL(16, R(EAX), Imm8(8));
OR(16, R(EAX), Imm16(opc & 0xFF));
@ -77,7 +80,6 @@ void DSPEmitter::lrs(const UDSPInstruction opc)
// 0000 0000 110d dddd
// mmmm mmmm mmmm mmmm
// Move value from data memory pointed by address M to register $D.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lr(const UDSPInstruction opc)
{
int reg = opc & DSP_REG_MASK;
@ -91,7 +93,6 @@ void DSPEmitter::lr(const UDSPInstruction opc)
// 0000 0000 111s ssss
// mmmm mmmm mmmm mmmm
// Store value from register $S to a memory pointed by address M.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::sr(const UDSPInstruction opc)
{
u8 reg = opc & DSP_REG_MASK;
@ -100,7 +101,10 @@ void DSPEmitter::sr(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
dsp_op_read_reg(reg, tmp1);
if (reg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(reg, tmp1);
else
dsp_op_read_reg(reg, tmp1);
dmem_write_imm(address, tmp1);
gpr.putXReg(tmp1);
@ -128,7 +132,6 @@ void DSPEmitter::si(const UDSPInstruction opc)
// LRR $D, @$S
// 0001 1000 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lrr(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
@ -150,7 +153,6 @@ void DSPEmitter::lrr(const UDSPInstruction opc)
// 0001 1000 1ssd dddd
// Move value from data memory pointed by addressing register $S toregister $D.
// Decrement register $S.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lrrd(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
@ -173,7 +175,6 @@ void DSPEmitter::lrrd(const UDSPInstruction opc)
// 0001 1001 0ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Increment register $S.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lrri(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
@ -196,7 +197,6 @@ void DSPEmitter::lrri(const UDSPInstruction opc)
// 0001 1001 1ssd dddd
// Move value from data memory pointed by addressing register $S to register $D.
// Add indexing register $(0x4+S) to register $S.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lrrn(const UDSPInstruction opc)
{
u8 sreg = (opc >> 5) & 0x3;
@ -219,7 +219,6 @@ void DSPEmitter::lrrn(const UDSPInstruction opc)
// 0001 1010 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D.
// FIXME: Perform additional operation depending on source register.
void DSPEmitter::srr(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
@ -228,7 +227,10 @@ void DSPEmitter::srr(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
dsp_op_read_reg(sreg, tmp1);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg, tmp1);
else
dsp_op_read_reg(sreg, tmp1);
dsp_op_read_reg(dreg, RAX, ZERO);
dmem_write(tmp1);
@ -239,7 +241,6 @@ void DSPEmitter::srr(const UDSPInstruction opc)
// 0001 1010 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Decrement register $D.
// FIXME: Perform additional operation depending on source register.
void DSPEmitter::srrd(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
@ -248,7 +249,10 @@ void DSPEmitter::srrd(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
dsp_op_read_reg(sreg, tmp1);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg, tmp1);
else
dsp_op_read_reg(sreg, tmp1);
dsp_op_read_reg(dreg, RAX, ZERO);
dmem_write(tmp1);
@ -261,7 +265,6 @@ void DSPEmitter::srrd(const UDSPInstruction opc)
// 0001 1011 0dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Increment register $D.
// FIXME: Perform additional operation depending on source register.
void DSPEmitter::srri(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
@ -270,7 +273,10 @@ void DSPEmitter::srri(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
dsp_op_read_reg(sreg, tmp1);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg, tmp1);
else
dsp_op_read_reg(sreg, tmp1);
dsp_op_read_reg(dreg, RAX, ZERO);
dmem_write(tmp1);
@ -283,7 +289,6 @@ void DSPEmitter::srri(const UDSPInstruction opc)
// 0001 1011 1dds ssss
// Store value from source register $S to a memory location pointed by
// addressing register $D. Add DSP_REG_IX0 register to register $D.
// FIXME: Perform additional operation depending on source register.
void DSPEmitter::srrn(const UDSPInstruction opc)
{
u8 dreg = (opc >> 5) & 0x3;
@ -292,7 +297,10 @@ void DSPEmitter::srrn(const UDSPInstruction opc)
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
dsp_op_read_reg(sreg, tmp1);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg, tmp1);
else
dsp_op_read_reg(sreg, tmp1);
dsp_op_read_reg(dreg, RAX, ZERO);
dmem_write(tmp1);
@ -319,7 +327,7 @@ void DSPEmitter::ilrr(const UDSPInstruction opc)
gpr.putXReg(tmp1);
set_acc_m(dreg, R(RAX));
dsp_conditional_extend_accum(dreg);
dsp_conditional_extend_accum(dreg + DSP_REG_ACM0);
}
// ILRRD $acD.m, @$arS
@ -340,7 +348,7 @@ void DSPEmitter::ilrrd(const UDSPInstruction opc)
gpr.putXReg(tmp1);
set_acc_m(dreg, R(RAX));
dsp_conditional_extend_accum(dreg);
dsp_conditional_extend_accum(dreg + DSP_REG_ACM0);
decrement_addr_reg(reg);
}
@ -362,7 +370,7 @@ void DSPEmitter::ilrri(const UDSPInstruction opc)
gpr.putXReg(tmp1);
set_acc_m(dreg, R(RAX));
dsp_conditional_extend_accum(dreg);
dsp_conditional_extend_accum(dreg + DSP_REG_ACM0);
increment_addr_reg(reg);
}
@ -385,7 +393,7 @@ void DSPEmitter::ilrrn(const UDSPInstruction opc)
gpr.putXReg(tmp1);
set_acc_m(dreg, R(RAX));
dsp_conditional_extend_accum(dreg);
dsp_conditional_extend_accum(dreg + DSP_REG_ACM0);
increase_addr_reg(reg, reg);
}

View File

@ -239,16 +239,105 @@ void DSPEmitter::dsp_op_read_reg(int reg, Gen::X64Reg host_dreg, DSPJitSignExten
}
}
void DSPEmitter::dsp_op_read_reg_and_saturate(int reg, Gen::X64Reg host_dreg, DSPJitSignExtend extend)
{
//we already know this is ACCM0 or ACCM1
#ifdef _M_IX86 // All32
gpr.readReg(reg, host_dreg, extend);
#else
OpArg acc_reg;
gpr.getReg(reg-DSP_REG_ACM0+DSP_REG_ACC0_64, acc_reg);
#endif
OpArg sr_reg;
gpr.getReg(DSP_REG_SR,sr_reg);
DSPJitRegCache c(gpr);
TEST(16, sr_reg, Imm16(SR_40_MODE_BIT));
FixupBranch not_40bit = J_CC(CC_Z, true);
#ifdef _M_IX86 // All32
DSPJitRegCache c2(gpr);
gpr.putReg(DSP_REG_SR, false);
X64Reg tmp1;
gpr.getFreeXReg(tmp1);
gpr.readReg(reg-DSP_REG_ACM0+DSP_REG_ACH0, tmp1, NONE);
MOVSX(32,16,host_dreg,R(host_dreg));
SHL(32, R(tmp1), Imm8(16));
MOV(16,R(tmp1),R(host_dreg));
CMP(32,R(host_dreg), R(tmp1));
FixupBranch no_saturate = J_CC(CC_Z);
CMP(32,R(tmp1),Imm32(0));
FixupBranch negative = J_CC(CC_LE);
MOV(32,R(host_dreg),Imm32(0x7fff));//this works for all extend modes
FixupBranch done_positive = J();
SetJumpTarget(negative);
if (extend == NONE || extend == ZERO)
MOV(32,R(host_dreg),Imm32(0x00008000));
else
MOV(32,R(host_dreg),Imm32(0xffff8000));
FixupBranch done_negative = J();
SetJumpTarget(no_saturate);
if (extend == ZERO)
MOVZX(32,16,host_dreg,R(host_dreg));
SetJumpTarget(done_positive);
SetJumpTarget(done_negative);
gpr.putXReg(tmp1);
gpr.flushRegs(c2);
SetJumpTarget(not_40bit);
gpr.flushRegs(c);
#else
MOVSX(64,32,host_dreg,acc_reg);
CMP(64,R(host_dreg),acc_reg);
FixupBranch no_saturate = J_CC(CC_Z);
CMP(64,acc_reg,Imm32(0));
FixupBranch negative = J_CC(CC_LE);
MOV(64,R(host_dreg),Imm32(0x7fff));//this works for all extend modes
FixupBranch done_positive = J();
SetJumpTarget(negative);
if (extend == NONE || extend == ZERO)
MOV(64,R(host_dreg),Imm32(0x00008000));
else
MOV(64,R(host_dreg),Imm32(0xffff8000));
FixupBranch done_negative = J();
SetJumpTarget(no_saturate);
SetJumpTarget(not_40bit);
MOV(64, R(host_dreg), acc_reg);
if (extend == NONE || extend == ZERO)
SHR(64, R(host_dreg), Imm8(16));
else
SAR(64, R(host_dreg), Imm8(16));
SetJumpTarget(done_positive);
SetJumpTarget(done_negative);
gpr.flushRegs(c);
gpr.putReg(reg-DSP_REG_ACM0+DSP_REG_ACC0_64, false);
#endif
gpr.putReg(DSP_REG_SR, false);
}
// MRR $D, $S
// 0001 11dd ddds ssss
// Move value from register $S to register $D.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::mrr(const UDSPInstruction opc)
{
u8 sreg = opc & 0x1f;
u8 dreg = (opc >> 5) & 0x1f;
dsp_op_read_reg(sreg, EDX);
if (sreg >= DSP_REG_ACM0)
dsp_op_read_reg_and_saturate(sreg, EDX);
else
dsp_op_read_reg(sreg, EDX);
dsp_op_write_reg(dreg, EDX);
dsp_conditional_extend_accum(dreg);
}
@ -273,7 +362,6 @@ void DSPEmitter::lri(const UDSPInstruction opc)
// LRIS $(0x18+D), #I
// 0000 1ddd iiii iiii
// Load immediate value I (8-bit sign extended) to accumulator register.
// FIXME: Perform additional operation depending on destination register.
void DSPEmitter::lris(const UDSPInstruction opc)
{
u8 reg = ((opc >> 8) & 0x7) + DSP_REG_AXL0;