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; DebugCores[Index].dmaFlag = 1; } TSA &= ~7; bool adma_enable = ((AutoDMACtrl&(Index+1))==(Index+1)); if(adma_enable) { TSA&=0x1fff; if(MsgAutoDMA() && DmaStarting) ConLog("* SPU2-X: DMA%c AutoDMA Transfer of %d bytes to %x (%02x %x %04x).\n", GetDmaIndexChar(), bytesLeft<<1, TSA, DMABits, AutoDMACtrl, (~Regs.ATTR)&0x7fff); 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); memcpy(GetMemPtr(0x2000+(Index<<10)+InputPosWrite),mptr,0x400); mptr+=0x200; // Flag interrupt? If IRQA occurs between start and dest, flag it. // Important: Test both core IRQ settings for either DMA! u32 dummyTSA = 0x2000+(Index<<10)+InputPosWrite; u32 dummyTDA = 0x2000+(Index<<10)+InputPosWrite+0x200; for( int i=0; i<2; i++ ) { if( Cores[i].IRQEnable && (Cores[i].IRQA >= dummyTSA) && (Cores[i].IRQA < dummyTDA) ) { SetIrqCall(i); } } } else { //memcpy((ADMATempBuffer+InputPosWrite),mptr,0x200); memcpy(GetMemPtr(0x2000+(Index<<10)+InputPosWrite),mptr,0x200); mptr+=0x100; // Flag interrupt? If IRQA occurs between start and dest, flag it. // Important: Test both core IRQ settings for either DMA! u32 dummyTSA = 0x2000+(Index<<10)+InputPosWrite; u32 dummyTDA = 0x2000+(Index<<10)+InputPosWrite+0x100; for( int i=0; i<2; i++ ) { if( Cores[i].IRQEnable && (Cores[i].IRQA >= dummyTSA) && (Cores[i].IRQA < dummyTDA) ) { SetIrqCall(i); } } //memcpy((ADMATempBuffer+InputPosWrite+0x200),mptr,0x200); memcpy(GetMemPtr(0x2200+(Index<<10)+InputPosWrite),mptr,0x200); mptr+=0x100; // Flag interrupt? If IRQA occurs between start and dest, flag it. // Important: Test both core IRQ settings for either DMA! dummyTSA = 0x2200+(Index<<10)+InputPosWrite; dummyTDA = 0x2200+(Index<<10)+InputPosWrite+0x100; for( int i=0; i<2; i++ ) { if( Cores[i].IRQEnable && (Cores[i].IRQA >= dummyTSA) && (Cores[i].IRQA < dummyTDA) ) { SetIrqCall(i); } } } // 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 { if(MsgDMA() && DmaStarting) ConLog("* SPU2-X: DMA%c Transfer of %d bytes to %x (%02x %x %04x).\n", GetDmaIndexChar(),bytesLeft,TSA,DMABits,AutoDMACtrl,(~Regs.ATTR)&0x7fff); if(bytesLeft> 2048) bytesLeft = 2048; // TODO: Sliced transfers? PlainDMAWrite((u16*)data,bytesLeft/2); } Regs.STATX &= ~0x80; Regs.STATX |= 0x400; #endif *bytesProcessed = bytesLeft; return 0; }
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) ) { SetIrqCall(i); } } } 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) ) { SetIrqCall(i); } } } TSA = TDA & 0xFFFFF; Regs.STATX &= ~0x80; Regs.STATX |= 0x400; #endif *bytesProcessed = bytesLeft; return 0; }
void V_Core::DoDMAread(u16* pMem, u32 size) { #ifndef ENABLE_NEW_IOPDMA_SPU2 TSA &= 0xffff8; u32 buff1end = TSA + size; 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)) { SetIrqCall(i); } } } 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) ) { SetIrqCall(i); } } } TSA = TDA & 0xFFFFF; DMAICounter = size; Regs.STATX &= ~0x80; //Regs.ATTR |= 0x30; TADR = MADR + (size<<1); #endif }
void V_Core::PlainDMAWrite(u16 *pMem, u32 size) { // Perform an alignment check. // Not really important. Everything should work regardless, // but it could be indicative of an emulation foopah elsewhere. #if 0 uptr pa = ((uptr)pMem)&7; uptr pm = TSA&0x7; if( pa ) { fprintf(stderr, "* SPU2 DMA Write > Missaligned SOURCE! Core: %d TSA: 0x%x TDA: 0x%x Size: 0x%x\n", core, TSA, TDA, size); } if( pm ) { fprintf(stderr, "* SPU2 DMA Write > Missaligned TARGET! Core: %d TSA: 0x%x TDA: 0x%x Size: 0x%x\n", core, TSA, TDA, size ); } #endif if(Index==0) DMA4LogWrite(pMem,size<<1); else DMA7LogWrite(pMem,size<<1); TSA &= 0xfffff; u32 buff1end = TSA + size; u32 buff2end=0; if( buff1end > 0x100000 ) { buff2end = buff1end - 0x100000; buff1end = 0x100000; } const int cacheIdxStart = TSA / pcm_WordsPerBlock; const int cacheIdxEnd = (buff1end+pcm_WordsPerBlock-1) / pcm_WordsPerBlock; PcmCacheEntry* cacheLine = &pcm_cache_data[cacheIdxStart]; PcmCacheEntry& cacheEnd = pcm_cache_data[cacheIdxEnd]; do { cacheLine->Validated = false; cacheLine++; } while ( cacheLine != &cacheEnd ); //ConLog( "* SPU2-X: Cache Clear Range! TSA=0x%x, TDA=0x%x (low8=0x%x, high8=0x%x, len=0x%x)\n", // TSA, buff1end, flagTSA, flagTDA, clearLen ); // First Branch needs cleared: // It starts at TSA and goes to buff1end. const u32 buff1size = (buff1end-TSA); memcpy( GetMemPtr( TSA ), pMem, buff1size*2 ); if( buff2end > 0 ) { // second branch needs copied: // It starts at the beginning of memory and moves forward to buff2end // endpoint cache should be irrelevant, since it's almost certainly dynamic // memory below 0x2800 (registers and such) //const u32 endpt2 = (buff2end + roundUp) / indexer_scalar; //memset( pcm_cache_flags, 0, endpt2 ); // Emulation Grayarea: Should addresses wrap around to zero, or wrap around to // 0x2800? Hard to know for sure (almost no games depend on this) memcpy( GetMemPtr( 0 ), &pMem[buff1size], buff2end*2 ); TDA = (buff2end+1) & 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 && #if NO_BIOS_HACKFIX for( int i=0; i<2; i++ ) { // Note: (start is inclusive, dest exclusive -- fixes DMC1 FMVs) 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); } } #else if ((IRQEnable && (IRQA >= TSA)) || (IRQA < TDA)) { SetIrqCall(Index); } #endif } else { // Buffer doesn't wrap/overflow! // Just set the TDA and check for an IRQ... TDA = buff1end; // Flag interrupt? If IRQA occurs between start and dest, flag it. // Important: Test both core IRQ settings for either DMA! #if NO_BIOS_HACKFIX for( int i=0; i<2; i++ ) { // Note: (start is inclusive, dest exclusive -- fixes DMC1 FMVs) 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); } } #else if( IRQEnable && (IRQA >= TSA) && (IRQA < TDA) ) { SetIrqCall(Index); } #endif } TSA = TDA & 0xFFFF0; DMAICounter = size; TADR = MADR + (size<<1); }
void SPU2readDMA(int core, u16* pMem, u32 size) { if(hasPtr) TimeUpdate(*cPtr); Cores[core].TSA &= 0xffff8; u32 buff1end = Cores[core].TSA + size; u32 buff2end = 0; if( buff1end > 0x100000 ) { buff2end = buff1end - 0x100000; buff1end = 0x100000; } const u32 buff1size = (buff1end-Cores[core].TSA); memcpy( pMem, GetMemPtr( Cores[core].TSA ), buff1size*2 ); 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 ); Cores[core].TDA = (buff2end+0x20) & 0xfffff; for( int i=0; i<2; i++ ) { if(Cores[i].IRQEnable) { // Flag interrupt? // If IRQA occurs between start and dest, flag it. // Since the buffer wraps, the conditional might seem odd, but it works. if( ( Cores[i].IRQA >= Cores[core].TSA ) || ( Cores[i].IRQA <= Cores[core].TDA ) ) { Spdif.Info=4<<i; SetIrqCall(); } } } } else { // Buffer doesn't wrap/overflow! // Just set the TDA and check for an IRQ... Cores[core].TDA = buff1end; for( int i=0; i<2; i++ ) { if(Cores[i].IRQEnable) { // Flag interrupt? // If IRQA occurs between start and dest, flag it: if( ( Cores[i].IRQA >= Cores[i].TSA ) && ( Cores[i].IRQA <= Cores[i].TDA+0x1f ) ) { Spdif.Info=4<<i; SetIrqCall(); } } } } Cores[core].TSA=Cores[core].TDA & 0xFFFFF; Cores[core].DMAICounter=size; Cores[core].Regs.STATX &= ~0x80; //Cores[core].Regs.ATTR |= 0x30; Cores[core].TADR=Cores[core].MADR+(size<<1); }
void DoDMAWrite(int core,u16 *pMem,u32 size) { // Perform an alignment check. // Not really important. Everything should work regardless, // but it could be indicative of an emulation foopah elsewhere. #if 0 uptr pa = ((uptr)pMem)&7; uptr pm = Cores[core].TSA&0x7; if( pa ) { fprintf(stderr, "* SPU2 DMA Write > Missaligned SOURCE! Core: %d TSA: 0x%x TDA: 0x%x Size: 0x%x\n", core, Cores[core].TSA, Cores[core].TDA, size); } if( pm ) { fprintf(stderr, "* SPU2 DMA Write > Missaligned TARGET! Core: %d TSA: 0x%x TDA: 0x%x Size: 0x%x\n", core, Cores[core].TSA, Cores[core].TDA, size ); } #endif if(core==0) DMA4LogWrite(pMem,size<<1); else DMA7LogWrite(pMem,size<<1); if(MsgDMA()) ConLog(" * SPU2: DMA%c Transfer of %d bytes to %x (%02x %x %04x).\n",(core==0)?'4':'7',size<<1,Cores[core].TSA,Cores[core].DMABits,Cores[core].AutoDMACtrl,(~Cores[core].Regs.ATTR)&0x7fff); Cores[core].TSA &= 0xfffff; u32 buff1end = Cores[core].TSA + size; u32 buff2end=0; if( buff1end > 0x100000 ) { buff2end = buff1end - 0x100000; buff1end = 0x100000; } const int cacheIdxStart = Cores[core].TSA / pcm_WordsPerBlock; const int cacheIdxEnd = (buff1end+pcm_WordsPerBlock-1) / pcm_WordsPerBlock; PcmCacheEntry* cacheLine = &pcm_cache_data[cacheIdxStart]; PcmCacheEntry& cacheEnd = pcm_cache_data[cacheIdxEnd]; do { cacheLine->Validated = false; cacheLine++; } while ( cacheLine != &cacheEnd ); #if 0 // Pcm Cache Invalidation! // It's a requirement that we mask bits for the blocks that are written to *only*, // because doing anything else can cause the cache to fail, thanks to the progressive // nature of the SPU2's ADPCM encoding. (the same thing that makes it impossible // to use SSE optimizations on it). u8* cache = (u8*)pcm_cache_flags; // Step 1: Clear bits in the front remainder. const int pcmTSA = Cores[core].TSA / pcm_WordsPerBlock; const int pcmTDA = buff1end / pcm_WordsPerBlock; const int remFront = pcmTSA & 31; const int remBack = ((buff1end+pcm_WordsPerBlock-1)/pcm_WordsPerBlock) & 31; // round up to get the end remainder int flagTSA = pcmTSA / 32; if( remFront ) { // need to clear some upper bits of this u32 uint mask = (1ul<<remFront)-1; cache[flagTSA++] &= mask; } // Step 2: Clear the middle run const int flagClearLen = pcmTDA-pcmTSA; memset( &cache[flagTSA], 0, flagClearLen ); // Step 3: Clear bits in the end remainder. if( remBack ) { // need to clear some lower bits in this u32 uint mask = ~(1ul<<remBack)-1; cache[flagTSA + flagClearLen] &= mask; } #endif //ConLog( " * SPU2 : Cache Clear Range! TSA=0x%x, TDA=0x%x (low8=0x%x, high8=0x%x, len=0x%x)\n", // Cores[core].TSA, buff1end, flagTSA, flagTDA, clearLen ); // First Branch needs cleared: // It starts at TSA and goes to buff1end. const u32 buff1size = (buff1end-Cores[core].TSA); memcpy( GetMemPtr( Cores[core].TSA ), pMem, buff1size*2 ); if( buff2end > 0 ) { // second branch needs copied: // It starts at the beginning of memory and moves forward to buff2end // endpoint cache should be irrelevant, since it's almost certainly dynamic // memory below 0x2800 (registers and such) //const u32 endpt2 = (buff2end + roundUp) / indexer_scalar; //memset( pcm_cache_flags, 0, endpt2 ); memcpy( GetMemPtr( 0 ), &pMem[buff1size], buff2end*2 ); Cores[core].TDA = (buff2end+1) & 0xfffff; if(Cores[core].IRQEnable) { // Flag interrupt? // If IRQA occurs between start and dest, flag it. // Since the buffer wraps, the conditional might seem odd, but it works. if( ( Cores[core].IRQA >= Cores[core].TSA ) || ( Cores[core].IRQA <= Cores[core].TDA ) ) { Spdif.Info=4<<core; SetIrqCall(); } } } else { // Buffer doesn't wrap/overflow! // Just set the TDA and check for an IRQ... Cores[core].TDA = buff1end; if(Cores[core].IRQEnable) { // Flag interrupt? // If IRQA occurs between start and dest, flag it: if( ( Cores[core].IRQA >= Cores[core].TSA ) && ( Cores[core].IRQA <= Cores[core].TDA ) ) { Spdif.Info=4<<core; SetIrqCall(); } } } Cores[core].TSA=Cores[core].TDA&0xFFFF0; Cores[core].DMAICounter=size; Cores[core].TADR=Cores[core].MADR+(size<<1); }