From 3475ba8918766f5aeda0a6384d2fa238457bfe42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 4 Sep 2017 00:14:22 +0200 Subject: [PATCH 1/2] DSP: Fix missing masking for accelerator registers Based on hardware tests, masking occurs for the accelerator registers. This fixes Red Steel and Far Cry Vengeance, which rely on this behavior when reading back the current playback position from the DSP. --- Source/Core/Core/DSP/DSPAccelerator.cpp | 5 +++-- Source/Core/Core/DSP/DSPHWInterface.cpp | 12 ++++++++++++ Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h | 11 +++++++---- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/DSP/DSPAccelerator.cpp b/Source/Core/Core/DSP/DSPAccelerator.cpp index 3777333719..6940bb0725 100644 --- a/Source/Core/Core/DSP/DSPAccelerator.cpp +++ b/Source/Core/Core/DSP/DSPAccelerator.cpp @@ -9,6 +9,7 @@ #include "Common/MathUtil.h" #include "Core/DSP/DSPCore.h" +#include "Core/DSP/DSPHWInterface.h" #include "Core/DSP/DSPHost.h" namespace DSP @@ -177,8 +178,8 @@ u16 dsp_read_accelerator() DSPCore_SetException(EXP_ACCOV); } - g_dsp.ifx_regs[DSP_ACCAH] = Address >> 16; - g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff; + gdsp_ifx_write(DSP_ACCAH, Address >> 16); + gdsp_ifx_write(DSP_ACCAL, Address & 0xffff); return val; } } // namespace DSP diff --git a/Source/Core/Core/DSP/DSPHWInterface.cpp b/Source/Core/Core/DSP/DSPHWInterface.cpp index fa18c96968..c965233a8e 100644 --- a/Source/Core/Core/DSP/DSPHWInterface.cpp +++ b/Source/Core/Core/DSP/DSPHWInterface.cpp @@ -156,6 +156,18 @@ void gdsp_ifx_write(u32 addr, u32 val) dsp_step_accelerator(); break; */ + + // Masking occurs for the start and end addresses as soon as the registers are written to. + case DSP_ACSAH: + case DSP_ACEAH: + g_dsp.ifx_regs[addr & 0xff] = val & 0x3fff; + break; + + // This also happens for the current address, but with a different mask. + case DSP_ACCAH: + g_dsp.ifx_regs[addr & 0xff] = val & 0xbfff; + break; + default: if ((addr & 0xff) >= 0xa0) { diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h b/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h index b7fca29356..d0a407c342 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h @@ -171,9 +171,12 @@ static bool acc_end_reached; void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr) { acc_pb = pb; - acc_loop_addr = HILO_TO_32(pb->audio_addr.loop_addr); - acc_end_addr = HILO_TO_32(pb->audio_addr.end_addr); + // Masking occurs for the start and end addresses as soon as the registers are written to. + acc_loop_addr = HILO_TO_32(pb->audio_addr.loop_addr) & 0x3fffffff; + acc_end_addr = HILO_TO_32(pb->audio_addr.end_addr) & 0x3fffffff; acc_cur_addr = cur_addr; + // It also happens for the current address, but with a different mask. + *acc_cur_addr &= 0xbfffffff; acc_end_reached = false; } @@ -456,8 +459,8 @@ void GetInputSamples(PB_TYPE& pb, s16* samples, u16 count, const s16* coeffs) pb.src.cur_addr_frac = (curr_pos & 0xFFFF); // Update current position in the PB. - pb.audio_addr.cur_addr_hi = (u16)(cur_addr >> 16); - pb.audio_addr.cur_addr_lo = (u16)(cur_addr & 0xFFFF); + pb.audio_addr.cur_addr_hi = static_cast(cur_addr >> 16) & 0xbfff; + pb.audio_addr.cur_addr_lo = static_cast(cur_addr); } // Add samples to an output buffer, with optional volume ramping. From 09544d748f1edb8ed7f1446f9461d2dbbf57f8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 2 Sep 2017 19:39:58 +0200 Subject: [PATCH 2/2] DSPSpy: Add test for accelerator masking behaviour --- Source/DSPSpy/tests/accelerator_test.ds | 153 ++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 Source/DSPSpy/tests/accelerator_test.ds diff --git a/Source/DSPSpy/tests/accelerator_test.ds b/Source/DSPSpy/tests/accelerator_test.ds new file mode 100644 index 0000000000..ac9cb99b83 --- /dev/null +++ b/Source/DSPSpy/tests/accelerator_test.ds @@ -0,0 +1,153 @@ +; Test for the DSP accelerator, in particular to check its masking behaviour. +; See https://github.com/dolphin-emu/dolphin/pull/5997 +incdir "tests" +include "dsp_base.inc" +jmp test_main + +; Writes the passed format, start and end addresses to the accelerator registers, +; then reads them back to registers. +; The current address is initialised to the start. +; Parameters: +; AC0.H: sample format +; AC0.M/L: start address +; AC1.M/L: end address +test_accelerator_addrs_ex: + ; Set the sample format + sr @0xffd1, $AC0.H + + ; Set the accelerator start and current address. + srs @ACSAH, $AC0.M + srs @ACCAH, $AC0.M + srs @ACSAL, $AC0.L + srs @ACCAL, $AC0.L + ; Set the accelerator end address. + srs @ACEAH, $AC1.M + srs @ACEAL, $AC1.L + + ; Move the values back to registers that can be printed by dspspy. + ; AC0 -> start, AC1 -> end, AX0 -> current + lri $AC0.H, #0 + lrs $AC0.M, @ACSAH + lrs $AC0.L, @ACSAL + lri $AC1.H, #0 + lrs $AC1.M, @ACEAH + lrs $AC1.L, @ACEAL + lrs $AX0.H, @ACCAH + lrs $AX0.L, @ACCAL + + ; Make the accelerator read memory + lrs $AX1.H, @ARAM + lrs $AX1.H, @ARAM + ; AX1 -> new current position after read + lrs $AX1.H, @ACCAH + lrs $AX1.L, @ACCAL + + call send_back + ret + +; Same as test_accelerator_addrs_ex, but with the end address set to start + 0x1000. +test_accelerator_addrs: + lri $AC1.H, #0 + lri $AC1.M, #0 + lri $AC1.L, #0x1000 + add $ACC1, $ACC0 + jmp test_accelerator_addrs_ex + +test_main: + ; Test 1 + lri $AC0.H, #0x19 ; 8-bit PCM + lri $AC0.M, #0x0000 ; start + lri $AC0.L, #0x1000 ; start + call test_accelerator_addrs + + ; Test 2 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x0000 ; start + lri $AC0.L, #0x1000 ; start + call test_accelerator_addrs + + ; Test 3 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x1000 ; start + lri $AC0.L, #0x0000 ; start + call test_accelerator_addrs + + ; Test 4 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x2000 ; start + lri $AC0.L, #0x0000 ; start + call test_accelerator_addrs + + ; Test 5 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x3000 ; start + lri $AC0.L, #0x0000 ; start + call test_accelerator_addrs + + ; Test 6 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x3fff ; start + lri $AC0.L, #0xffff ; start + call test_accelerator_addrs + + ; Test 7 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x4000 ; start + lri $AC0.L, #0x0000 ; start + call test_accelerator_addrs + + ; Test 8 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x5000 ; start + lri $AC0.L, #0x0000 ; start + call test_accelerator_addrs + + ; Test 9 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x6000 ; start + lri $AC0.L, #0x0000 ; start + call test_accelerator_addrs + + ; Test 10 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x7000 ; start + lri $AC0.L, #0x0000 ; start + call test_accelerator_addrs + + ; Test 11 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x7fff ; start + lri $AC0.L, #0xffff ; start + call test_accelerator_addrs + + ; Test 12 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x8000 ; start + lri $AC0.L, #0x0000 ; start + call test_accelerator_addrs + + ; Test 13 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0x9000 ; start + lri $AC0.L, #0x0000 ; start + call test_accelerator_addrs + + ; Test 14 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0xc000 ; start + lri $AC0.L, #0x1000 ; start + call test_accelerator_addrs + + ; Test 15 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0xd000 ; start + lri $AC0.L, #0x1000 ; start + call test_accelerator_addrs + + ; Test 16 + lri $AC0.H, #0xa ; 16-bit PCM + lri $AC0.M, #0xffff ; start + lri $AC0.L, #0xffff ; start + call test_accelerator_addrs + + jmp end_of_test