diff --git a/Source/Core/Core/DSP/DSPAccelerator.cpp b/Source/Core/Core/DSP/DSPAccelerator.cpp index 6940bb0725..850f210b3f 100644 --- a/Source/Core/Core/DSP/DSPAccelerator.cpp +++ b/Source/Core/Core/DSP/DSPAccelerator.cpp @@ -14,45 +14,6 @@ namespace DSP { -// The hardware adpcm decoder :) -static s16 ADPCM_Step(u32& _rSamplePos) -{ - const s16* pCoefTable = (const s16*)&g_dsp.ifx_regs[DSP_COEF_A1_0]; - - if ((_rSamplePos & 15) == 0) - { - g_dsp.ifx_regs[DSP_PRED_SCALE] = Host::ReadHostMemory((_rSamplePos & ~15) >> 1); - _rSamplePos += 2; - } - - int scale = 1 << (g_dsp.ifx_regs[DSP_PRED_SCALE] & 0xF); - int coef_idx = (g_dsp.ifx_regs[DSP_PRED_SCALE] >> 4) & 0x7; - - s32 coef1 = pCoefTable[coef_idx * 2 + 0]; - s32 coef2 = pCoefTable[coef_idx * 2 + 1]; - - int temp = (_rSamplePos & 1) ? (Host::ReadHostMemory(_rSamplePos >> 1) & 0xF) : - (Host::ReadHostMemory(_rSamplePos >> 1) >> 4); - - if (temp >= 8) - temp -= 16; - - // 0x400 = 0.5 in 11-bit fixed point - int val = - (scale * temp) + - ((0x400 + coef1 * (s16)g_dsp.ifx_regs[DSP_YN1] + coef2 * (s16)g_dsp.ifx_regs[DSP_YN2]) >> 11); - val = MathUtil::Clamp(val, -0x7FFF, 0x7FFF); - - g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1]; - g_dsp.ifx_regs[DSP_YN1] = val; - - _rSamplePos++; - - // The advanced interpolation (linear, polyphase,...) is done by the ucode, - // so we don't need to bother with it here. - return val; -} - u16 dsp_read_aram_d3() { // Zelda ucode reads ARAM through 0xffd3. @@ -110,10 +71,10 @@ void dsp_write_aram_d3(u16 value) g_dsp.ifx_regs[DSP_ACCAL] = Address & 0xffff; } -u16 dsp_read_accelerator() +u16 ReadAccelerator(u32 start_address, u32 end_address, u32* current_address, u16 sample_format, + s16* yn1, s16* yn2, u16* pred_scale, s16* coefs, + std::function end_exception) { - const u32 EndAddress = (g_dsp.ifx_regs[DSP_ACEAH] << 16) | g_dsp.ifx_regs[DSP_ACEAL]; - u32 Address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL]; u16 val; u8 step_size_bytes = 0; @@ -123,10 +84,18 @@ u16 dsp_read_accelerator() // extension and do/do not use ADPCM. It also remains to be figured out // whether there's a difference between the usual accelerator "read // address" and 0xd3. - switch (g_dsp.ifx_regs[DSP_FORMAT]) + switch (sample_format) { case 0x00: // ADPCM audio - switch (EndAddress & 15) + { + // ADPCM decoding, not much to explain here. + if ((*current_address & 15) == 0) + { + *pred_scale = Host::ReadHostMemory((*current_address & ~15) >> 1); + *current_address += 2; + } + + switch (end_address & 15) { case 0: // Tom and Jerry step_size_bytes = 1; @@ -138,26 +107,46 @@ u16 dsp_read_accelerator() step_size_bytes = 2; break; } - val = ADPCM_Step(Address); + + int scale = 1 << (*pred_scale & 0xF); + int coef_idx = (*pred_scale >> 4) & 0x7; + + s32 coef1 = coefs[coef_idx * 2 + 0]; + s32 coef2 = coefs[coef_idx * 2 + 1]; + + int temp = (*current_address & 1) ? (Host::ReadHostMemory(*current_address >> 1) & 0xF) : + (Host::ReadHostMemory(*current_address >> 1) >> 4); + + if (temp >= 8) + temp -= 16; + + val = (scale * temp) + ((0x400 + coef1 * *yn1 + coef2 * *yn2) >> 11); + val = MathUtil::Clamp(val, -0x7FFF, 0x7FFF); + + *yn2 = *yn1; + *yn1 = val; + *current_address += 1; break; + } case 0x0A: // 16-bit PCM audio - val = (Host::ReadHostMemory(Address * 2) << 8) | Host::ReadHostMemory(Address * 2 + 1); - g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1]; - g_dsp.ifx_regs[DSP_YN1] = val; + val = (Host::ReadHostMemory(*current_address * 2) << 8) | + Host::ReadHostMemory(*current_address * 2 + 1); + *yn2 = *yn1; + *yn1 = val; step_size_bytes = 2; - Address++; + *current_address += 1; break; case 0x19: // 8-bit PCM audio - val = Host::ReadHostMemory(Address) << 8; - g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1]; - g_dsp.ifx_regs[DSP_YN1] = val; + val = Host::ReadHostMemory(*current_address) << 8; + *yn2 = *yn1; + *yn1 = val; step_size_bytes = 2; - Address++; + *current_address += 1; break; default: ERROR_LOG(DSPLLE, "dsp_read_accelerator() - unknown format 0x%x", g_dsp.ifx_regs[DSP_FORMAT]); step_size_bytes = 2; - Address++; + *current_address += 1; val = 0; break; } @@ -171,15 +160,30 @@ u16 dsp_read_accelerator() // Somehow, YN1 and YN2 must be initialized with their "loop" values, // so yeah, it seems likely that we should raise an exception to let // the DSP program do that, at least if DSP_FORMAT == 0x0A. - if (Address == (EndAddress + step_size_bytes - 1)) + if (*current_address == (end_address + step_size_bytes - 1)) { // Set address back to start address. - Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL]; - DSPCore_SetException(EXP_ACCOV); + *current_address = start_address; + end_exception(); } + return val; +} - gdsp_ifx_write(DSP_ACCAH, Address >> 16); - gdsp_ifx_write(DSP_ACCAL, Address & 0xffff); +u16 dsp_read_accelerator() +{ + const u32 start_address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL]; + const u32 end_address = (g_dsp.ifx_regs[DSP_ACEAH] << 16) | g_dsp.ifx_regs[DSP_ACEAL]; + u32 current_address = (g_dsp.ifx_regs[DSP_ACCAH] << 16) | g_dsp.ifx_regs[DSP_ACCAL]; + + auto end_address_reached = [] { DSPCore_SetException(EXP_ACCOV); }; + const u16 val = ReadAccelerator( + start_address, end_address, ¤t_address, g_dsp.ifx_regs[DSP_FORMAT], + reinterpret_cast(&g_dsp.ifx_regs[DSP_YN1]), + reinterpret_cast(&g_dsp.ifx_regs[DSP_YN2]), &g_dsp.ifx_regs[DSP_PRED_SCALE], + reinterpret_cast(&g_dsp.ifx_regs[DSP_COEF_A1_0]), end_address_reached); + + gdsp_ifx_write(DSP_ACCAH, current_address >> 16); + gdsp_ifx_write(DSP_ACCAL, current_address & 0xffff); return val; } } // namespace DSP diff --git a/Source/Core/Core/DSP/DSPAccelerator.h b/Source/Core/Core/DSP/DSPAccelerator.h index 01a74f26e4..6cb7428b88 100644 --- a/Source/Core/Core/DSP/DSPAccelerator.h +++ b/Source/Core/Core/DSP/DSPAccelerator.h @@ -4,10 +4,16 @@ #pragma once +#include + #include "Common/CommonTypes.h" namespace DSP { +u16 ReadAccelerator(u32 start_address, u32 end_address, u32* current_address, u16 sample_format, + s16* yn1, s16* yn2, u16* pred_scale, s16* coefs, + std::function end_exception); + u16 dsp_read_accelerator(); u16 dsp_read_aram_d3(); diff --git a/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h b/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h index d0a407c342..059ae46ee9 100644 --- a/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h +++ b/Source/Core/Core/HW/DSPHLE/UCodes/AXVoice.h @@ -16,6 +16,7 @@ #include "Common/CommonTypes.h" #include "Common/MathUtil.h" +#include "Core/DSP/DSPAccelerator.h" #include "Core/HW/DSP.h" #include "Core/HW/DSPHLE/UCodes/AX.h" #include "Core/HW/DSPHLE/UCodes/AXStructs.h" @@ -180,98 +181,22 @@ void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr) acc_end_reached = false; } -// Reads a sample from the simulated accelerator. Also handles looping and +// Reads a sample from the accelerator. Also handles looping and // disabling streams that reached the end (this is done by an exception raised // by the accelerator on real hardware). u16 AcceleratorGetSample() { - u16 ret; - u8 step_size_bytes = 0; - // See below for explanations about acc_end_reached. if (acc_end_reached) return 0; - switch (acc_pb->audio_addr.sample_format) - { - case 0x00: // ADPCM - { - // ADPCM decoding, not much to explain here. - if ((*acc_cur_addr & 15) == 0) - { - acc_pb->adpcm.pred_scale = DSP::ReadARAM((*acc_cur_addr & ~15) >> 1); - *acc_cur_addr += 2; - } - - switch (acc_end_addr & 15) - { - case 0: // Tom and Jerry - step_size_bytes = 1; - break; - case 1: // Blazing Angels - step_size_bytes = 0; - break; - default: - step_size_bytes = 2; - break; - } - - int scale = 1 << (acc_pb->adpcm.pred_scale & 0xF); - int coef_idx = (acc_pb->adpcm.pred_scale >> 4) & 0x7; - - s32 coef1 = acc_pb->adpcm.coefs[coef_idx * 2 + 0]; - s32 coef2 = acc_pb->adpcm.coefs[coef_idx * 2 + 1]; - - int temp = (*acc_cur_addr & 1) ? (DSP::ReadARAM(*acc_cur_addr >> 1) & 0xF) : - (DSP::ReadARAM(*acc_cur_addr >> 1) >> 4); - - if (temp >= 8) - temp -= 16; - - int val = - (scale * temp) + ((0x400 + coef1 * acc_pb->adpcm.yn1 + coef2 * acc_pb->adpcm.yn2) >> 11); - val = MathUtil::Clamp(val, -0x7FFF, 0x7FFF); - - acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; - acc_pb->adpcm.yn1 = val; - *acc_cur_addr += 1; - ret = val; - break; - } - - case 0x0A: // 16-bit PCM audio - ret = (DSP::ReadARAM(*acc_cur_addr * 2) << 8) | DSP::ReadARAM(*acc_cur_addr * 2 + 1); - acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; - acc_pb->adpcm.yn1 = ret; - step_size_bytes = 2; - *acc_cur_addr += 1; - break; - - case 0x19: // 8-bit PCM audio - ret = DSP::ReadARAM(*acc_cur_addr) << 8; - acc_pb->adpcm.yn2 = acc_pb->adpcm.yn1; - acc_pb->adpcm.yn1 = ret; - step_size_bytes = 2; - *acc_cur_addr += 1; - break; - - default: - ERROR_LOG(DSPHLE, "Unknown sample format: %d", acc_pb->audio_addr.sample_format); - return 0; - } - - // Have we reached the end address? - // - // On real hardware, this would raise an interrupt that is handled by the - // UCode. We simulate what this interrupt does here. - if (*acc_cur_addr == (acc_end_addr + step_size_bytes - 1)) - { + auto end_address_reached = [] { // loop back to loop_addr. *acc_cur_addr = acc_loop_addr; if (acc_pb->audio_addr.looping) { - // Set the ADPCM infos to continue processing at loop_addr. + // Set the ADPCM info to continue processing at loop_addr. // // For some reason, yn1 and yn2 aren't set if the voice is not of // stream type. This is what the AX UCode does and I don't really @@ -304,9 +229,11 @@ u16 AcceleratorGetSample() acc_end_reached = true; #endif } - } + }; - return ret; + return ReadAccelerator(acc_loop_addr, acc_end_addr, acc_cur_addr, + acc_pb->audio_addr.sample_format, &acc_pb->adpcm.yn1, &acc_pb->adpcm.yn2, + &acc_pb->adpcm.pred_scale, acc_pb->adpcm.coefs, end_address_reached); } // Reads samples from the input callback, resamples them to samples at