Refactor accelerator format as a bitfield, use gain/yn1/yn2 in PCM mode

This commit is contained in:
xperia64 2022-06-18 18:38:31 -04:00 committed by Tillmann Karras
parent d01636f94a
commit 3544596f64
3 changed files with 140 additions and 66 deletions

View file

@ -11,33 +11,42 @@
namespace DSP
{
u16 Accelerator::ReadRaw()
u16 Accelerator::GetCurrentSample()
{
u16 val = 0;
// The lower two bits of the sample format indicate the access size
switch (m_sample_format & 3)
switch (m_sample_format.size)
{
case 0x0: // u4 reads
val = ReadMemory(m_current_address / 2);
case FormatSize::Size4Bit:
val = ReadMemory(m_current_address >> 1);
if (m_current_address & 1)
val &= 0xf;
else
val >>= 4;
m_current_address++;
break;
case 0x1: // u8 reads
case FormatSize::Size8Bit:
val = ReadMemory(m_current_address);
m_current_address++;
break;
case 0x2: // u16 reads
case FormatSize::Size16Bit:
val = (ReadMemory(m_current_address * 2) << 8) | ReadMemory(m_current_address * 2 + 1);
break;
default: // produces garbage, but affects the current address
ERROR_LOG_FMT(DSPLLE, "dsp_get_current_sample() - bad format {:#x}", m_sample_format.hex);
break;
}
return val;
}
u16 Accelerator::ReadRaw()
{
u16 val = GetCurrentSample();
if (m_sample_format.size != FormatSize::SizeInvalid)
{
m_current_address++;
break;
case 0x3: // produces garbage, but affects the current address
ERROR_LOG_FMT(DSPLLE, "dsp_read_aram_raw() - bad format {:#x}", m_sample_format);
}
else
{
m_current_address = (m_current_address & ~3) | ((m_current_address + 1) & 3);
break;
}
// There are edge cases that are currently not handled here in u4 and u8 mode
@ -108,34 +117,69 @@ u16 Accelerator::ReadSample(const s16* coefs)
if (m_reads_stopped)
return 0x0000;
u16 val;
u8 step_size_bytes = 0;
if (m_sample_format.raw_only)
{
// Seems to return garbage on hardware
ERROR_LOG_FMT(
DSPLLE,
"dsp_read_accelerator_sample() tried sample read with raw only bit set for format {:#x}",
m_sample_format.hex);
return 0x0000;
}
// let's do the "hardware" decode DSP_FORMAT is interesting - the Zelda
// ucode seems to indicate that the bottom two bits specify the "read size"
// and the address multiplier. The bits above that may be things like sign
// 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 (m_sample_format)
if (m_sample_format.unk != 0)
{
case 0x00: // ADPCM audio
WARN_LOG_FMT(DSPLLE, "dsp_read_accelerator_sample() format {:#x} has unknown upper bits set",
m_sample_format.hex);
}
u16 val = 0;
u8 step_size = 0;
s16 raw_sample = GetCurrentSample();
int coef_idx = (m_pred_scale >> 4) & 0x7;
s32 coef1 = coefs[coef_idx * 2 + 0];
s32 coef2 = coefs[coef_idx * 2 + 1];
u8 gain_shift = 0;
switch (m_sample_format.gain_cfg)
{
case FormatGainCfg::GainShift11:
gain_shift = 11;
break;
case FormatGainCfg::GainShift0:
gain_shift = 0;
break;
case FormatGainCfg::GainShift16:
gain_shift = 16;
break;
default:
ERROR_LOG_FMT(DSPLLE, "dsp_read_accelerator_sample() invalid gain mode in format {:#x}",
m_sample_format.hex);
break;
}
switch (m_sample_format.decode)
{
case FormatDecode::ADPCM: // ADPCM audio
{
if (m_sample_format.size != FormatSize::Size4Bit)
{
ERROR_LOG_FMT(
DSPLLE, "dsp_read_accelerator_sample() tried to read ADPCM with bad size in format {:#x}",
m_sample_format.hex);
break;
}
int scale = 1 << (m_pred_scale & 0xF);
int coef_idx = (m_pred_scale >> 4) & 0x7;
s32 coef1 = coefs[coef_idx * 2 + 0];
s32 coef2 = coefs[coef_idx * 2 + 1];
if (raw_sample >= 8)
raw_sample -= 16;
int temp = (m_current_address & 1) ? (ReadMemory(m_current_address >> 1) & 0xF) :
(ReadMemory(m_current_address >> 1) >> 4);
if (temp >= 8)
temp -= 16;
s32 val32 = (scale * temp) + ((0x400 + coef1 * m_yn1 + coef2 * m_yn2) >> 11);
// Not sure if GAIN is applied for ADPCM or not
s32 val32 = (scale * raw_sample) + ((0x400 + coef1 * m_yn1 + coef2 * m_yn2) >> 11);
val = static_cast<s16>(std::clamp<s32>(val32, -0x7FFF, 0x7FFF));
step_size_bytes = 2;
step_size = 2;
m_yn2 = m_yn1;
m_yn1 = val;
@ -158,42 +202,28 @@ u16 Accelerator::ReadSample(const s16* coefs)
{
m_pred_scale = ReadMemory((m_current_address & ~15) >> 1);
m_current_address += 2;
step_size_bytes += 2;
step_size += 2;
}
break;
}
case 0x0A: // 16-bit PCM audio
val = (ReadMemory(m_current_address * 2) << 8) | ReadMemory(m_current_address * 2 + 1);
case FormatDecode::PCM: // 16-bit PCM audio
{
s32 val32 = ((static_cast<s32>(m_gain) * raw_sample) >> gain_shift) +
(((coef1 * m_yn1) >> gain_shift) + ((coef2 * m_yn2) >> gain_shift));
val = static_cast<s16>(val32);
m_yn2 = m_yn1;
m_yn1 = val;
step_size_bytes = 2;
step_size = 2;
m_current_address += 1;
break;
case 0x19: // 8-bit PCM audio
val = ReadMemory(m_current_address) << 8;
m_yn2 = m_yn1;
m_yn1 = val;
step_size_bytes = 2;
m_current_address += 1;
break;
default:
ERROR_LOG_FMT(DSPLLE, "dsp_read_accelerator_sample() - unknown format {:#x}", m_sample_format);
step_size_bytes = 2;
m_current_address += 1;
val = 0;
break;
}
// TODO: Take GAIN into account
// adpcm = 0, pcm8 = 0x100, pcm16 = 0x800
// games using pcm8 : Phoenix Wright Ace Attorney (WiiWare), Megaman 9-10 (WiiWare)
// games using pcm16: GC Sega games, ...
}
// Check for loop.
// 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 (m_current_address == (m_end_address + step_size_bytes - 1))
if (m_current_address == (m_end_address + step_size - 1))
{
// Set address back to start address.
m_current_address = m_start_address;
@ -237,7 +267,12 @@ void Accelerator::SetCurrentAddress(u32 address)
void Accelerator::SetSampleFormat(u16 format)
{
m_sample_format = format;
m_sample_format.hex = format;
}
void Accelerator::SetGain(s16 gain)
{
m_gain = gain;
}
void Accelerator::SetYn1(s16 yn1)

View file

@ -3,6 +3,7 @@
#pragma once
#include "Common/BitField.h"
#include "Common/CommonTypes.h"
class PointerWrap;
@ -22,7 +23,8 @@ public:
u32 GetStartAddress() const { return m_start_address; }
u32 GetEndAddress() const { return m_end_address; }
u32 GetCurrentAddress() const { return m_current_address; }
u16 GetSampleFormat() const { return m_sample_format; }
u16 GetSampleFormat() const { return m_sample_format.hex; }
s16 GetGain() const { return m_gain; }
s16 GetYn1() const { return m_yn1; }
s16 GetYn2() const { return m_yn2; }
u16 GetPredScale() const { return m_pred_scale; }
@ -30,6 +32,7 @@ public:
void SetEndAddress(u32 address);
void SetCurrentAddress(u32 address);
void SetSampleFormat(u16 format);
void SetGain(s16 gain);
void SetYn1(s16 yn1);
void SetYn2(s16 yn2);
void SetPredScale(u16 pred_scale);
@ -40,12 +43,49 @@ protected:
virtual void OnEndException() = 0;
virtual u8 ReadMemory(u32 address) = 0;
virtual void WriteMemory(u32 address, u8 value) = 0;
u16 GetCurrentSample();
// DSP accelerator registers.
u32 m_start_address = 0;
u32 m_end_address = 0;
u32 m_current_address = 0;
u16 m_sample_format = 0;
enum class FormatSize : u16
{
Size4Bit = 0,
Size8Bit = 1,
Size16Bit = 2,
SizeInvalid = 3
};
enum class FormatDecode : u16
{
ADPCM = 0,
PCM = 1,
};
// When reading samples (at least in PCM mode), they are multiplied by the gain, then shifted
// right by an amount dependent on this config
enum class FormatGainCfg : u16
{
GainShift11 = 0,
GainShift0 = 1,
GainShift16 = 2,
GainInvalid = 3
};
union SampleFormat
{
u16 hex;
BitField<0, 2, FormatSize> size;
BitField<2, 1, bool, u16>
raw_only; // When this bit is set, sample reads seem broken, while raw accesses work
BitField<3, 1, FormatDecode> decode;
BitField<4, 2, FormatGainCfg> gain_cfg;
BitField<6, 10, u16> unk;
} m_sample_format;
s16 m_gain = 0;
s16 m_yn1 = 0;
s16 m_yn2 = 0;
u16 m_pred_scale = 0;

View file

@ -122,12 +122,6 @@ void SDSP::WriteIFX(u32 address, u16 value)
m_ifx_regs[DSP_DSBL] = 0;
break;
case DSP_GAIN:
if (value != 0)
{
DEBUG_LOG_FMT(DSPLLE, "Gain Written: {:#06x}", value);
}
[[fallthrough]];
case DSP_DSPA:
case DSP_DSMAH:
case DSP_DSMAL:
@ -161,6 +155,9 @@ void SDSP::WriteIFX(u32 address, u16 value)
case DSP_FORMAT:
m_accelerator->SetSampleFormat(value);
break;
case DSP_GAIN:
m_accelerator->SetGain(value);
break;
case DSP_YN1:
m_accelerator->SetYn1(value);
break;
@ -231,6 +228,8 @@ u16 SDSP::ReadIFXImpl(u16 address)
return static_cast<u16>(m_accelerator->GetCurrentAddress());
case DSP_FORMAT:
return m_accelerator->GetSampleFormat();
case DSP_GAIN:
return m_accelerator->GetGain();
case DSP_YN1:
return m_accelerator->GetYn1();
case DSP_YN2: