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
This commit is contained in:
gigaherz 2010-02-03 03:37:55 +00:00
parent 3ab89904e4
commit 9ae807134e
9 changed files with 325 additions and 48 deletions

View File

@ -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;

View File

@ -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
}

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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)<b) break;
@ -117,6 +121,7 @@ void dummy7()
#include "Windows/Dialogs.h"
EXPORT_C_(void) s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdShow)
{
#ifndef ENABLE_NEW_IOPDMA_SPU2
// load file
FILE *file=fopen(filename,"rb");
@ -190,5 +195,6 @@ EXPORT_C_(void) s2r_replay(HWND hwnd, HINSTANCE hinst, LPSTR filename, int nCmdS
fclose(file);
replay_mode=false;
#endif
}
#endif

View File

@ -29,13 +29,18 @@ EXPORTS
SPU2close @8
SPU2write @9
SPU2read @10
SPU2readDMA4Mem @11
SPU2writeDMA4Mem @12
SPU2readDMA7Mem @13
SPU2writeDMA7Mem @14
SPU2async @15
SPU2async @11
SPU2readDMA4Mem @12
SPU2writeDMA4Mem @13
SPU2readDMA7Mem @14
SPU2writeDMA7Mem @15
SPU2interruptDMA4 @16
SPU2interruptDMA7 @17
SPU2dmaRead
SPU2dmaWrite
SPU2dmaInterrupt
SPU2irqCallback @18
SPU2setupRecording @19

View File

@ -391,7 +391,8 @@ struct V_Core
u16 AutoDMACtrl; // AutoDMA Status
s32 DMAICounter; // DMA Interrupt Counter
u32 InputDataLeft; // Input Buffer
u32 InputPos;
u32 InputPosRead;
u32 InputPosWrite;
u32 InputDataProgress;
V_Reverb Revb; // Reverb Registers
@ -419,6 +420,11 @@ struct V_Core
u8 AttrBit4;
u8 AttrBit5;
// new dma only
bool DmaStarted;
u32 AutoDmaFree;
// old dma only
u16* DMAPtr;
u32 MADR;
u32 TADR;
@ -493,7 +499,12 @@ struct V_Core
}
void LogAutoDMA( FILE* fp );
s32 NewDmaRead(u32* data, u32 bytesLeft, u32* bytesProcessed);
s32 NewDmaWrite(u32* data, u32 bytesLeft, u32* bytesProcessed);
void NewDmaInterrupt();
// old dma only
void DoDMAwrite(u16* pMem, u32 size);
void DoDMAread(u16* pMem, u32 size);

View File

@ -327,6 +327,7 @@ __forceinline void TimeUpdate(u32 cClocks)
}
}*/
#ifndef ENABLE_NEW_IOPDMA_SPU2
//Update DMA4 interrupt delay counter
if(Cores[0].DMAICounter>0)
{
@ -357,6 +358,7 @@ __forceinline void TimeUpdate(u32 cClocks)
Cores[1].MADR+=TickInterval<<1;
}
}
#endif
dClocks -= TickInterval;
lClocks += TickInterval;