SPU2: Improve DMA/IRQ timing.

Tighten SPU2 sync with IOP
This commit is contained in:
refractionpcsx2 2020-12-29 10:30:33 +00:00
parent b7f5404062
commit f07ca859e5
6 changed files with 119 additions and 69 deletions

View File

@ -146,7 +146,7 @@ void psxRcntInit()
psxCounters[4].interrupt = 0x08000;
psxCounters[5].interrupt = 0x10000;
psxCounters[6].rate = 768 * 12;
psxCounters[6].rate = 768; // One SPU tick
psxCounters[6].CycleT = psxCounters[6].rate;
psxCounters[6].mode = 0x8;

View File

@ -43,7 +43,7 @@ static void __fastcall psxDmaGeneric(u32 madr, u32 bcr, u32 chcr, u32 spuCore)
//Console.Status("cycles sent to SPU2 %x\n", psxRegs.cycle - psxCounters[6].sCycleT);
psxCounters[6].sCycleT = psxRegs.cycle;
psxCounters[6].CycleT = size * 3;
psxCounters[6].CycleT = size * 2;
psxNextCounter -= (psxRegs.cycle - psxNextsCounter);
psxNextsCounter = psxRegs.cycle;

View File

@ -146,6 +146,7 @@ void V_Core::StartADMAWrite(u16* pMem, u32 sz)
{
TSA = 0x2000 + (Index << 10);
DMAICounter = size;
LastClock = lClocks;
}
else if (size >= 512)
{
@ -169,13 +170,17 @@ void V_Core::StartADMAWrite(u16* pMem, u32 sz)
#endif
// Klonoa 2
if (size == 512)
{
DMAICounter = size;
LastClock = lClocks;
}
}
AdmaInProgress = 1;
}
else
{
LastClock = lClocks;
InputDataLeft = 0;
DMAICounter = 1;
}
@ -271,7 +276,7 @@ void V_Core::PlainDMAWrite(u16* pMem, u32 size)
// 0x2800? Hard to know for sure (almost no games depend on this)
memcpy(GetMemPtr(0), &pMem[buff1size], buff2end * 2);
TDA = (buff2end + 1) & 0xfffff;
TDA = (buff2end) & 0xfffff;
// Flag interrupt? If IRQA occurs between start and dest, flag it.
// Important: Test both core IRQ settings for either DMA!
@ -292,7 +297,7 @@ void V_Core::PlainDMAWrite(u16* pMem, u32 size)
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA || Cores[i].IRQA <= TDA))
{
//ConLog("DMAwrite Core %d: IRQ Called (IRQ passed). IRQA = %x Cycles = %d\n", i, Cores[i].IRQA, Cycles );
SetIrqCall(i);
SetIrqCallDMA(i);
}
}
#else
@ -307,7 +312,7 @@ void V_Core::PlainDMAWrite(u16* pMem, u32 size)
// Buffer doesn't wrap/overflow!
// Just set the TDA and check for an IRQ...
TDA = (buff1end + 1) & 0xfffff;
TDA = (buff1end) & 0xfffff;
// Flag interrupt? If IRQA occurs between start and dest, flag it.
// Important: Test both core IRQ settings for either DMA!
@ -318,7 +323,7 @@ void V_Core::PlainDMAWrite(u16* pMem, u32 size)
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA && Cores[i].IRQA <= TDA))
{
//ConLog("DMAwrite Core %d: IRQ Called (IRQ passed). IRQA = %x Cycles = %d\n", i, Cores[i].IRQA, Cycles );
SetIrqCall(i);
SetIrqCallDMA(i);
}
}
#else
@ -328,7 +333,7 @@ void V_Core::PlainDMAWrite(u16* pMem, u32 size)
}
#endif
}
LastClock = lClocks;
TSA = TDA;
DMAICounter = size;
TADR = MADR + (size << 1);
@ -369,7 +374,7 @@ void V_Core::FinishDMAread()
{
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA || Cores[i].IRQA <= TDA))
{
SetIrqCall(i);
SetIrqCallDMA(i);
}
}
}
@ -387,7 +392,7 @@ void V_Core::FinishDMAread()
{
if (Cores[i].IRQEnable && (Cores[i].IRQA > TSA && Cores[i].IRQA <= TDA))
{
SetIrqCall(i);
SetIrqCallDMA(i);
}
}
}
@ -402,9 +407,10 @@ void V_Core::DoDMAread(u16* pMem, u32 size)
DMARPtr = pMem;
ReadSize = size;
IsDMARead = true;
LastClock = lClocks;
DMAICounter = size;
Regs.STATX &= ~0x80;
Regs.STATX |= 0x400;
//Regs.ATTR |= 0x30;
TADR = MADR + (size << 1);
}
@ -418,7 +424,7 @@ void V_Core::DoDMAwrite(u16* pMem, u32 size)
Regs.STATX &= ~0x80;
//Regs.ATTR |= 0x30;
DMAICounter = 1;
LastClock = lClocks;
return;
}
@ -449,11 +455,12 @@ void V_Core::DoDMAwrite(u16* pMem, u32 size)
{
if (MsgDMA())
ConLog("* SPU2: DMA%c Transfer of %d bytes to %x (%02x %x %04x). IRQE = %d IRQA = %x \n",
GetDmaIndexChar(), size << 1, TSA, DMABits, AutoDMACtrl, (~Regs.ATTR) & 0x7fff,
Cores[0].IRQEnable, Cores[0].IRQA);
GetDmaIndexChar(), size << 1, TSA, DMABits, AutoDMACtrl, Regs.ATTR & 0x7fff,
Cores[Index].IRQEnable, Cores[Index].IRQA);
PlainDMAWrite(pMem, size);
}
Regs.STATX &= ~0x80;
Regs.STATX |= 0x400;
//Regs.ATTR |= 0x30;
}

View File

@ -401,6 +401,7 @@ struct V_Core
s8 NoiseClk; // Noise Clock
u16 AutoDMACtrl; // AutoDMA Status
s32 DMAICounter; // DMA Interrupt Counter
u32 LastClock; // DMA Interrupt Clock Cycle Counter
u32 InputDataLeft; // Input Buffer
u32 InputPosRead;
u32 InputPosWrite;
@ -549,6 +550,7 @@ extern s16* _spu2mem;
extern int PlayMode;
extern void SetIrqCall(int core);
extern void SetIrqCallDMA(int core);
extern void StartVoices(int core, u32 value);
extern void StopVoices(int core, u32 value);
extern void InitADSR();

View File

@ -133,14 +133,16 @@ void SPU2interruptDMA4()
{
FileLog("[%10d] SPU2 interruptDMA4\n", Cycles);
Cores[0].Regs.STATX |= 0x80;
//Cores[0].Regs.ATTR &= ~0x30;
Cores[0].Regs.STATX &= ~0x400;
Cores[0].Regs.ATTR &= ~0x30;
}
void SPU2interruptDMA7()
{
FileLog("[%10d] SPU2 interruptDMA7\n", Cycles);
Cores[1].Regs.STATX |= 0x80;
//Cores[1].Regs.ATTR &= ~0x30;
Cores[1].Regs.STATX &= ~0x400;
Cores[1].Regs.ATTR &= ~0x30;
}
void SPU2readDMA7Mem(u16* pMem, u32 size)

View File

@ -40,7 +40,8 @@ u32 Cycles;
int PlayMode;
bool has_to_call_irq = false;
bool has_to_call_irq[2] = { false, false };
bool has_to_call_irq_dma[2] = { false, false };
bool psxmode = false;
@ -48,10 +49,14 @@ void SetIrqCall(int core)
{
// reset by an irq disable/enable cycle, behaviour found by
// test programs that bizarrely only fired one interrupt
if (Spdif.Info & 4 << core)
return;
Spdif.Info |= 4 << core;
has_to_call_irq = true;
has_to_call_irq[core] = true;
}
void SetIrqCallDMA(int core)
{
// reset by an irq disable/enable cycle, behaviour found by
// test programs that bizarrely only fired one interrupt
has_to_call_irq_dma[core] = true;
}
__forceinline s16* GetMemPtr(u32 addr)
@ -340,6 +345,7 @@ bool V_Voice::Start()
ADSR.Phase = 1;
SCurrent = 28;
LoopMode = 0;
SP = 0;
LoopFlags = 0;
NextA = StartA | 1;
Prev1 = 0;
@ -395,60 +401,93 @@ __forceinline void TimeUpdate(u32 cClocks)
else
TickInterval = 768; // Reset to default, in case the user hotswitched from async to something else.
//Update DMA4 interrupt delay counter
if (Cores[0].DMAICounter > 0 && (cClocks - Cores[0].LastClock) > 0)
{
const u32 amt = std::min(cClocks - Cores[0].LastClock, (u32)Cores[0].DMAICounter);
Cores[0].DMAICounter -= amt;
Cores[0].LastClock = cClocks;
Cores[0].MADR += amt * 2;
if (Cores[0].DMAICounter <= 0)
{
if (Cores[0].IsDMARead)
Cores[0].FinishDMAread();
for (int i = 0; i < 2; i++)
{
if (has_to_call_irq_dma[i])
{
//ConLog("* SPU2: Irq Called (%04x) at cycle %d.\n", Spdif.Info, Cycles);
has_to_call_irq_dma[i] = false;
if (!(Spdif.Info & (4 << i)))
{
Spdif.Info |= (4 << i);
if (!SPU2_dummy_callback)
spu2Irq();
}
}
}
//ConLog("counter set and callback!\n");
Cores[0].DMAICounter = 0;
if (!SPU2_dummy_callback)
spu2DMA4Irq();
else
SPU2interruptDMA4();
}
}
//Update DMA7 interrupt delay counter
if (Cores[1].DMAICounter > 0 && (cClocks - Cores[1].LastClock) > 0)
{
const u32 amt = std::min(cClocks - Cores[1].LastClock, (u32)Cores[1].DMAICounter);
Cores[1].DMAICounter -= amt;
Cores[1].LastClock = cClocks;
Cores[1].MADR += amt * 2;
if (Cores[1].DMAICounter <= 0)
{
if (Cores[1].IsDMARead)
Cores[1].FinishDMAread();
for (int i = 0; i < 2; i++)
{
if (has_to_call_irq_dma[i])
{
//ConLog("* SPU2: Irq Called (%04x) at cycle %d.\n", Spdif.Info, Cycles);
has_to_call_irq_dma[i] = false;
if (!(Spdif.Info & (4 << i)))
{
Spdif.Info |= (4 << i);
if (!SPU2_dummy_callback)
spu2Irq();
}
}
}
Cores[1].DMAICounter = 0;
//ConLog( "* SPU2 > DMA 7 Callback! %d\n", Cycles );
if (!SPU2_dummy_callback)
spu2DMA7Irq();
else
SPU2interruptDMA7();
}
}
//Update Mixing Progress
while (dClocks >= TickInterval)
{
if (has_to_call_irq)
for (int i = 0; i < 2; i++)
{
//ConLog("* SPU2: Irq Called (%04x) at cycle %d.\n", Spdif.Info, Cycles);
has_to_call_irq = false;
if (!SPU2_dummy_callback)
spu2Irq();
}
//Update DMA4 interrupt delay counter
if (Cores[0].DMAICounter > 0)
{
Cores[0].DMAICounter -= TickInterval;
if (Cores[0].DMAICounter <= 0)
if (has_to_call_irq[i])
{
if (Cores[0].IsDMARead)
Cores[0].FinishDMAread();
//ConLog("counter set and callback!\n");
Cores[0].MADR = Cores[0].TADR;
Cores[0].DMAICounter = 0;
if (!SPU2_dummy_callback)
spu2DMA4Irq();
else
SPU2interruptDMA4();
}
else
{
Cores[0].MADR += TickInterval << 1;
}
}
//Update DMA7 interrupt delay counter
if (Cores[1].DMAICounter > 0)
{
Cores[1].DMAICounter -= TickInterval;
if (Cores[1].DMAICounter <= 0)
{
if (Cores[1].IsDMARead)
Cores[1].FinishDMAread();
Cores[1].MADR = Cores[1].TADR;
Cores[1].DMAICounter = 0;
//ConLog( "* SPU2 > DMA 7 Callback! %d\n", Cycles );
if (!SPU2_dummy_callback)
spu2DMA7Irq();
else
SPU2interruptDMA7();
}
else
{
Cores[1].MADR += TickInterval << 1;
//ConLog("* SPU2: Irq Called (%04x) at cycle %d.\n", Spdif.Info, Cycles);
has_to_call_irq[i] = false;
if (!(Spdif.Info & (4 << i)))
{
Spdif.Info |= (4 << i);
if (!SPU2_dummy_callback)
spu2Irq();
}
}
}