From 9ae807134ed6c96f86f2f3ad865b5700d92d86db Mon Sep 17 00:00:00 2001 From: gigaherz Date: Wed, 3 Feb 2010 03:37:55 +0000 Subject: [PATCH] Experimental implementation of spu2 interface for the new iop dmac. It's implemented only on SPU2-X and disabled by default. FF12 and Atelier iris had working FMVs and bgm, other games might not Try at your own risk. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2554 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/IopDma.cpp | 21 ++- plugins/spu2-x/src/Dma.cpp | 192 +++++++++++++++++++++++++- plugins/spu2-x/src/PS2E-spu2.cpp | 59 ++++++-- plugins/spu2-x/src/PS2E-spu2.h | 21 ++- plugins/spu2-x/src/ReadInput.cpp | 42 ++++-- plugins/spu2-x/src/Spu2replay.cpp | 6 + plugins/spu2-x/src/Windows/Spu2-X.def | 15 +- plugins/spu2-x/src/defs.h | 15 +- plugins/spu2-x/src/spu2sys.cpp | 2 + 9 files changed, 325 insertions(+), 48 deletions(-) diff --git a/pcsx2/IopDma.cpp b/pcsx2/IopDma.cpp index 0ad8bedbfc..285ec70e8a 100644 --- a/pcsx2/IopDma.cpp +++ b/pcsx2/IopDma.cpp @@ -344,17 +344,30 @@ const DmaHandlerInfo IopDmaHandlers[DMA_CHANNEL_MAX] = {"Ps1 Mdec", _D__}, //1 {"Ps1 Gpu", _D__}, //2 {"CDVD", _DR_, CHANNEL_BASE1(3), cdvdDmaRead, errDmaWrite, cdvdDmaInterrupt}, //3: CDVD - {"SPU2 Core0", _DRW, CHANNEL_BASE1(4), spu2DmaRead, spu2DmaWrite, spu2DmaInterrupt}, //4: Spu Core0 +#ifdef ENABLE_NEW_IOPDMA_SPU2 + {"SPU2 Core0", _ERW, CHANNEL_BASE1(4), spu2DmaRead, spu2DmaWrite, spu2DmaInterrupt}, //4: Spu/Spu2 Core0 +#else + {"SPU2 Core0", _D__}, //4: Spu/Spu2 Core0 +#endif {"?", _D__}, //5 {"OT", _D__}, //6: OT? // Second DMAC, new in PS2 IOP - {"SPU2 Core1", _DRW, CHANNEL_BASE2(0), spu2DmaRead, spu2DmaWrite, spu2DmaInterrupt}, //7: Spu Core1 +#ifdef ENABLE_NEW_IOPDMA_SPU2 + {"SPU2 Core1", _ERW, CHANNEL_BASE2(0), spu2DmaRead, spu2DmaWrite, spu2DmaInterrupt}, //7: Spu2 Core1 +#else + {"SPU2 Core1", _D__}, //7: Spu2 Core1 +#endif {"Dev9", _DRW},// CHANNEL_BASE2(1), dev9DmaRead, dev9DmaWrite, dev9DmaInterrupt}, //8: Dev9 {"Sif0", _DRW},// CHANNEL_BASE2(2), sif0DmaRead, sif0DmaWrite, sif0DmaInterrupt}, //9: SIF0 {"Sif1", _DRW},// CHANNEL_BASE2(3), sif1DmaRead, sif1DmaWrite, sif1DmaInterrupt}, //10: SIF1 +#ifdef ENABLE_NEW_IOPDMA_SIO {"Sio2 (writes)", _E_W, CHANNEL_BASE2(4), errDmaRead, sio2DmaWrite, sio2DmaInterrupt}, //11: Sio2 {"Sio2 (reads)", _ER_, CHANNEL_BASE2(5), sio2DmaRead, errDmaWrite, sio2DmaInterrupt}, //12: Sio2 +#else + {"Sio2 (writes)", _D__}, //11: Sio2 + {"Sio2 (reads)", _D__}, //12: Sio2 +#endif {"?", _D__}, //13 // if each dmac has 7 channels, the list would end here, but I'm not sure :p }; @@ -486,8 +499,8 @@ static void __releaseinline IopDmaProcessChannel(int elapsed, int& MinDelay) NextUpdateDelay = ProcessedBytes/2; // / ch->Width; } - else - DevCon.Warning("What now? :p"); + else if(RequestedDelay==0) + DevCon.Warning("What now? :p"); // its ok as long as there's a delay requeste, autodma requires this. if (RequestedDelay != 0) NextUpdateDelay = RequestedDelay; diff --git a/plugins/spu2-x/src/Dma.cpp b/plugins/spu2-x/src/Dma.cpp index da4970e108..857c1fd714 100644 --- a/plugins/spu2-x/src/Dma.cpp +++ b/plugins/spu2-x/src/Dma.cpp @@ -85,7 +85,8 @@ void V_Core::LogAutoDMA( FILE* fp ) void V_Core::AutoDMAReadBuffer(int mode) //mode: 0= split stereo; 1 = do not split stereo { - int spos = ((InputPos+0xff)&0x100); //starting position of the free buffer +#ifndef ENABLE_NEW_IOPDMA_SPU2 + int spos = ((InputPosRead+0xff)&0x100); //starting position of the free buffer LogAutoDMA( Index ? ADMA7LogFile : ADMA4LogFile ); @@ -119,10 +120,12 @@ void V_Core::AutoDMAReadBuffer(int mode) //mode: 0= split stereo; 1 = do not spl } // See ReadInput at mixer.cpp for explanation on the commented out lines // +#endif } void V_Core::StartADMAWrite(u16 *pMem, u32 sz) { +#ifndef ENABLE_NEW_IOPDMA_SPU2 int size = (sz)&(~511); if(MsgAutoDMA()) ConLog(" * SPU2: DMA%c AutoDMA Transfer of %d bytes to %x (%02x %x %04x).\n", @@ -150,7 +153,7 @@ void V_Core::StartADMAWrite(u16 *pMem, u32 sz) } #else if( ((PlayMode&4)==4) && (Index==0) ) - Cores[0].InputPos=0; + Cores[0].InputPosRead=0; AutoDMAReadBuffer(0); #endif @@ -167,6 +170,7 @@ void V_Core::StartADMAWrite(u16 *pMem, u32 sz) DMAICounter = 1; } TADR = MADR + (size<<1); +#endif } // HACKFIX: The BIOS breaks if we check the IRQA for both cores when issuing DMA writes. The @@ -186,7 +190,6 @@ void V_Core::StartADMAWrite(u16 *pMem, u32 sz) // Update: This hack is no longer needed when we don't do a core reset. Guess the null pc was in spu2 memory? #define NO_BIOS_HACKFIX 1 // set to 1 to disable the hackfix - void V_Core::PlainDMAWrite(u16 *pMem, u32 size) { // Perform an alignment check. @@ -322,6 +325,7 @@ void V_Core::PlainDMAWrite(u16 *pMem, u32 size) void V_Core::DoDMAread(u16* pMem, u32 size) { +#ifndef ENABLE_NEW_IOPDMA_SPU2 TSA &= 0xffff8; u32 buff1end = TSA + size; @@ -387,10 +391,12 @@ void V_Core::DoDMAread(u16* pMem, u32 size) Regs.STATX &= ~0x80; //Regs.ATTR |= 0x30; TADR = MADR + (size<<1); +#endif } void V_Core::DoDMAwrite(u16* pMem, u32 size) { +#ifndef ENABLE_NEW_IOPDMA_SPU2 DMAPtr = pMem; if(size<2) { @@ -420,4 +426,184 @@ void V_Core::DoDMAwrite(u16* pMem, u32 size) } Regs.STATX &= ~0x80; //Regs.ATTR |= 0x30; +#endif } + +s32 V_Core::NewDmaRead(u32* data, u32 bytesLeft, u32* bytesProcessed) +{ +#ifdef ENABLE_NEW_IOPDMA_SPU2 + bool DmaStarting = !DmaStarted; + DmaStarted = true; + + TSA &= 0xffff8; + + u16* pMem = (u16*)data; + + u32 buff1end = TSA + bytesLeft; + u32 buff2end = 0; + if( buff1end > 0x100000 ) + { + buff2end = buff1end - 0x100000; + buff1end = 0x100000; + } + + const u32 buff1size = (buff1end-TSA); + memcpy( pMem, GetMemPtr( TSA ), buff1size*2 ); + + // Note on TSA's position after our copy finishes: + // IRQA should be measured by the end of the writepos+0x20. But the TDA + // should be written back at the precise endpoint of the xfer. + + if( buff2end > 0 ) + { + // second branch needs cleared: + // It starts at the beginning of memory and moves forward to buff2end + + memcpy( &pMem[buff1size], GetMemPtr( 0 ), buff2end*2 ); + + TDA = (buff2end+0x20) & 0xfffff; + + // Flag interrupt? If IRQA occurs between start and dest, flag it. + // Important: Test both core IRQ settings for either DMA! + // Note: Because this buffer wraps, we use || instead of && + + for( int i=0; i<2; i++ ) + { + if( Cores[i].IRQEnable && (Cores[i].IRQA >= TSA) || (Cores[i].IRQA < TDA) ) + { + Spdif.Info = 4 << i; + SetIrqCall(); + } + } + } + else + { + // Buffer doesn't wrap/overflow! + // Just set the TDA and check for an IRQ... + + TDA = (buff1end + 0x20) & 0xfffff; + + // Flag interrupt? If IRQA occurs between start and dest, flag it. + // Important: Test both core IRQ settings for either DMA! + + for( int i=0; i<2; i++ ) + { + if( Cores[i].IRQEnable && (Cores[i].IRQA >= TSA) && (Cores[i].IRQA < TDA) ) + { + Spdif.Info = 4 << i; + SetIrqCall(); + } + } + } + + TSA = TDA & 0xFFFFF; + + Regs.STATX &= ~0x80; + //Regs.ATTR |= 0x30; + +#endif + *bytesProcessed = bytesLeft; + return 0; +} + +s32 V_Core::NewDmaWrite(u32* data, u32 bytesLeft, u32* bytesProcessed) +{ +#ifdef ENABLE_NEW_IOPDMA_SPU2 + bool DmaStarting = !DmaStarted; + DmaStarted = true; + + if(bytesLeft<2) + { + // execute interrupt code early + NewDmaInterrupt(); + + *bytesProcessed = bytesLeft; + return 0; + } + + if( IsDevBuild ) + DebugCores[Index].lastsize = bytesLeft; + + TSA &= ~7; + + bool adma_enable = ((AutoDMACtrl&(Index+1))==(Index+1)); + + if(adma_enable) + { + TSA&=0x1fff; + //Console.Error(" * SPU2: AutoDMA transfers not supported yet! (core %d)\n", Index); + + if(DmaStarting) + { + ConLog(" * SPU2: AutoDMA transfer starting on core %d: %d bytes\n", Index, bytesLeft); + } + + u32 processed = 0; + while((AutoDmaFree>0)&&(bytesLeft>=0x400)) + { + // copy block + + LogAutoDMA( Index ? ADMA7LogFile : ADMA4LogFile ); + + // HACKFIX!! DMAPtr can be invalid after a savestate load, so the savestate just forces it + // to NULL and we ignore it here. (used to work in old VM editions of PCSX2 with fixed + // addressing, but new PCSX2s have dynamic memory addressing). + + s16* mptr = (s16*)data; + + if(false)//(mode) + { + memcpy((ADMATempBuffer+(InputPosWrite<<1)),mptr,0x400); + mptr+=0x200; + } + else + { + memcpy((ADMATempBuffer+InputPosWrite),mptr,0x200); + //memcpy((spu2mem+0x2000+(core<<10)+InputPosWrite),mptr,0x200); + mptr+=0x100; + + memcpy((ADMATempBuffer+InputPosWrite+0x200),mptr,0x200); + //memcpy((spu2mem+0x2200+(core<<10)+InputPosWrite),mptr,0x200); + mptr+=0x100; + } + // See ReadInput at mixer.cpp for explanation on the commented out lines + // + + InputPosWrite = (InputPosWrite + 0x100) & 0x1ff; + AutoDmaFree -= 0x200; + processed += 0x400; + bytesLeft -= 0x400; + } + + if(processed==0) + { + *bytesProcessed = 0; + return 768*15; // pause a bit + } + else + { + *bytesProcessed = processed; + return 0; // auto pause + } + } + else + { + // TODO: Sliced transfers? + PlainDMAWrite((u16*)data,bytesLeft); + } + Regs.STATX &= ~0x80; + //Regs.ATTR |= 0x30; +#endif + *bytesProcessed = bytesLeft; + return 0; +} + +void V_Core::NewDmaInterrupt() +{ +#ifdef ENABLE_NEW_IOPDMA_SPU2 + FileLog("[%10d] SPU2 interruptDMA4\n",Cycles); + Regs.STATX |= 0x80; + //Regs.ATTR &= ~0x30; + DmaStarted = false; +#endif +} \ No newline at end of file diff --git a/plugins/spu2-x/src/PS2E-spu2.cpp b/plugins/spu2-x/src/PS2E-spu2.cpp index 56a6888c51..52f26eb1f6 100644 --- a/plugins/spu2-x/src/PS2E-spu2.cpp +++ b/plugins/spu2-x/src/PS2E-spu2.cpp @@ -195,6 +195,44 @@ EXPORT_C_(void) CALLBACK SPU2setSettingsDir(const char* dir) CfgSetSettingsDir( dir ); } +EXPORT_C_(s32) SPU2dmaRead(s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) +{ + if(channel==4) + return Cores[0].NewDmaRead(data,bytesLeft, bytesProcessed); + else + return Cores[1].NewDmaRead(data,bytesLeft, bytesProcessed); +} + +EXPORT_C_(s32) SPU2dmaWrite(s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed) +{ + if(channel==4) + return Cores[0].NewDmaWrite(data,bytesLeft, bytesProcessed); + else + return Cores[1].NewDmaWrite(data,bytesLeft, bytesProcessed); +} + +EXPORT_C_(void) SPU2dmaInterrupt(s32 channel) +{ + if(channel==4) + return Cores[0].NewDmaInterrupt(); + else + return Cores[1].NewDmaInterrupt(); +} + +#ifdef ENABLE_NEW_IOPDMA_SPU2 +EXPORT_C_(void) SPU2irqCallback(void (*SPU2callback)()) +{ + _irqcallback = SPU2callback; +} +#else +EXPORT_C_(void) SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)()) +{ + _irqcallback = SPU2callback; + dma4callback = DMA4callback; + dma7callback = DMA7callback; +} +#endif + EXPORT_C_(void) CALLBACK SPU2readDMA4Mem(u16 *pMem, u32 size) // size now in 16bit units { if( cyclePtr != NULL ) TimeUpdate( *cyclePtr ); @@ -222,6 +260,13 @@ EXPORT_C_(void) CALLBACK SPU2interruptDMA4() //Cores[0].Regs.ATTR &= ~0x30; } +EXPORT_C_(void) CALLBACK SPU2interruptDMA7() +{ + FileLog("[%10d] SPU2 interruptDMA7\n",Cycles); + Cores[1].Regs.STATX |= 0x80; + //Cores[1].Regs.ATTR &= ~0x30; +} + EXPORT_C_(void) CALLBACK SPU2readDMA7Mem(u16* pMem, u32 size) { if( cyclePtr != NULL ) TimeUpdate( *cyclePtr ); @@ -242,13 +287,6 @@ EXPORT_C_(void) CALLBACK SPU2writeDMA7Mem(u16* pMem, u32 size) Cores[1].DoDMAwrite(pMem,size); } -EXPORT_C_(void) CALLBACK SPU2interruptDMA7() -{ - FileLog("[%10d] SPU2 interruptDMA7\n",Cycles); - Cores[1].Regs.STATX |= 0x80; - //Cores[1].Regs.ATTR &= ~0x30; -} - EXPORT_C_(s32) SPU2init() { assert( regtable[0x400] == NULL ); @@ -452,13 +490,6 @@ EXPORT_C_(void) SPU2async(u32 cycles) } } -EXPORT_C_(void) SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)()) -{ - _irqcallback = SPU2callback; - dma4callback = DMA4callback; - dma7callback = DMA7callback; -} - EXPORT_C_(u16) SPU2read(u32 rmem) { // if(!replay_mode) diff --git a/plugins/spu2-x/src/PS2E-spu2.h b/plugins/spu2-x/src/PS2E-spu2.h index b04b7878f2..79cb00eeb1 100644 --- a/plugins/spu2-x/src/PS2E-spu2.h +++ b/plugins/spu2-x/src/PS2E-spu2.h @@ -37,20 +37,28 @@ EXPORT_C_(void) SPU2close(); EXPORT_C_(void) SPU2shutdown(); EXPORT_C_(void) SPU2write(u32 mem, u16 value); EXPORT_C_(u16) SPU2read(u32 mem); + +#ifdef ENABLE_NEW_IOPDMA_SPU2 +EXPORT_C_(s32) SPU2dmaRead(s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed); +EXPORT_C_(s32) SPU2dmaWrite(s32 channel, u32* data, u32 bytesLeft, u32* bytesProcessed); +EXPORT_C_(void) SPU2dmaInterrupt(s32 channel); + +// dma irq callbacks not needed anymore, they are handled by the dmac +EXPORT_C_(void) SPU2irqCallback(void (*SPU2callback)()); +#else EXPORT_C_(void) SPU2readDMA4Mem(u16 *pMem, u32 size); EXPORT_C_(void) SPU2writeDMA4Mem(u16 *pMem, u32 size); EXPORT_C_(void) SPU2interruptDMA4(); EXPORT_C_(void) SPU2readDMA7Mem(u16* pMem, u32 size); EXPORT_C_(void) SPU2writeDMA7Mem(u16 *pMem, u32 size); +EXPORT_C_(void) SPU2interruptDMA7(); // all addresses passed by dma will be pointers to the array starting at baseaddr // This function is necessary to successfully save and reload the spu2 state -EXPORT_C_(void) SPU2setDMABaseAddr(uptr baseaddr); - -EXPORT_C_(void) SPU2interruptDMA7(); EXPORT_C_(u32) SPU2ReadMemAddr(int core); EXPORT_C_(void) SPU2WriteMemAddr(int core,u32 value); EXPORT_C_(void) SPU2irqCallback(void (*SPU2callback)(),void (*DMA4callback)(),void (*DMA7callback)()); +#endif // extended funcs // if start is 1, starts recording spu2 data, else stops @@ -71,13 +79,16 @@ EXPORT_C_(s32) SPU2test(); extern u8 callirq; extern void (* _irqcallback)(); + +#ifndef ENABLE_NEW_IOPDMA_SPU2 extern void (* dma4callback)(); extern void (* dma7callback)(); -extern double srate_pv; - extern s16 *input_data; extern u32 input_data_ptr; +#endif + +extern double srate_pv; extern int recording; extern u32 lClocks; diff --git a/plugins/spu2-x/src/ReadInput.cpp b/plugins/spu2-x/src/ReadInput.cpp index 09129a772f..a00823c849 100644 --- a/plugins/spu2-x/src/ReadInput.cpp +++ b/plugins/spu2-x/src/ReadInput.cpp @@ -18,6 +18,8 @@ #include "Global.h" #include "dma.h" +#include "PS2E-spu2.h" // required for ENABLE_NEW_IOPDMA_SPU2 define + // Core 0 Input is "SPDIF mode" - Source audio is AC3 compressed. // Core 1 Input is "CDDA mode" - Source audio data is 32 bits. @@ -29,17 +31,17 @@ // StereoOut32 V_Core::ReadInput_HiFi() { - InputPos &= ~1; + InputPosRead &= ~1; #ifdef PCM24_S1_INTERLEAVE StereoOut32 retval( - *((s32*)(ADMATempBuffer+(InputPos<<1))), - *((s32*)(ADMATempBuffer+(InputPos<<1)+2)) + *((s32*)(ADMATempBuffer+(InputPosRead<<1))), + *((s32*)(ADMATempBuffer+(InputPosRead<<1)+2)) ); #else StereoOut32 retval( - (s32&)(ADMATempBuffer[InputPos]), - (s32&)(ADMATempBuffer[InputPos+0x200]) + (s32&)(ADMATempBuffer[InputPosRead]), + (s32&)(ADMATempBuffer[InputPosRead+0x200]) ); #endif @@ -53,13 +55,17 @@ StereoOut32 V_Core::ReadInput_HiFi() retval.Right >>= 4; } - InputPos += 2; + InputPosRead += 2; // Why does CDDA mode check for InputPos == 0x100? In the old code, SPDIF mode did not but CDDA did. // One of these seems wrong, they should be the same. Since standard ADMA checks too I'm assuming that as default. -- air - if( (InputPos==0x100) || (InputPos>=0x200) ) + if( (InputPosRead==0x100) || (InputPosRead>=0x200) ) { +#ifdef ENABLE_NEW_IOPDMA_SPU2 + // WARNING: Assumes this to be in the same thread as the dmas + AutoDmaFree += 0x200; +#else AdmaInProgress = 0; if(InputDataLeft >= 0x200) { @@ -70,7 +76,7 @@ StereoOut32 V_Core::ReadInput_HiFi() #endif AdmaInProgress = 1; - TSA = (Index<<10) + InputPos; + TSA = (Index<<10) + InputPosRead; if (InputDataLeft < 0x200) { @@ -87,7 +93,8 @@ StereoOut32 V_Core::ReadInput_HiFi() DMAICounter = 1; } } - InputPos &= 0x1ff; +#endif + InputPosRead &= 0x1ff; } return retval; } @@ -106,14 +113,18 @@ StereoOut32 V_Core::ReadInput() //*PData.Right = (s32)*(s16*)(spu2mem+0x2200+(core<<10)+InputPos); retval = StereoOut32( - (s32)ADMATempBuffer[InputPos], - (s32)ADMATempBuffer[InputPos+0x200] + (s32)ADMATempBuffer[InputPosRead], + (s32)ADMATempBuffer[InputPosRead+0x200] ); } - InputPos++; - if( (InputPos==0x100) || (InputPos>=0x200) ) + InputPosRead++; + if( (InputPosRead==0x100) || (InputPosRead>=0x200) ) { +#ifdef ENABLE_NEW_IOPDMA_SPU2 + // WARNING: Assumes this to be in the same thread as the dmas + AutoDmaFree += 0x200; +#else AdmaInProgress = 0; if(InputDataLeft >= 0x200) { @@ -122,7 +133,7 @@ StereoOut32 V_Core::ReadInput() AutoDMAReadBuffer(0); AdmaInProgress = 1; - TSA = (Index<<10) + InputPos; + TSA = (Index<<10) + InputPosRead; if (InputDataLeft < 0x200) { @@ -141,7 +152,8 @@ StereoOut32 V_Core::ReadInput() DMAICounter = 1; } } - InputPos&=0x1ff; +#endif + InputPosRead&=0x1ff; } return retval; } diff --git a/plugins/spu2-x/src/Spu2replay.cpp b/plugins/spu2-x/src/Spu2replay.cpp index acce80dd0a..0c9d0973ba 100644 --- a/plugins/spu2-x/src/Spu2replay.cpp +++ b/plugins/spu2-x/src/Spu2replay.cpp @@ -103,12 +103,16 @@ void dummy1() void dummy4() { +#ifndef ENABLE_NEW_IOPDMA_SPU2 SPU2interruptDMA4(); +#endif } void dummy7() { +#ifndef ENABLE_NEW_IOPDMA_SPU2 SPU2interruptDMA7(); +#endif } #define Cread(a,b,c,d) if(fread(a,b,c,d)0) { @@ -357,6 +358,7 @@ __forceinline void TimeUpdate(u32 cClocks) Cores[1].MADR+=TickInterval<<1; } } +#endif dClocks -= TickInterval; lClocks += TickInterval;