pureikyubu/SRC/DSP/DspAccel.cpp
2020-07-21 23:28:29 +03:00

140 lines
3.4 KiB
C++

// DSP ARAM Accelerator
// Accelerator addresses work in accordance with the selected mode (4-bit, 8-bit, 16-bit).
// That is, for example, in 4-bit mode - the start, current and end addresses point to nibble in ARAM.
// On a real system, the next piece of data is cached in 3 16-bit registers ("output ports").
// Here we do not repeat this mechanism, but refer directly to ARAM.
// The accelerator can work both independently, simply driving data between ARAM and DSP, and in conjunction with an ADPCM decoder.
// In the first case, one register is used (ACDAT, read-write), in the second case, another (ACDAT2, read-only).
#include "pch.h"
using namespace Debug;
namespace DSP
{
uint16_t DspCore::AccelFetch()
{
uint16_t val = 0;
uint8_t tempByte = 0;
switch (Accel.Fmt & 3)
{
case 0:
// Refresh pred/scale
if ((Accel.CurrAddress.addr & 0xF) == 0 && ((Accel.Fmt >> 2) & 3) == 0)
{
Accel.AdpcmPds = *(uint8_t*)(aram.mem + (Accel.CurrAddress.addr & 0x07ff'ffff) / 2);
Accel.CurrAddress.addr += 2;
}
// TODO: Check currAddr == endAddr after Pred/Scale update.
tempByte = *(uint8_t*)(aram.mem + (Accel.CurrAddress.addr & 0x07ff'ffff) / 2);
if ((Accel.CurrAddress.addr & 1) == 0)
{
val = tempByte >> 4; // High nibble
}
else
{
val = tempByte & 0xf; // Low nibble
}
break;
case 1:
val = *(uint8_t*)(aram.mem + (Accel.CurrAddress.addr & 0x07ff'ffff));
break;
case 2:
val = _byteswap_ushort(*(uint16_t*)(aram.mem + 2 * (uint64_t)(Accel.CurrAddress.addr & 0x07ff'ffff)));
break;
default:
Halt("DSP: Invalid accelerator mode: 0x%04X\n", Accel.Fmt);
break;
}
Accel.CurrAddress.addr++;
if ((Accel.CurrAddress.addr & 0x07ff'ffff) >= (Accel.EndAddress.addr & 0x07FF'FFFF))
{
Accel.CurrAddress.addr = Accel.StartAddress.addr;
if (logAccel)
{
Report(Channel::DSP, "Accelerator Overflow while read\n");
}
if (regs.sr.ge && regs.sr.acie)
{
Accel.pendingOverflow = true;
Accel.overflowVector = ((Accel.Fmt >> 3) & 3) == 0 ? DspException::ADP_OVF : DspException::ACR_OVF;
}
}
return val;
}
// Read data by accelerator and optionally decode (raw=false)
uint16_t DspCore::AccelReadData(bool raw)
{
uint16_t val = 0;
// Check bit15 of ACCAH
if ((Accel.CurrAddress.h & 0x8000) != 0)
{
// This is #UB
Halt("DSP: Accelerator is not configured to read\n");
}
val = AccelFetch();
// Issue ADPCM Decoder
if (!raw)
{
val = DecodeAdpcm(val);
}
return val;
}
// Write RAW data to ARAM
void DspCore::AccelWriteData(uint16_t data)
{
// Check bit15 of ACCAH
if ((Accel.CurrAddress.h & 0x8000) == 0)
{
// This is #UB
Halt("DSP: Accelerator is not configured to write\n");
}
// Write mode is always 16-bit
*(uint16_t*)(aram.mem + 2 * (uint64_t)(Accel.CurrAddress.addr & 0x07ff'ffff)) = _byteswap_ushort(data);
Accel.CurrAddress.addr++;
if ((Accel.CurrAddress.addr & 0x07ff'ffff) >= (Accel.EndAddress.addr & 0x07FF'FFFF))
{
Accel.CurrAddress.addr = Accel.StartAddress.addr;
Accel.CurrAddress.h |= 0x8000;
if (logAccel)
{
Report(Channel::DSP, "Accelerator Overflow while write\n");
}
if (regs.sr.ge && regs.sr.acie)
{
Accel.pendingOverflow = true;
Accel.overflowVector = DspException::ACW_OVF;
}
}
}
void DspCore::ResetAccel()
{
Accel.pendingOverflow = false;
}
}