summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTilka <tilkax@gmail.com>2021-08-18 20:20:31 +0100
committerGitHub <noreply@github.com>2021-08-18 20:20:31 +0100
commit3aaab258109f7d1d373a020ccbd61a2d428aec79 (patch)
tree904b774e3f7cffc6abbe44a97ccbe7666ae4f4fa
parent33154de614981f60a6d4dcd9328a86ed3af8808e (diff)
parent439bf1597b0c3c067c9aef40a65118550ff7a7bd (diff)
Merge pull request #10032 from Pokechu22/dsp-lle-masks
DSP masking and sign extension fixes
-rw-r--r--Source/Core/Core/DSP/DSPCore.h2
-rw-r--r--Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp18
-rw-r--r--Source/Core/Core/DSP/Jit/x64/DSPJitRegCache.cpp53
-rw-r--r--Source/Core/Core/DSP/Jit/x64/DSPJitUtil.cpp11
-rw-r--r--Source/DSPSpy/tests/dsp_base.inc13
-rw-r--r--Source/DSPSpy/tests/reg_mask_test.ds65
6 files changed, 140 insertions, 22 deletions
diff --git a/Source/Core/Core/DSP/DSPCore.h b/Source/Core/Core/DSP/DSPCore.h
index 8162dde619..506e3995d4 100644
--- a/Source/Core/Core/DSP/DSPCore.h
+++ b/Source/Core/Core/DSP/DSPCore.h
@@ -200,7 +200,7 @@ enum : u16
SR_LOGIC_ZERO = 0x0040,
SR_OVERFLOW_STICKY =
0x0080, // Set at the same time as 0x2 (under same conditions) - but not cleared the same
- SR_100 = 0x0100, // Unknown
+ SR_100 = 0x0100, // Unknown, always reads back as 0
SR_INT_ENABLE = 0x0200, // Not 100% sure but duddie says so. This should replace the hack, if so.
SR_400 = 0x0400, // Unknown
SR_EXT_INT_ENABLE = 0x0800, // Appears in zelda - seems to disable external interrupts
diff --git a/Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp b/Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp
index 5d8fd7e32b..1fd3aa3ff3 100644
--- a/Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp
+++ b/Source/Core/Core/DSP/Interpreter/DSPInterpreter.cpp
@@ -394,13 +394,14 @@ s16 Interpreter::GetAXHigh(s32 reg) const
s64 Interpreter::GetLongAcc(s32 reg) const
{
const auto& state = m_dsp_core.DSPState();
- return static_cast<s64>(state.r.ac[reg].val << 24) >> 24;
+ return static_cast<s64>(state.r.ac[reg].val);
}
void Interpreter::SetLongAcc(s32 reg, s64 value)
{
auto& state = m_dsp_core.DSPState();
- state.r.ac[reg].val = static_cast<u64>(value);
+ // 40-bit sign extension
+ state.r.ac[reg].val = static_cast<u64>((value << (64 - 40)) >> (64 - 40));
}
s16 Interpreter::GetAccLow(s32 reg) const
@@ -687,11 +688,11 @@ void Interpreter::OpWriteRegister(int reg_, u16 val)
switch (reg)
{
- // 8-bit sign extended registers. Should look at prod.h too...
+ // 8-bit sign extended registers.
case DSP_REG_ACH0:
case DSP_REG_ACH1:
- // sign extend from the bottom 8 bits.
- state.r.ac[reg - DSP_REG_ACH0].h = (u16)(s16)(s8)(u8)val;
+ // Sign extend from the bottom 8 bits.
+ state.r.ac[reg - DSP_REG_ACH0].h = static_cast<s8>(val);
break;
// Stack registers.
@@ -720,10 +721,10 @@ void Interpreter::OpWriteRegister(int reg_, u16 val)
state.r.wr[reg - DSP_REG_WR0] = val;
break;
case DSP_REG_CR:
- state.r.cr = val;
+ state.r.cr = val & 0x00ff;
break;
case DSP_REG_SR:
- state.r.sr = val;
+ state.r.sr = val & ~SR_100;
break;
case DSP_REG_PRODL:
state.r.prod.l = val;
@@ -732,7 +733,8 @@ void Interpreter::OpWriteRegister(int reg_, u16 val)
state.r.prod.m = val;
break;
case DSP_REG_PRODH:
- state.r.prod.h = val;
+ // Unlike ac0.h and ac1.h, prod.h is not sign-extended
+ state.r.prod.h = val & 0x00ff;
break;
case DSP_REG_PRODM2:
state.r.prod.m2 = val;
diff --git a/Source/Core/Core/DSP/Jit/x64/DSPJitRegCache.cpp b/Source/Core/Core/DSP/Jit/x64/DSPJitRegCache.cpp
index d6ffde8d00..1d3b2768de 100644
--- a/Source/Core/Core/DSP/Jit/x64/DSPJitRegCache.cpp
+++ b/Source/Core/Core/DSP/Jit/x64/DSPJitRegCache.cpp
@@ -751,6 +751,7 @@ void DSPJitRegCache::PutReg(int reg, bool dirty)
else if (oparg.IsImm())
{
// TODO: Immediates?
+ ASSERT(false);
}
else
{
@@ -772,6 +773,58 @@ void DSPJitRegCache::PutReg(int reg, bool dirty)
m_emitter.SAR(64, oparg, Imm8(64 - 40));
}
break;
+ case DSP_REG_CR:
+ case DSP_REG_PRODH:
+ if (dirty)
+ {
+ if (oparg.IsSimpleReg())
+ {
+ // register is already shifted correctly
+ // (if at all)
+
+ // Zero extend from the bottom 8 bits.
+ m_emitter.MOVZX(16, 8, oparg.GetSimpleReg(), oparg);
+ }
+ else if (oparg.IsImm())
+ {
+ // TODO: Immediates?
+ ASSERT(false);
+ }
+ else
+ {
+ // This works on the memory, so use reg instead
+ // of real_reg, since it has the right loc
+ X64Reg tmp = GetFreeXReg();
+ // Zero extend from the bottom 8 bits.
+ m_emitter.MOVZX(16, 8, tmp, m_regs[reg].loc);
+ m_emitter.MOV(16, m_regs[reg].loc, R(tmp));
+ PutXReg(tmp);
+ }
+ }
+ break;
+ case DSP_REG_SR:
+ if (dirty)
+ {
+ if (oparg.IsSimpleReg())
+ {
+ // register is already shifted correctly
+ // (if at all)
+
+ // Clear SR_100, which always reads back as 0
+ m_emitter.AND(16, R(oparg.GetSimpleReg()), Gen::Imm16(~SR_100));
+ }
+ else if (oparg.IsImm())
+ {
+ // TODO: Immediates?
+ ASSERT(false);
+ }
+ else
+ {
+ // Clear SR_100, which always reads back as 0
+ m_emitter.AND(16, m_regs[reg].loc, Gen::Imm16(~SR_100));
+ }
+ }
+ break;
default:
break;
}
diff --git a/Source/Core/Core/DSP/Jit/x64/DSPJitUtil.cpp b/Source/Core/Core/DSP/Jit/x64/DSPJitUtil.cpp
index 896fd30d40..4957947b40 100644
--- a/Source/Core/Core/DSP/Jit/x64/DSPJitUtil.cpp
+++ b/Source/Core/Core/DSP/Jit/x64/DSPJitUtil.cpp
@@ -109,12 +109,6 @@ void DSPEmitter::dsp_op_write_reg(int reg, Gen::X64Reg host_sreg)
{
switch (reg & 0x1f)
{
- // 8-bit sign extended registers.
- case DSP_REG_ACH0:
- case DSP_REG_ACH1:
- m_gpr.WriteReg(reg, R(host_sreg));
- break;
-
// Stack registers.
case DSP_REG_ST0:
case DSP_REG_ST1:
@@ -133,11 +127,6 @@ void DSPEmitter::dsp_op_write_reg_imm(int reg, u16 val)
{
switch (reg & 0x1f)
{
- // 8-bit sign extended registers. Should look at prod.h too...
- case DSP_REG_ACH0:
- case DSP_REG_ACH1:
- m_gpr.WriteReg(reg, Imm16((u16)(s16)(s8)(u8)val));
- break;
// Stack registers.
case DSP_REG_ST0:
case DSP_REG_ST1:
diff --git a/Source/DSPSpy/tests/dsp_base.inc b/Source/DSPSpy/tests/dsp_base.inc
index c890390314..46441fd674 100644
--- a/Source/DSPSpy/tests/dsp_base.inc
+++ b/Source/DSPSpy/tests/dsp_base.inc
@@ -173,6 +173,9 @@ send_back:
; first, store $sr so we can modify it
sr @(REGS_BASE + 19), $sr
set16
+ ; Now store $wr0, as it must be 0xffff for srri to work as we expect
+ sr @(REGS_BASE + 8), $wr0
+ lri $wr0, #0xffff
; store registers to reg table
sr @REGS_BASE, $ar0
lri $ar0, #(REGS_BASE + 1)
@@ -183,7 +186,8 @@ send_back:
srri @$ar0, $ix1
srri @$ar0, $ix2
srri @$ar0, $ix3
- srri @$ar0, $wr0
+ ; skip $wr0 since we already stored and modified it
+ iar $ar0
srri @$ar0, $wr1
srri @$ar0, $wr2
srri @$ar0, $wr3
@@ -210,6 +214,9 @@ send_back:
srri @$ar0, $ac1.m
; Regs are stored. Prepare DMA.
+; $cr must be 0x00ff because the ROM uses lrs and srs with the assumption that
+; they will modify hardware registers.
+ lri $cr, #0x00ff
lri $ax0.l, #0x0000
lri $ax1.l, #1 ;(DSP_CR_IMEM | DSP_CR_TO_CPU)
lri $ax0.h, #0x200
@@ -246,7 +253,8 @@ dma_copy:
lrri $ix1, @$ar0
lrri $ix2, @$ar0
lrri $ix3, @$ar0
- lrri $wr0, @$ar0
+ ; leave $wr for later
+ iar $ar0
lrri $wr1, @$ar0
lrri $wr2, @$ar0
lrri $wr3, @$ar0
@@ -272,6 +280,7 @@ dma_copy:
lrri $ac0.m, @$ar0
lrri $ac1.m, @$ar0
lr $ar0, @REGS_BASE
+ lr $wr0, @(REGS_BASE+8)
lr $sr, @(REGS_BASE+19)
ret ; from send_back
diff --git a/Source/DSPSpy/tests/reg_mask_test.ds b/Source/DSPSpy/tests/reg_mask_test.ds
new file mode 100644
index 0000000000..3bc0e9068b
--- /dev/null
+++ b/Source/DSPSpy/tests/reg_mask_test.ds
@@ -0,0 +1,65 @@
+incdir "tests"
+include "dsp_base.inc"
+
+; Test what happens various values are written to every register
+ LRI $ar0, #0xffff
+ CALL set_all_regs
+ CALL send_back
+
+ LRI $ar0, #0x0000
+ CALL set_all_regs
+ CALL send_back
+
+ LRI $ar0, #0x007f
+ CALL set_all_regs
+ CALL send_back
+
+ LRI $ar0, #0x0080
+ CALL set_all_regs
+ CALL send_back
+
+ LRI $ar0, #0x0100
+ CALL set_all_regs
+ CALL send_back
+
+; We're done, DO NOT DELETE THIS LINE
+ JMP end_of_test
+
+; Copy $ar0 to all other registers
+set_all_regs:
+ SET16
+ MRR $ar1, $ar0
+ MRR $ar2, $ar0
+ MRR $ar3, $ar0
+ MRR $ix0, $ar0
+ MRR $ix1, $ar0
+ MRR $ix2, $ar0
+ MRR $ix3, $ar0
+ MRR $wr0, $ar0
+ MRR $wr1, $ar0
+ MRR $wr2, $ar0
+ MRR $wr3, $ar0
+ ; Don't write to the stacks; returning from this function breaks otherwise
+ ; They don't show up in DSPSpy anyways
+ ;MRR $st0, $ar0
+ ;MRR $st1, $ar0
+ ;MRR $st2, $ar0
+ ;MRR $st3, $ar0
+ MRR $ac0.h, $ar0
+ MRR $ac1.h, $ar0
+ MRR $cr, $ar0
+ ; Wait to set $sr, as it can change the way stores work
+ MRR $prod.l, $ar0
+ MRR $prod.m1, $ar0
+ MRR $prod.h, $ar0
+ MRR $prod.m2, $ar0
+ MRR $ax0.l, $ar0
+ MRR $ax1.l, $ar0
+ MRR $ax0.h, $ar0
+ MRR $ax1.h, $ar0
+ MRR $ac0.l, $ar0
+ MRR $ac1.l, $ar0
+ MRR $ac0.m, $ar0
+ MRR $ac1.m, $ar0
+ MRR $sr, $ar0
+ RET