From f07ca859e55140195234c160ed47d934e4c79d1e Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Tue, 29 Dec 2020 10:30:33 +0000 Subject: [PATCH] SPU2: Improve DMA/IRQ timing. Tighten SPU2 sync with IOP --- pcsx2/IopCounters.cpp | 2 +- pcsx2/IopDma.cpp | 2 +- pcsx2/SPU2/Dma.cpp | 29 +++++--- pcsx2/SPU2/defs.h | 2 + pcsx2/SPU2/spu2.cpp | 6 +- pcsx2/SPU2/spu2sys.cpp | 147 ++++++++++++++++++++++++++--------------- 6 files changed, 119 insertions(+), 69 deletions(-) diff --git a/pcsx2/IopCounters.cpp b/pcsx2/IopCounters.cpp index 59a5316489..d3e5ea694d 100644 --- a/pcsx2/IopCounters.cpp +++ b/pcsx2/IopCounters.cpp @@ -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; diff --git a/pcsx2/IopDma.cpp b/pcsx2/IopDma.cpp index d5043f5122..f49a6ed63e 100644 --- a/pcsx2/IopDma.cpp +++ b/pcsx2/IopDma.cpp @@ -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; diff --git a/pcsx2/SPU2/Dma.cpp b/pcsx2/SPU2/Dma.cpp index 32ca6bc5ec..8dea57a0ba 100644 --- a/pcsx2/SPU2/Dma.cpp +++ b/pcsx2/SPU2/Dma.cpp @@ -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; } diff --git a/pcsx2/SPU2/defs.h b/pcsx2/SPU2/defs.h index 54aa371a85..8067cc35c5 100644 --- a/pcsx2/SPU2/defs.h +++ b/pcsx2/SPU2/defs.h @@ -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(); diff --git a/pcsx2/SPU2/spu2.cpp b/pcsx2/SPU2/spu2.cpp index e9c1309fa9..2224043d3d 100644 --- a/pcsx2/SPU2/spu2.cpp +++ b/pcsx2/SPU2/spu2.cpp @@ -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) diff --git a/pcsx2/SPU2/spu2sys.cpp b/pcsx2/SPU2/spu2sys.cpp index 2d4ca9b678..6376310f48 100644 --- a/pcsx2/SPU2/spu2sys.cpp +++ b/pcsx2/SPU2/spu2sys.cpp @@ -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(); + } } }