mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-06-28 01:49:33 -06:00
Merge pull request #10766 from xperia64/dsp_format_fun
DSP accelerator improvements
This commit is contained in:
@ -11,86 +11,152 @@
|
||||
|
||||
namespace DSP
|
||||
{
|
||||
u16 Accelerator::ReadD3()
|
||||
u16 Accelerator::GetCurrentSample()
|
||||
{
|
||||
u16 val = 0;
|
||||
|
||||
switch (m_sample_format)
|
||||
// The lower two bits of the sample format indicate the access size
|
||||
switch (m_sample_format.size)
|
||||
{
|
||||
case 0x5: // u8 reads
|
||||
case FormatSize::Size4Bit:
|
||||
val = ReadMemory(m_current_address >> 1);
|
||||
if (m_current_address & 1)
|
||||
val &= 0xf;
|
||||
else
|
||||
val >>= 4;
|
||||
break;
|
||||
case FormatSize::Size8Bit:
|
||||
val = ReadMemory(m_current_address);
|
||||
m_current_address++;
|
||||
break;
|
||||
case 0x6: // u16 reads
|
||||
case FormatSize::Size16Bit:
|
||||
val = (ReadMemory(m_current_address * 2) << 8) | ReadMemory(m_current_address * 2 + 1);
|
||||
m_current_address++;
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG_FMT(DSPLLE, "dsp_read_aram_d3() - unknown format {:#x}", m_sample_format);
|
||||
default: // produces garbage, but affects the current address
|
||||
ERROR_LOG_FMT(DSPLLE, "GetCurrentSample() - bad format {:#x}", m_sample_format.hex);
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_current_address >= m_end_address)
|
||||
{
|
||||
// Set address back to start address. (never seen this here!)
|
||||
m_current_address = m_start_address;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void Accelerator::WriteD3(u16 value)
|
||||
u16 Accelerator::ReadRaw()
|
||||
{
|
||||
u16 val = GetCurrentSample();
|
||||
if (m_sample_format.size != FormatSize::SizeInvalid)
|
||||
{
|
||||
m_current_address++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_current_address = (m_current_address & ~3) | ((m_current_address + 1) & 3);
|
||||
}
|
||||
|
||||
// There are edge cases that are currently not handled here in u4 and u8 mode
|
||||
// In u8 mode, if ea & 1 == 0 and ca == ea + 1, the accelerator can be read twice. Upon the second
|
||||
// read, the data returned appears to be the other half of the u16 from the first read, and the
|
||||
// DSP resets the current address and throws exception 3
|
||||
|
||||
// During these reads, ca is not incremented normally.
|
||||
|
||||
// Instead, incrementing ca works like this: ca = (ca & ~ 3) | ((ca + 1) & 3)
|
||||
// u4 mode extends this further.
|
||||
|
||||
// When ea & 3 == 0, and ca in [ea + 1, ea + 3], the accelerator can be read (4 - (ca - ea - 1))
|
||||
// times. On the last read, the data returned appears to be the remaining nibble of the u16 from
|
||||
// the first read, and the DSP resets the current address and throws exception 3
|
||||
|
||||
// When ea & 3 == 1, and ca in [ea + 1, ea + 2], the accelerator can be read (4 - (ca - ea - 1))
|
||||
// times. On the last read, the data returned appears to be the remaining nibble of the u16 from
|
||||
// the first read, and the DSP resets the current address and throws exception 3
|
||||
|
||||
// When ea & 3 == 2, and ca == ea + 1, the accelerator can be read 4 times. On the last read, the
|
||||
// data returned appears to be the remaining nibble of the u16 from the first read, and the DSP
|
||||
// resets the current address and throws exception 3
|
||||
|
||||
// There are extra extra edge cases if ca, ea, and potentially other registers are adjusted during
|
||||
// this pre-reset phase
|
||||
|
||||
// The cleanest way to emulate the normal non-edge behavior is to only reset things if we just
|
||||
// read the end address. If the current address is larger than the end address (and not in the
|
||||
// edge range), it ignores the end address
|
||||
if (m_current_address - 1 == m_end_address)
|
||||
{
|
||||
// Set address back to start address (confirmed on hardware)
|
||||
m_current_address = m_start_address;
|
||||
OnRawReadEndException();
|
||||
}
|
||||
|
||||
SetCurrentAddress(m_current_address);
|
||||
return val;
|
||||
}
|
||||
|
||||
void Accelerator::WriteRaw(u16 value)
|
||||
{
|
||||
// Zelda ucode writes a bunch of zeros to ARAM through d3 during
|
||||
// initialization. Don't know if it ever does it later, too.
|
||||
// Pikmin 2 Wii writes non-stop to 0x10008000-0x1000801f (non-zero values too)
|
||||
// Zelda TP Wii writes non-stop to 0x10000000-0x1000001f (non-zero values too)
|
||||
|
||||
switch (m_sample_format)
|
||||
// Writes only seem to be accepted when the upper most bit of the address is set
|
||||
if (m_current_address & 0x80000000)
|
||||
{
|
||||
case 0xA: // u16 writes
|
||||
// The format doesn't matter for raw writes; all writes are u16 and the address is treated as if
|
||||
// we are in a 16-bit format
|
||||
WriteMemory(m_current_address * 2, value >> 8);
|
||||
WriteMemory(m_current_address * 2 + 1, value & 0xFF);
|
||||
m_current_address++;
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG_FMT(DSPLLE, "dsp_write_aram_d3() - unknown format {:#x}", m_sample_format);
|
||||
break;
|
||||
OnRawWriteEndException();
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(DSPLLE, "WriteRaw() - tried to write to address {:#x} without high bit set",
|
||||
m_current_address);
|
||||
}
|
||||
}
|
||||
|
||||
u16 Accelerator::Read(const s16* coefs)
|
||||
u16 Accelerator::ReadSample(const s16* coefs)
|
||||
{
|
||||
if (m_reads_stopped)
|
||||
return 0x0000;
|
||||
|
||||
u16 val;
|
||||
u8 step_size_bytes = 0;
|
||||
if (m_sample_format.unk != 0)
|
||||
{
|
||||
WARN_LOG_FMT(DSPLLE, "ReadSample() format {:#x} has unknown upper bits set",
|
||||
m_sample_format.hex);
|
||||
}
|
||||
|
||||
// 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)
|
||||
u16 val = 0;
|
||||
u8 step_size = 0;
|
||||
s16 raw_sample;
|
||||
if (m_sample_format.decode == FormatDecode::MMIOPCMNoInc ||
|
||||
m_sample_format.decode == FormatDecode::MMIOPCMInc)
|
||||
{
|
||||
case 0x00: // ADPCM audio
|
||||
// The addresses can be complete nonsense in either of these modes
|
||||
raw_sample = m_input;
|
||||
}
|
||||
else
|
||||
{
|
||||
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];
|
||||
|
||||
switch (m_sample_format.decode)
|
||||
{
|
||||
case FormatDecode::ADPCM: // ADPCM audio
|
||||
{
|
||||
// ADPCM really only supports 4-bit decoding, but for larger values on hardware, it just ignores
|
||||
// the upper 12 bits
|
||||
raw_sample &= 0xF;
|
||||
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);
|
||||
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;
|
||||
@ -100,6 +166,7 @@ u16 Accelerator::Read(const s16* coefs)
|
||||
// the ACCOV exception does not fire at all, the predscale register is not updated,
|
||||
// and if the end address is 16-byte aligned, the DSP loops to start_address + 1
|
||||
// instead of start_address.
|
||||
// TODO: This probably needs to be adjusted when using 8 or 16-bit accesses.
|
||||
if ((m_end_address & 0xf) == 0x0 && m_current_address == m_end_address)
|
||||
{
|
||||
m_current_address = m_start_address + 1;
|
||||
@ -113,47 +180,54 @@ u16 Accelerator::Read(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::MMIOPCMNoInc:
|
||||
case FormatDecode::PCM: // 16-bit PCM audio
|
||||
case FormatDecode::MMIOPCMInc:
|
||||
{
|
||||
// Gain seems to only apply for PCM decoding
|
||||
u8 gain_shift = 0;
|
||||
switch (m_sample_format.gain_scale)
|
||||
{
|
||||
case FormatGainScale::GainScale2048:
|
||||
gain_shift = 11; // x / 2048 = x >> 11
|
||||
break;
|
||||
case FormatGainScale::GainScale1:
|
||||
gain_shift = 0; // x / 1 = x >> 0
|
||||
break;
|
||||
case FormatGainScale::GainScale65536:
|
||||
gain_shift = 16; // x / 65536 = x >> 16
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG_FMT(DSPLLE, "ReadSample() invalid gain mode in format {:#x}", m_sample_format.hex);
|
||||
break;
|
||||
}
|
||||
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;
|
||||
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() - unknown format {:#x}", m_sample_format);
|
||||
step_size_bytes = 2;
|
||||
m_current_address += 1;
|
||||
val = 0;
|
||||
step_size = 2;
|
||||
if (m_sample_format.decode != FormatDecode::MMIOPCMNoInc)
|
||||
{
|
||||
m_current_address += 1;
|
||||
}
|
||||
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))
|
||||
// YN1 and YN2 need to be initialized with their "loop" values,
|
||||
// which is usually done upon this exception.
|
||||
if (m_current_address == (m_end_address + step_size - 1))
|
||||
{
|
||||
// Set address back to start address.
|
||||
m_current_address = m_start_address;
|
||||
m_reads_stopped = true;
|
||||
OnEndException();
|
||||
OnSampleReadEndException();
|
||||
}
|
||||
|
||||
SetCurrentAddress(m_current_address);
|
||||
@ -192,7 +266,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)
|
||||
@ -210,4 +289,10 @@ void Accelerator::SetPredScale(u16 pred_scale)
|
||||
{
|
||||
m_pred_scale = pred_scale & 0x7f;
|
||||
}
|
||||
|
||||
void Accelerator::SetInput(u16 input)
|
||||
{
|
||||
m_input = input;
|
||||
}
|
||||
|
||||
} // namespace DSP
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/BitField.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class PointerWrap;
|
||||
@ -14,41 +15,85 @@ class Accelerator
|
||||
public:
|
||||
virtual ~Accelerator() = default;
|
||||
|
||||
u16 Read(const s16* coefs);
|
||||
u16 ReadSample(const s16* coefs);
|
||||
// Zelda ucode reads ARAM through 0xffd3.
|
||||
u16 ReadD3();
|
||||
void WriteD3(u16 value);
|
||||
u16 ReadRaw();
|
||||
void WriteRaw(u16 value);
|
||||
|
||||
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; }
|
||||
u16 GetInput() const { return m_input; }
|
||||
void SetStartAddress(u32 address);
|
||||
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);
|
||||
void SetInput(u16 input);
|
||||
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
protected:
|
||||
virtual void OnEndException() = 0;
|
||||
virtual void OnRawReadEndException() = 0;
|
||||
virtual void OnRawWriteEndException() = 0;
|
||||
virtual void OnSampleReadEndException() = 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, // ADPCM reads from ARAM, ACCA increments
|
||||
MMIOPCMNoInc = 1, // PCM Reads from ACIN, ACCA doesn't increment
|
||||
PCM = 2, // PCM reads from ARAM, ACCA increments
|
||||
MMIOPCMInc = 3 // PCM reads from ACIN, ACCA increments
|
||||
};
|
||||
|
||||
// When reading samples (at least in PCM mode), they are multiplied by the gain, then divided by
|
||||
// the value specified here
|
||||
enum class FormatGainScale : u16
|
||||
{
|
||||
GainScale2048 = 0,
|
||||
GainScale1 = 1,
|
||||
GainScale65536 = 2,
|
||||
GainScaleInvalid = 3
|
||||
};
|
||||
|
||||
union SampleFormat
|
||||
{
|
||||
u16 hex;
|
||||
BitField<0, 2, FormatSize> size;
|
||||
BitField<2, 2, FormatDecode> decode;
|
||||
BitField<4, 2, FormatGainScale> gain_scale;
|
||||
BitField<6, 10, u16> unk;
|
||||
} m_sample_format{0};
|
||||
|
||||
s16 m_gain = 0;
|
||||
s16 m_yn1 = 0;
|
||||
s16 m_yn2 = 0;
|
||||
u16 m_pred_scale = 0;
|
||||
u16 m_input = 0;
|
||||
|
||||
// When an ACCOV is triggered, the accelerator stops reading back anything
|
||||
// and updating the current address register, unless the YN2 register is written to.
|
||||
|
@ -109,7 +109,18 @@ public:
|
||||
protected:
|
||||
u8 ReadMemory(u32 address) override { return Host::ReadHostMemory(address); }
|
||||
void WriteMemory(u32 address, u8 value) override { Host::WriteHostMemory(value, address); }
|
||||
void OnEndException() override { m_dsp.SetException(ExceptionType::AcceleratorOverflow); }
|
||||
void OnRawReadEndException() override
|
||||
{
|
||||
m_dsp.SetException(ExceptionType::AcceleratorRawReadOverflow);
|
||||
}
|
||||
void OnRawWriteEndException() override
|
||||
{
|
||||
m_dsp.SetException(ExceptionType::AcceleratorRawWriteOverflow);
|
||||
}
|
||||
void OnSampleReadEndException() override
|
||||
{
|
||||
m_dsp.SetException(ExceptionType::AcceleratorSampleReadOverflow);
|
||||
}
|
||||
|
||||
private:
|
||||
SDSP& m_dsp;
|
||||
|
@ -152,10 +152,10 @@ enum : u32
|
||||
DSP_DSMAH = 0xce, // DSP DMA Address High (External)
|
||||
DSP_DSMAL = 0xcf, // DSP DMA Address Low (External)
|
||||
|
||||
DSP_FORMAT = 0xd1, // Sample format
|
||||
DSP_ACUNK = 0xd2, // Set to 3 on my dumps
|
||||
DSP_ACDATA1 = 0xd3, // Used only by Zelda ucodes
|
||||
DSP_ACSAH = 0xd4, // Start of loop
|
||||
DSP_FORMAT = 0xd1, // Sample format
|
||||
DSP_ACUNK = 0xd2, // Set to 3 on my dumps
|
||||
DSP_ACDRAW = 0xd3, // Raw accelerator accesses
|
||||
DSP_ACSAH = 0xd4, // Start of loop
|
||||
DSP_ACSAL = 0xd5,
|
||||
DSP_ACEAH = 0xd6, // End of sample (and loop)
|
||||
DSP_ACEAL = 0xd7,
|
||||
@ -164,9 +164,9 @@ enum : u32
|
||||
DSP_PRED_SCALE = 0xda, // ADPCM predictor and scale
|
||||
DSP_YN1 = 0xdb,
|
||||
DSP_YN2 = 0xdc,
|
||||
DSP_ACCELERATOR = 0xdd, // ADPCM accelerator read. Used by AX.
|
||||
DSP_ACDSAMP = 0xdd, // Accelerator sample reads, processed differently depending on FORMAT
|
||||
DSP_GAIN = 0xde,
|
||||
DSP_ACUNK2 = 0xdf, // Set to 0xc on my dumps
|
||||
DSP_ACIN = 0xdf, // Feeds PCM samples written here
|
||||
|
||||
DSP_AMDM = 0xef, // ARAM DMA Request Mask 0: DMA with ARAM unmasked 1: masked
|
||||
|
||||
@ -226,13 +226,13 @@ enum : u16
|
||||
// Exception vectors
|
||||
enum class ExceptionType
|
||||
{
|
||||
StackOverflow = 1, // 0x0002 stack under/over flow
|
||||
EXP_2 = 2, // 0x0004
|
||||
EXP_3 = 3, // 0x0006
|
||||
EXP_4 = 4, // 0x0008
|
||||
AcceleratorOverflow = 5, // 0x000a accelerator address overflow
|
||||
EXP_6 = 6, // 0x000c
|
||||
ExternalInterrupt = 7 // 0x000e external int (message from CPU)
|
||||
StackOverflow = 1, // 0x0002 stack under/over flow
|
||||
EXP_2 = 2, // 0x0004
|
||||
AcceleratorRawReadOverflow = 3, // 0x0006 accelerator raw read address overflow
|
||||
AcceleratorRawWriteOverflow = 4, // 0x0008 accelerator raw write address overflow
|
||||
AcceleratorSampleReadOverflow = 5, // 0x000a accelerator sample reads address overflow
|
||||
EXP_6 = 6, // 0x000c
|
||||
ExternalInterrupt = 7 // 0x000e external int (message from CPU)
|
||||
};
|
||||
|
||||
enum class Mailbox
|
||||
|
@ -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;
|
||||
@ -170,8 +167,11 @@ void SDSP::WriteIFX(u32 address, u16 value)
|
||||
case DSP_PRED_SCALE:
|
||||
m_accelerator->SetPredScale(value);
|
||||
break;
|
||||
case DSP_ACDATA1: // Accelerator write (Zelda type) - "UnkZelda"
|
||||
m_accelerator->WriteD3(value);
|
||||
case DSP_ACDRAW: // Raw accelerator write
|
||||
m_accelerator->WriteRaw(value);
|
||||
break;
|
||||
case DSP_ACIN:
|
||||
m_accelerator->SetInput(value);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -231,16 +231,20 @@ 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:
|
||||
return m_accelerator->GetYn2();
|
||||
case DSP_PRED_SCALE:
|
||||
return m_accelerator->GetPredScale();
|
||||
case DSP_ACCELERATOR: // ADPCM Accelerator reads
|
||||
return m_accelerator->Read(reinterpret_cast<s16*>(&m_ifx_regs[DSP_COEF_A1_0]));
|
||||
case DSP_ACDATA1: // Accelerator reads (Zelda type) - "UnkZelda"
|
||||
return m_accelerator->ReadD3();
|
||||
case DSP_ACDSAMP: // Processed sample accelerator read
|
||||
return m_accelerator->ReadSample(reinterpret_cast<s16*>(&m_ifx_regs[DSP_COEF_A1_0]));
|
||||
case DSP_ACDRAW: // Raw accelerator read
|
||||
return m_accelerator->ReadRaw();
|
||||
case DSP_ACIN:
|
||||
return m_accelerator->GetInput();
|
||||
|
||||
default:
|
||||
{
|
||||
|
@ -400,21 +400,21 @@ const std::array<pdlabel_t, 96> pdlabels =
|
||||
{0xffcf, "DSMAL", "DSP DMA Mem Address L",},
|
||||
|
||||
{0xffd0, "0xffd0",nullptr,},
|
||||
{0xffd1, "SampleFormat", "SampleFormat",},
|
||||
{0xffd1, "FORMAT", "Accelerator sample format",},
|
||||
{0xffd2, "0xffd2",nullptr,},
|
||||
{0xffd3, "UnkZelda", "Unk Zelda reads/writes from/to it",},
|
||||
{0xffd3, "ACDRAW", "Accelerator raw read/write from ARAM",},
|
||||
{0xffd4, "ACSAH", "Accelerator start address H",},
|
||||
{0xffd5, "ACSAL", "Accelerator start address L",},
|
||||
{0xffd6, "ACEAH", "Accelerator end address H",},
|
||||
{0xffd7, "ACEAL", "Accelerator end address L",},
|
||||
{0xffd8, "ACCAH", "Accelerator current address H",},
|
||||
{0xffd9, "ACCAL", "Accelerator current address L",},
|
||||
{0xffda, "pred_scale", "pred_scale",},
|
||||
{0xffdb, "yn1", "yn1",},
|
||||
{0xffdc, "yn2", "yn2",},
|
||||
{0xffdd, "ARAM", "Direct Read from ARAM (uses ADPCM)",},
|
||||
{0xffda, "PRED_SCALE", "ADPCM predictor and scale",},
|
||||
{0xffdb, "YN1", "ADPCM output history Y[N - 1]",},
|
||||
{0xffdc, "YN2", "ADPCM output history Y[N - 2]",},
|
||||
{0xffdd, "ACDSAMP", "Accelerator processed sample read from ARAM or ACIN",},
|
||||
{0xffde, "GAIN", "Gain",},
|
||||
{0xffdf, "0xffdf", nullptr,},
|
||||
{0xffdf, "ACIN", "Accelerator MMIO PCM input value",},
|
||||
|
||||
{0xffe0, "0xffe0",nullptr,},
|
||||
{0xffe1, "0xffe1",nullptr,},
|
||||
|
@ -246,7 +246,7 @@ AESndAccelerator::AESndAccelerator(DSPManager& dsp) : m_dsp(dsp)
|
||||
|
||||
AESndAccelerator::~AESndAccelerator() = default;
|
||||
|
||||
void AESndAccelerator::OnEndException()
|
||||
void AESndAccelerator::OnSampleReadEndException()
|
||||
{
|
||||
// exception5 - this updates internal state
|
||||
SetYn1(GetYn1());
|
||||
@ -266,12 +266,11 @@ void AESndAccelerator::WriteMemory(u32 address, u8 value)
|
||||
|
||||
static constexpr std::array<s16, 16> ACCELERATOR_COEFS = {}; // all zeros
|
||||
|
||||
void AESndUCode::SetUpAccelerator(u16 format, [[maybe_unused]] u16 gain)
|
||||
void AESndUCode::SetUpAccelerator(u16 format, u16 gain)
|
||||
{
|
||||
// setup_accl
|
||||
m_accelerator.SetSampleFormat(format);
|
||||
// not currently implemented, but it doesn't matter since the gain is configured to be a no-op
|
||||
// m_accelerator.SetGain(gain);
|
||||
m_accelerator.SetGain(gain);
|
||||
m_accelerator.SetStartAddress(m_parameter_block.buf_start);
|
||||
m_accelerator.SetEndAddress(m_parameter_block.buf_end);
|
||||
m_accelerator.SetCurrentAddress(m_parameter_block.buf_curr);
|
||||
@ -372,7 +371,7 @@ void AESndUCode::DoMixing()
|
||||
while (counter_h >= 1)
|
||||
{
|
||||
counter_h--;
|
||||
new_r = m_accelerator.Read(ACCELERATOR_COEFS.data());
|
||||
new_r = m_accelerator.ReadSample(ACCELERATOR_COEFS.data());
|
||||
new_l = new_r;
|
||||
}
|
||||
break;
|
||||
@ -383,8 +382,8 @@ void AESndUCode::DoMixing()
|
||||
while (counter_h >= 1)
|
||||
{
|
||||
counter_h--;
|
||||
new_r = m_accelerator.Read(ACCELERATOR_COEFS.data());
|
||||
new_l = m_accelerator.Read(ACCELERATOR_COEFS.data());
|
||||
new_r = m_accelerator.ReadSample(ACCELERATOR_COEFS.data());
|
||||
new_l = m_accelerator.ReadSample(ACCELERATOR_COEFS.data());
|
||||
}
|
||||
break; // falls through to mix_samples normally
|
||||
|
||||
@ -394,7 +393,7 @@ void AESndUCode::DoMixing()
|
||||
while (counter_h >= 1)
|
||||
{
|
||||
counter_h--;
|
||||
new_r = m_accelerator.Read(ACCELERATOR_COEFS.data());
|
||||
new_r = m_accelerator.ReadSample(ACCELERATOR_COEFS.data());
|
||||
new_l = new_r;
|
||||
}
|
||||
new_r ^= 0x8000;
|
||||
@ -407,8 +406,8 @@ void AESndUCode::DoMixing()
|
||||
while (counter_h >= 1)
|
||||
{
|
||||
counter_h--;
|
||||
new_r = m_accelerator.Read(ACCELERATOR_COEFS.data());
|
||||
new_l = m_accelerator.Read(ACCELERATOR_COEFS.data());
|
||||
new_r = m_accelerator.ReadSample(ACCELERATOR_COEFS.data());
|
||||
new_l = m_accelerator.ReadSample(ACCELERATOR_COEFS.data());
|
||||
}
|
||||
new_r ^= 0x8000;
|
||||
new_l ^= 0x8000;
|
||||
|
@ -30,7 +30,9 @@ public:
|
||||
~AESndAccelerator();
|
||||
|
||||
protected:
|
||||
void OnEndException() override;
|
||||
void OnRawReadEndException() override {}
|
||||
void OnRawWriteEndException() override {}
|
||||
void OnSampleReadEndException() override;
|
||||
u8 ReadMemory(u32 address) override;
|
||||
void WriteMemory(u32 address, u8 value) override;
|
||||
|
||||
|
@ -134,7 +134,9 @@ public:
|
||||
PB_TYPE* acc_pb = nullptr;
|
||||
|
||||
protected:
|
||||
void OnEndException() override
|
||||
void OnRawReadEndException() override {}
|
||||
void OnRawWriteEndException() override {}
|
||||
void OnSampleReadEndException() override
|
||||
{
|
||||
if (acc_pb->audio_addr.looping)
|
||||
{
|
||||
@ -181,6 +183,7 @@ void AcceleratorSetup(HLEAccelerator* accelerator, PB_TYPE* pb)
|
||||
accelerator->SetSampleFormat(pb->audio_addr.sample_format);
|
||||
accelerator->SetYn1(pb->adpcm.yn1);
|
||||
accelerator->SetYn2(pb->adpcm.yn2);
|
||||
accelerator->SetGain(pb->adpcm.gain);
|
||||
accelerator->SetPredScale(pb->adpcm.pred_scale);
|
||||
}
|
||||
|
||||
@ -189,7 +192,7 @@ void AcceleratorSetup(HLEAccelerator* accelerator, PB_TYPE* pb)
|
||||
// by the accelerator on real hardware).
|
||||
u16 AcceleratorGetSample(HLEAccelerator* accelerator)
|
||||
{
|
||||
return accelerator->Read(accelerator->acc_pb->adpcm.coefs);
|
||||
return accelerator->ReadSample(accelerator->acc_pb->adpcm.coefs);
|
||||
}
|
||||
|
||||
// Reads samples from the input callback, resamples them to <count> samples at
|
||||
|
@ -9,15 +9,9 @@ lri $AC0.L, #0x0000 ; start
|
||||
lri $AC1.M, #0x0000 ; end
|
||||
lri $AC1.L, #0x0011 ; end
|
||||
|
||||
; Reset some registers
|
||||
lri $AC0.H, #0xffff
|
||||
sr @0xffda, $AC0.H ; pred scale
|
||||
sr @0xffdb, $AC0.H ; yn1
|
||||
sr @0xffdc, $AC0.H ; yn2
|
||||
|
||||
; Set the sample format
|
||||
lri $AC0.H, #0x0
|
||||
sr @0xffd1, $AC0.H
|
||||
sr @FORMAT, $AC0.H
|
||||
; Set the starting and current address
|
||||
srs @ACSAH, $AC0.M
|
||||
srs @ACCAH, $AC0.M
|
||||
@ -27,25 +21,32 @@ srs @ACCAL, $AC0.L
|
||||
srs @ACEAH, $AC1.M
|
||||
srs @ACEAL, $AC1.L
|
||||
|
||||
; Reset some registers (these must be reset after setting FORMAT)
|
||||
lri $AC0.H, #0xffff
|
||||
sr @PRED_SCALE, $AC0.H
|
||||
sr @YN1, $AC0.H
|
||||
sr @YN2, $AC0.H
|
||||
|
||||
call load_hw_reg_to_regs
|
||||
call send_back ; check the accelerator regs before a read
|
||||
|
||||
bloopi #40, end_of_loop
|
||||
lr $IX3, @ARAM
|
||||
lr $IX3, @ACDSAMP
|
||||
call load_hw_reg_to_regs
|
||||
call send_back ; after a read
|
||||
nop ; Loops that end at a return of a call are buggy on hw
|
||||
end_of_loop:
|
||||
nop
|
||||
|
||||
jmp end_of_test
|
||||
|
||||
load_hw_reg_to_regs:
|
||||
lr $AR0, @0xffd1 ; format
|
||||
lr $AR0, @FORMAT
|
||||
lr $AR1, @0xffd2 ; unknown
|
||||
lr $AR2, @0xffda ; pred scale
|
||||
lr $AR3, @0xffdb ; yn1
|
||||
lr $IX0, @0xffdc ; yn2
|
||||
lr $IX1, @0xffdf ; unknown accelerator register
|
||||
lr $AR2, @PRED_SCALE
|
||||
lr $AR3, @YN1
|
||||
lr $IX0, @YN2
|
||||
lr $IX1, @ACIN
|
||||
|
||||
lri $AC0.H, #0
|
||||
lrs $AC0.M, @ACSAH
|
||||
|
79
Source/DSPSpy/tests/accelerator_pcm_test.ds
Normal file
79
Source/DSPSpy/tests/accelerator_pcm_test.ds
Normal file
@ -0,0 +1,79 @@
|
||||
incdir "tests"
|
||||
include "dsp_base.inc"
|
||||
|
||||
test_main:
|
||||
|
||||
; Test parameters
|
||||
lri $AC0.M, #0x0000 ; start
|
||||
lri $AC0.L, #0x0000 ; start
|
||||
lri $AC1.M, #0x0000 ; end
|
||||
lri $AC1.L, #0x0011 ; end
|
||||
|
||||
; Set the sample format
|
||||
lri $AC0.H, #0x08 ; 4-bit PCM, gain scaling = x / 2048
|
||||
sr @FORMAT, $AC0.H
|
||||
; Set the starting and current address
|
||||
srs @ACSAH, $AC0.M
|
||||
srs @ACCAH, $AC0.M
|
||||
srs @ACSAL, $AC0.L
|
||||
srs @ACCAL, $AC0.L
|
||||
; Set the ending address
|
||||
srs @ACEAH, $AC1.M
|
||||
srs @ACEAL, $AC1.L
|
||||
|
||||
; Set the gains
|
||||
si @GAIN, #0x0800 ; 2048 / 2048 = 1.0
|
||||
si @COEF_A1_0, #0x0400 ; 1024 / 2048 = 0.5
|
||||
si @COEF_A2_0, #0x0200 ; 512 / 2048 = 0.25
|
||||
|
||||
; Reset some registers (these must be reset after setting FORMAT)
|
||||
lri $AC0.H, #0x0000
|
||||
sr @PRED_SCALE, $AC0.H ; use 0th coefficients
|
||||
sr @YN1, $AC0.H
|
||||
sr @YN2, $AC0.H
|
||||
|
||||
call load_hw_reg_to_regs
|
||||
call send_back ; check the accelerator regs before a read
|
||||
|
||||
; Expected read sequence
|
||||
; r[0] = (data[0] >> 4) + (0/2) + (0/4)
|
||||
; r[1] = (data[0] & 0xf) + (r[0]/2) + (0/4)
|
||||
; r[2] = (data[1] >> 4) + (r[1]/2) + (r[0]/4)
|
||||
; r[3] = (data[1] & 0xf) + (r[2]/2) + (r[1]/4)
|
||||
; ...
|
||||
|
||||
bloopi #40, end_of_loop
|
||||
lr $IX3, @ACDSAMP
|
||||
call load_hw_reg_to_regs
|
||||
call send_back ; after a read
|
||||
nop ; Loops that end at a return of a call are buggy on hw
|
||||
end_of_loop:
|
||||
nop
|
||||
|
||||
jmp end_of_test
|
||||
|
||||
load_hw_reg_to_regs:
|
||||
lr $AR0, @FORMAT
|
||||
lr $AR1, @0xffd2 ; unknown
|
||||
lr $AR2, @PRED_SCALE
|
||||
lr $AR3, @YN1
|
||||
lr $IX0, @YN2
|
||||
lr $IX1, @ACIN
|
||||
|
||||
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
|
||||
lrs $AX1.H, @ACCAH
|
||||
lrs $AX1.L, @ACCAL
|
||||
|
||||
lrs $AX1.H, @ACCAH
|
||||
lrs $AX1.L, @ACCAL
|
||||
|
||||
ret
|
112
Source/DSPSpy/tests/accelerator_raw_test.ds
Normal file
112
Source/DSPSpy/tests/accelerator_raw_test.ds
Normal file
@ -0,0 +1,112 @@
|
||||
incdir "tests"
|
||||
include "dsp_base.inc"
|
||||
|
||||
; To use: set up a buffer in main_spy,
|
||||
; then modify the start, current, and ending addresses
|
||||
; and verify things look correct
|
||||
loop_read_test:
|
||||
; Set the sample format
|
||||
sr @FORMAT, $AC0.H
|
||||
|
||||
; Test parameters
|
||||
lri $AC0.M, #0x0000 ; start
|
||||
lri $AC0.L, #0x0000 ; start
|
||||
lri $AC1.M, #0x0000 ; end
|
||||
lri $AC1.L, #0x0011 ; end
|
||||
|
||||
; pred scale, coefs, etc do not matter for raw
|
||||
|
||||
; Set the starting and current address
|
||||
srs @ACSAH, $AC0.M
|
||||
srs @ACCAH, $AC0.M
|
||||
srs @ACSAL, $AC0.L
|
||||
srs @ACCAL, $AC0.L
|
||||
; Set the ending address
|
||||
srs @ACEAH, $AC1.M
|
||||
srs @ACEAL, $AC1.L
|
||||
|
||||
call load_hw_reg_to_regs
|
||||
call send_back ; check the accelerator regs before a read
|
||||
|
||||
bloopi #4, end_of_read_loop
|
||||
lr $IX3, @ACDRAW ; Raw reads
|
||||
call load_hw_reg_to_regs
|
||||
call send_back ; after a read
|
||||
nop
|
||||
end_of_read_loop:
|
||||
nop
|
||||
ret
|
||||
|
||||
loop_write_test:
|
||||
; Set the sample format
|
||||
sr @FORMAT, $AC0.H
|
||||
|
||||
; Test parameters
|
||||
lri $AC0.M, #0x0000 ; start
|
||||
lri $AC0.L, #0x0000 ; start
|
||||
lri $AC1.M, #0x0000 ; end
|
||||
lri $AC1.L, #0x0011 ; end
|
||||
|
||||
; pred scale, coefs, etc do not matter for raw
|
||||
|
||||
; Set the starting and current address
|
||||
srs @ACSAH, $AC0.M
|
||||
srs @ACCAH, $AC0.M
|
||||
srs @ACSAL, $AC0.L
|
||||
srs @ACCAL, $AC0.L
|
||||
; Set the ending address
|
||||
srs @ACEAH, $AC1.M
|
||||
srs @ACEAL, $AC1.L
|
||||
|
||||
call load_hw_reg_to_regs
|
||||
call send_back ; check the accelerator regs before a write
|
||||
|
||||
bloopi #4, end_of_write_loop
|
||||
sr @ACDRAW, $IX3 ; Raw writes
|
||||
call load_hw_reg_to_regs
|
||||
call send_back ; after a write
|
||||
nop
|
||||
end_of_write_loop:
|
||||
nop
|
||||
ret
|
||||
|
||||
test_main:
|
||||
lri $AC0.H, #0x00 ; 4-bit
|
||||
call loop_read_test
|
||||
lri $AC0.H, #0x01 ; 8-bit
|
||||
call loop_read_test
|
||||
lri $AC0.H, #0x02 ; 16-bit
|
||||
call loop_read_test
|
||||
lri $AC0.H, #0x00 ; "4-bit", but all writes are 16-bits
|
||||
call loop_write_test
|
||||
lri $AC0.H, #0x01 ; "8-bit", but all writes are 16-bits
|
||||
call loop_write_test
|
||||
lri $AC0.H, #0x02 ; 16-bit
|
||||
call loop_write_test
|
||||
jmp end_of_test
|
||||
|
||||
load_hw_reg_to_regs:
|
||||
lr $AR0, @FORMAT
|
||||
lr $AR1, @0xffd2 ; unknown
|
||||
lr $AR2, @PRED_SCALE
|
||||
lr $AR3, @YN1
|
||||
lr $IX0, @YN2
|
||||
lr $IX1, @ACIN
|
||||
|
||||
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
|
||||
lrs $AX1.H, @ACCAH
|
||||
lrs $AX1.L, @ACCAL
|
||||
|
||||
lrs $AX1.H, @ACCAH
|
||||
lrs $AX1.L, @ACCAL
|
||||
|
||||
ret
|
@ -12,7 +12,7 @@ include "dsp_base.inc"
|
||||
; AC1.M/L: end address
|
||||
test_accelerator_addrs_ex:
|
||||
; Set the sample format
|
||||
sr @0xffd1, $AC0.H
|
||||
sr @FORMAT, $AC0.H
|
||||
|
||||
; Set the accelerator start and current address.
|
||||
srs @ACSAH, $AC0.M
|
||||
@ -35,8 +35,8 @@ test_accelerator_addrs_ex:
|
||||
lrs $AX0.L, @ACCAL
|
||||
|
||||
; Make the accelerator read memory
|
||||
lrs $AX1.H, @ARAM
|
||||
lrs $AX1.H, @ARAM
|
||||
lrs $AX1.H, @ACDSAMP
|
||||
lrs $AX1.H, @ACDSAMP
|
||||
; AX1 -> new current position after read
|
||||
lrs $AX1.H, @ACCAH
|
||||
lrs $AX1.L, @ACCAL
|
||||
|
@ -142,10 +142,10 @@ irq5:
|
||||
si @DMBL, #0x0000
|
||||
si @DIRQ, #0x0001
|
||||
lri $ac0.m, #0xbbbb
|
||||
sr @0xffda, $ac0.m ; pred scale
|
||||
sr @0xffdb, $ac0.m ; yn1
|
||||
lr $ix2, @ARAM
|
||||
sr @0xffdc, $ac0.m ; yn2
|
||||
sr @PRED_SCALE, $ac0.m
|
||||
sr @YN1, $ac0.m
|
||||
lr $ix2, @ACDSAMP
|
||||
sr @YN2, $ac0.m
|
||||
rti
|
||||
irq6:
|
||||
lri $ac0.m, #0x0006
|
||||
|
@ -18,7 +18,7 @@ call send_back
|
||||
|
||||
CLR $ACC0
|
||||
CLRP
|
||||
SET15
|
||||
SET15
|
||||
LRI $AX0.L, #0xFFFF
|
||||
LRI $AX1.H, #0x100
|
||||
MULXMVZ $AX0.L, $AX1.H, $ACC0 ; UNSIGNED
|
||||
@ -37,7 +37,7 @@ call send_back
|
||||
|
||||
CLR $ACC0
|
||||
CLRP
|
||||
SET15
|
||||
SET15
|
||||
LRI $AX0.L, #0xFFFF
|
||||
LRI $AX1.H, #0x100
|
||||
MULXMV $AX0.L, $AX1.H, $ACC0 ; UNSIGNED
|
||||
@ -56,7 +56,7 @@ call send_back
|
||||
|
||||
CLR $ACC0
|
||||
CLRP
|
||||
SET15
|
||||
SET15
|
||||
LRI $AX0.L, #0xFFFF
|
||||
LRI $AX1.H, #0x100
|
||||
MULXAC $AX0.L, $AX1.H, $ACC0 ; UNSIGNED
|
||||
@ -75,7 +75,7 @@ call send_back
|
||||
|
||||
CLR $ACC0
|
||||
CLRP
|
||||
SET15
|
||||
SET15
|
||||
LRI $AX0.L, #0xFFFF
|
||||
LRI $AX1.H, #0x100
|
||||
MULX $AX0.L, $AX1.H ; UNSIGNED
|
||||
@ -95,7 +95,7 @@ call send_back
|
||||
|
||||
CLR $ACC0
|
||||
CLRP
|
||||
SET15
|
||||
SET15
|
||||
LRI $AX0.L, #0xFFFF
|
||||
LRI $AX1.L, #0x100
|
||||
MADDX $AX0.L, $AX1.L ; SIGNED (!)
|
||||
@ -115,7 +115,7 @@ call send_back
|
||||
|
||||
CLR $ACC0
|
||||
CLRP
|
||||
SET15
|
||||
SET15
|
||||
LRI $AC0.M, #0xFFFF
|
||||
LRI $AX0.H, #0x100
|
||||
MULC $AC0.M, $AX0.H ; SIGNED (!)
|
||||
@ -135,7 +135,7 @@ call send_back
|
||||
|
||||
CLR $ACC0
|
||||
CLRP
|
||||
SET15
|
||||
SET15
|
||||
LRI $AC0.M, #0xFFFF
|
||||
LRI $AX0.H, #0x100
|
||||
MULCAC $AC0.M, $AX0.H, $ACC0 ; SIGNED (!)
|
||||
@ -154,7 +154,7 @@ MOVP $ACC0
|
||||
call send_back
|
||||
|
||||
CLR $ACC0
|
||||
SET15
|
||||
SET15
|
||||
LRI $AX0.L, #0xFFFF
|
||||
LRI $AX0.H, #0x100
|
||||
MUL $AX0.L, $AX0.H ; SIGNED (!)
|
||||
@ -173,7 +173,7 @@ MOVP $ACC0
|
||||
call send_back
|
||||
|
||||
CLR $ACC0
|
||||
SET15
|
||||
SET15
|
||||
LRI $AX0.L, #0xFFFF
|
||||
LRI $AX0.H, #0x100
|
||||
MULAC $AX0.L, $AX0.H, $ACC0 ; SIGNED (!)
|
||||
@ -187,65 +187,3 @@ CLR15
|
||||
|
||||
; We're done, DO NOT DELETE THIS LINE
|
||||
jmp end_of_test
|
||||
|
||||
; test accelerator
|
||||
|
||||
; TODO: DSPSpy puts a 16-bit ramp at 0x10000000
|
||||
LRIS $AC1.M, #0x0a ; 16-bit PCM audio
|
||||
;SRS @SampleFormat, $AC1.M
|
||||
; Start accelerator position
|
||||
LRI $AC1.M, #0x0100
|
||||
SRS @ACCAH, $AC1.M
|
||||
LRI $AC1.M, #0x1000
|
||||
SRS @ACCAH, $AC1.M
|
||||
; Current accelerator position
|
||||
LRI $AC1.M, #0x0100
|
||||
SRS @ACCAH, $AC1.M
|
||||
LRI $AC1.M, #0x1000
|
||||
SRS @ACCAH, $AC1.M
|
||||
; End accelerator position
|
||||
LRI $AC1.M, #0x0100
|
||||
SRS @ACCAH, $AC1.M
|
||||
LRI $AC1.M, #0x2000
|
||||
SRS @ACCAH, $AC1.M
|
||||
|
||||
; Now to the interesting parameter - gain.
|
||||
LRI $AC1.M, #0xFFFF
|
||||
SRS @GAIN, $AC1.M
|
||||
|
||||
; Let's now load a sample through the accelerator.
|
||||
LRS $AC1.M, @ARAM
|
||||
call send_back
|
||||
|
||||
jmp end_of_test
|
||||
|
||||
; test addpaxz
|
||||
call send_back
|
||||
|
||||
clrp
|
||||
lri $AX0.L, #0x1111
|
||||
lri $AX0.H, #0x2222
|
||||
call send_back
|
||||
|
||||
clrp
|
||||
addpaxz $ACC0, $AX0.H
|
||||
|
||||
call send_back
|
||||
|
||||
clrp
|
||||
set40
|
||||
addpaxz $ACC0, $AX0.H
|
||||
set16
|
||||
|
||||
call send_back
|
||||
|
||||
clrp
|
||||
set15
|
||||
addpaxz $ACC0, $AX0.H
|
||||
clr15
|
||||
|
||||
call send_back
|
||||
|
||||
|
||||
jmp end_of_test
|
||||
|
||||
|
@ -14,10 +14,10 @@ include "dsp_base_noirq.inc"
|
||||
test_main:
|
||||
; Use the accelerator to generate an IRQ by setting the start and end address to 0
|
||||
; This will result in an interrupt on every read
|
||||
SI @0xffda, #0 ; pred_scale
|
||||
SI @0xffdb, #0 ; yn1
|
||||
SI @0xffdc, #0 ; yn2
|
||||
SI @0xffd1, #0 ; SampleFormat
|
||||
SI @PRED_SCALE, #0
|
||||
SI @YN1, #0
|
||||
SI @YN2, #0
|
||||
SI @FORMAT, #0
|
||||
SI @ACSAH, #0
|
||||
SI @ACCAH, #0
|
||||
SI @ACSAL, #0
|
||||
@ -27,24 +27,24 @@ test_main:
|
||||
|
||||
|
||||
LRI $AX1.H, #0x0000
|
||||
LRS $AX0.L, @ARAM ; Trigger interrupt
|
||||
LRS $AX0.L, @ACDSAMP ; Trigger interrupt
|
||||
CALL send_back
|
||||
|
||||
LRI $AX1.H, #0x0001
|
||||
LRS $AX0.L, @ARAM ; Trigger interrupt
|
||||
LRS $AX0.L, @ACDSAMP ; Trigger interrupt
|
||||
CALL send_back
|
||||
|
||||
LRI $AX1.H, #0x0000
|
||||
LRS $AX0.L, @ARAM ; Trigger interrupt
|
||||
LRS $AX0.L, @ACDSAMP ; Trigger interrupt
|
||||
CALL send_back
|
||||
|
||||
jmp end_of_test
|
||||
|
||||
accov_irq:
|
||||
; Restore registers, otherwise no new interrupt will be generated
|
||||
SI @0xffda, #0 ; pred_scale
|
||||
SI @0xffdb, #0 ; yn1
|
||||
SI @0xffdc, #0 ; yn2
|
||||
SI @PRED_SCALE, #0
|
||||
SI @YN1, #0
|
||||
SI @YN2, #0
|
||||
|
||||
TSTAXH $AX1.H
|
||||
LRI $AX1.L, #0x1111
|
||||
|
@ -17,13 +17,15 @@ public:
|
||||
{
|
||||
std::array<s16, 16> coefs{};
|
||||
m_accov_raised = false;
|
||||
return Read(coefs.data());
|
||||
return ReadSample(coefs.data());
|
||||
}
|
||||
|
||||
bool EndExceptionRaised() const { return m_accov_raised; }
|
||||
|
||||
protected:
|
||||
void OnEndException() override
|
||||
void OnRawReadEndException() override {}
|
||||
void OnRawWriteEndException() override {}
|
||||
void OnSampleReadEndException() override
|
||||
{
|
||||
EXPECT_TRUE(m_reads_stopped);
|
||||
m_accov_raised = true;
|
||||
|
Reference in New Issue
Block a user