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