Merge pull request #6069 from leoetlino/common-accelerator
DSP: Deduplicate the accelerator code
This commit is contained in:
commit
755253948b
|
@ -14,45 +14,6 @@
|
||||||
|
|
||||||
namespace DSP
|
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()
|
u16 dsp_read_aram_d3()
|
||||||
{
|
{
|
||||||
// Zelda ucode reads ARAM through 0xffd3.
|
// 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;
|
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<void()> 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;
|
u16 val;
|
||||||
u8 step_size_bytes = 0;
|
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
|
// extension and do/do not use ADPCM. It also remains to be figured out
|
||||||
// whether there's a difference between the usual accelerator "read
|
// whether there's a difference between the usual accelerator "read
|
||||||
// address" and 0xd3.
|
// address" and 0xd3.
|
||||||
switch (g_dsp.ifx_regs[DSP_FORMAT])
|
switch (sample_format)
|
||||||
{
|
{
|
||||||
case 0x00: // ADPCM audio
|
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
|
case 0: // Tom and Jerry
|
||||||
step_size_bytes = 1;
|
step_size_bytes = 1;
|
||||||
|
@ -138,26 +107,46 @@ u16 dsp_read_accelerator()
|
||||||
step_size_bytes = 2;
|
step_size_bytes = 2;
|
||||||
break;
|
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<s16>(val, -0x7FFF, 0x7FFF);
|
||||||
|
|
||||||
|
*yn2 = *yn1;
|
||||||
|
*yn1 = val;
|
||||||
|
*current_address += 1;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 0x0A: // 16-bit PCM audio
|
case 0x0A: // 16-bit PCM audio
|
||||||
val = (Host::ReadHostMemory(Address * 2) << 8) | Host::ReadHostMemory(Address * 2 + 1);
|
val = (Host::ReadHostMemory(*current_address * 2) << 8) |
|
||||||
g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
|
Host::ReadHostMemory(*current_address * 2 + 1);
|
||||||
g_dsp.ifx_regs[DSP_YN1] = val;
|
*yn2 = *yn1;
|
||||||
|
*yn1 = val;
|
||||||
step_size_bytes = 2;
|
step_size_bytes = 2;
|
||||||
Address++;
|
*current_address += 1;
|
||||||
break;
|
break;
|
||||||
case 0x19: // 8-bit PCM audio
|
case 0x19: // 8-bit PCM audio
|
||||||
val = Host::ReadHostMemory(Address) << 8;
|
val = Host::ReadHostMemory(*current_address) << 8;
|
||||||
g_dsp.ifx_regs[DSP_YN2] = g_dsp.ifx_regs[DSP_YN1];
|
*yn2 = *yn1;
|
||||||
g_dsp.ifx_regs[DSP_YN1] = val;
|
*yn1 = val;
|
||||||
step_size_bytes = 2;
|
step_size_bytes = 2;
|
||||||
Address++;
|
*current_address += 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERROR_LOG(DSPLLE, "dsp_read_accelerator() - unknown format 0x%x", g_dsp.ifx_regs[DSP_FORMAT]);
|
ERROR_LOG(DSPLLE, "dsp_read_accelerator() - unknown format 0x%x", g_dsp.ifx_regs[DSP_FORMAT]);
|
||||||
step_size_bytes = 2;
|
step_size_bytes = 2;
|
||||||
Address++;
|
*current_address += 1;
|
||||||
val = 0;
|
val = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -171,15 +160,30 @@ u16 dsp_read_accelerator()
|
||||||
// Somehow, YN1 and YN2 must be initialized with their "loop" values,
|
// Somehow, YN1 and YN2 must be initialized with their "loop" values,
|
||||||
// so yeah, it seems likely that we should raise an exception to let
|
// so yeah, it seems likely that we should raise an exception to let
|
||||||
// the DSP program do that, at least if DSP_FORMAT == 0x0A.
|
// 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.
|
// Set address back to start address.
|
||||||
Address = (g_dsp.ifx_regs[DSP_ACSAH] << 16) | g_dsp.ifx_regs[DSP_ACSAL];
|
*current_address = start_address;
|
||||||
DSPCore_SetException(EXP_ACCOV);
|
end_exception();
|
||||||
|
}
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
gdsp_ifx_write(DSP_ACCAH, Address >> 16);
|
u16 dsp_read_accelerator()
|
||||||
gdsp_ifx_write(DSP_ACCAL, Address & 0xffff);
|
{
|
||||||
|
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<s16*>(&g_dsp.ifx_regs[DSP_YN1]),
|
||||||
|
reinterpret_cast<s16*>(&g_dsp.ifx_regs[DSP_YN2]), &g_dsp.ifx_regs[DSP_PRED_SCALE],
|
||||||
|
reinterpret_cast<s16*>(&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;
|
return val;
|
||||||
}
|
}
|
||||||
} // namespace DSP
|
} // namespace DSP
|
||||||
|
|
|
@ -4,10 +4,16 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
namespace DSP
|
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<void()> end_exception);
|
||||||
|
|
||||||
u16 dsp_read_accelerator();
|
u16 dsp_read_accelerator();
|
||||||
|
|
||||||
u16 dsp_read_aram_d3();
|
u16 dsp_read_aram_d3();
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/MathUtil.h"
|
#include "Common/MathUtil.h"
|
||||||
|
#include "Core/DSP/DSPAccelerator.h"
|
||||||
#include "Core/HW/DSP.h"
|
#include "Core/HW/DSP.h"
|
||||||
#include "Core/HW/DSPHLE/UCodes/AX.h"
|
#include "Core/HW/DSPHLE/UCodes/AX.h"
|
||||||
#include "Core/HW/DSPHLE/UCodes/AXStructs.h"
|
#include "Core/HW/DSPHLE/UCodes/AXStructs.h"
|
||||||
|
@ -180,98 +181,22 @@ void AcceleratorSetup(PB_TYPE* pb, u32* cur_addr)
|
||||||
acc_end_reached = false;
|
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
|
// disabling streams that reached the end (this is done by an exception raised
|
||||||
// by the accelerator on real hardware).
|
// by the accelerator on real hardware).
|
||||||
u16 AcceleratorGetSample()
|
u16 AcceleratorGetSample()
|
||||||
{
|
{
|
||||||
u16 ret;
|
|
||||||
u8 step_size_bytes = 0;
|
|
||||||
|
|
||||||
// See below for explanations about acc_end_reached.
|
// See below for explanations about acc_end_reached.
|
||||||
if (acc_end_reached)
|
if (acc_end_reached)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (acc_pb->audio_addr.sample_format)
|
auto end_address_reached = [] {
|
||||||
{
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
// loop back to loop_addr.
|
// loop back to loop_addr.
|
||||||
*acc_cur_addr = acc_loop_addr;
|
*acc_cur_addr = acc_loop_addr;
|
||||||
|
|
||||||
if (acc_pb->audio_addr.looping)
|
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
|
// 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
|
// stream type. This is what the AX UCode does and I don't really
|
||||||
|
@ -304,9 +229,11 @@ u16 AcceleratorGetSample()
|
||||||
acc_end_reached = true;
|
acc_end_reached = true;
|
||||||
#endif
|
#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 <count> samples at
|
// Reads samples from the input callback, resamples them to <count> samples at
|
||||||
|
|
Loading…
Reference in New Issue