diff --git a/Source/Core/Core/HW/DSP.cpp b/Source/Core/Core/HW/DSP.cpp index c8413658e4..23e6b0698c 100644 --- a/Source/Core/Core/HW/DSP.cpp +++ b/Source/Core/Core/HW/DSP.cpp @@ -158,6 +158,9 @@ static ARAMInfo g_ARAM; static DSPState g_dspState; static AudioDMA g_audioDMA; static ARAM_DMA g_arDMA; +static u32 last_mmaddr; +static u32 last_aram_dma_count; +static bool instant_dma; union ARAM_Info { @@ -195,6 +198,9 @@ void DoState(PointerWrap &p) p.Do(g_AR_MODE); p.Do(g_AR_REFRESH); p.Do(dsp_slice); + p.Do(last_mmaddr); + p.Do(last_aram_dma_count); + p.Do(instant_dma); dsp_emulator->DoState(p); } @@ -213,6 +219,12 @@ static void CompleteARAM(u64 userdata, int cyclesLate) GenerateDSPInterrupt(INT_ARAM); } +void EnableInstantDMA() +{ + CoreTiming::RemoveEvent(et_CompleteARAM); + CompleteARAM(0, 0); + instant_dma = true; +} DSPEmulator *GetDSPEmulator() { @@ -250,6 +262,11 @@ void Init(bool hle) g_AR_MODE = 1; // ARAM Controller has init'd g_AR_REFRESH = 156; // 156MHz + instant_dma = false; + + last_aram_dma_count = 0; + last_mmaddr = 0; + et_GenerateDSPInterrupt = CoreTiming::RegisterEvent("DSPint", GenerateDSPInterrupt); et_CompleteARAM = CoreTiming::RegisterEvent("ARAMint", CompleteARAM); } @@ -442,14 +459,6 @@ static void UpdateInterrupts() bool ints_set = (((g_dspState.DSPControl.Hex >> 1) & g_dspState.DSPControl.Hex & (INT_DSP | INT_ARAM | INT_AID)) != 0); ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_DSP, ints_set); - - if ((g_dspState.DSPControl.Hex >> 1) & g_dspState.DSPControl.Hex & INT_ARAM) - { - if (g_dspState.DSPControl.ARAM & g_dspState.DSPControl.ARAM_mask) - { - JitInterface::CompileExceptionCheck(JitInterface::EXCEPTIONS_ARAM_DMA); - } - } } static void GenerateDSPInterrupt(u64 DSPIntType, int cyclesLate) @@ -525,11 +534,20 @@ void UpdateAudioDMA() static void Do_ARAM_DMA() { g_dspState.DSPControl.DMAState = 1; - CoreTiming::ScheduleEvent_Threadsafe(0, et_CompleteARAM); - // Force an early exception check on large transfers (transfers longer than 250+ ticks). - // The shorter transfers are checked by dspARAMAddresses. Fixes RE2 audio. - CoreTiming::ForceExceptionCheck(250); + // ARAM DMA transfer rate has been measured on real hw + int ticksToTransfer = (g_arDMA.Cnt.count / 32) * 246; + + if (instant_dma) + ticksToTransfer = 0; + + CoreTiming::ScheduleEvent_Threadsafe(ticksToTransfer, et_CompleteARAM); + + if (instant_dma) + CoreTiming::ForceExceptionCheck(100); + + last_mmaddr = g_arDMA.MMAddr; + last_aram_dma_count = g_arDMA.Cnt.count; // Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks if (g_arDMA.Cnt.dir) @@ -655,5 +673,14 @@ u8 *GetARAMPtr() return g_ARAM.ptr; } +u64 DMAInProgress() +{ + if (g_dspState.DSPControl.DMAState == 1) + { + return ((u64)last_mmaddr << 32 | (last_mmaddr + last_aram_dma_count)); + } + return 0; +} + } // end of namespace DSP diff --git a/Source/Core/Core/HW/DSP.h b/Source/Core/Core/HW/DSP.h index ecbd04c8ea..6051857113 100644 --- a/Source/Core/Core/HW/DSP.h +++ b/Source/Core/Core/HW/DSP.h @@ -76,5 +76,7 @@ u8* GetARAMPtr(); void UpdateAudioDMA(); void UpdateDSPSlice(int cycles); +u64 DMAInProgress(); +void EnableInstantDMA(); }// end of namespace DSP diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_LoadStore.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_LoadStore.cpp index 5b8e6351ff..ee28c5f540 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_LoadStore.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_LoadStore.cpp @@ -5,6 +5,7 @@ #include "Common/CommonTypes.h" #include "Common/MathUtil.h" +#include "Core/HW/DSP.h" #include "Core/PowerPC/JitInterface.h" #include "Core/PowerPC/Interpreter/Interpreter.h" #include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h" @@ -325,24 +326,40 @@ void Interpreter::dcbf(UGeckoInstruction _inst) { NPC = PC + 12; }*/ - u32 address = Helper_Get_EA_X(_inst); - JitInterface::InvalidateICache(address & ~0x1f, 32); + u32 address = Helper_Get_EA_X(_inst); + JitInterface::InvalidateICache(address & ~0x1f, 32); } void Interpreter::dcbi(UGeckoInstruction _inst) { // Removes a block from data cache. Since we don't emulate the data cache, we don't need to do anything to the data cache // However, we invalidate the jit block cache on dcbi - u32 address = Helper_Get_EA_X(_inst); - JitInterface::InvalidateICache(address & ~0x1f, 32); + u32 address = Helper_Get_EA_X(_inst); + JitInterface::InvalidateICache(address & ~0x1f, 32); + + // The following detects a situation where the game is writing to the dcache at the address being DMA'd. As we do not + // have dcache emulation, invalid data is being DMA'd causing audio glitches. The following code detects this and + // enables the DMA to complete instantly before the invalid data is written. + u64 dma_in_progress = DSP::DMAInProgress(); + if (dma_in_progress != 0) + { + u32 start_addr = (dma_in_progress >> 32) & Memory::RAM_MASK; + u32 end_addr = (dma_in_progress & Memory::RAM_MASK) & 0xffffffff; + u32 invalidated_addr = (address & Memory::RAM_MASK) & ~0x1f; + + if (invalidated_addr >= start_addr && invalidated_addr <= end_addr) + { + DSP::EnableInstantDMA(); + } + } } void Interpreter::dcbst(UGeckoInstruction _inst) { // Cache line flush. Since we don't emulate the data cache, we don't need to do anything. // Invalidate the jit block cache on dcbst in case new code has been loaded via the data cache - u32 address = Helper_Get_EA_X(_inst); - JitInterface::InvalidateICache(address & ~0x1f, 32); + u32 address = Helper_Get_EA_X(_inst); + JitInterface::InvalidateICache(address & ~0x1f, 32); } void Interpreter::dcbt(UGeckoInstruction _inst) diff --git a/Source/Core/Core/PowerPC/JitInterface.cpp b/Source/Core/Core/PowerPC/JitInterface.cpp index 7562789846..a8986f7251 100644 --- a/Source/Core/Core/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/PowerPC/JitInterface.cpp @@ -251,19 +251,14 @@ namespace JitInterface exception_addresses = &jit->js.fifoWriteAddresses; break; } - case EXCEPTIONS_ARAM_DMA: - { - exception_addresses = &jit->js.dspARAMAddresses; - break; - } default: ERROR_LOG(POWERPC, "Unknown exception check type"); } if (PC != 0 && (exception_addresses->find(PC)) == (exception_addresses->end())) { - int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; - if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_STOREPS)) + int optype = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; + if (optype == OPTYPE_STORE || optype == OPTYPE_STOREFP || (optype == OPTYPE_STOREPS)) { exception_addresses->insert(PC); diff --git a/Source/Core/Core/PowerPC/JitInterface.h b/Source/Core/Core/PowerPC/JitInterface.h index 92a6533c44..53b2b38312 100644 --- a/Source/Core/Core/PowerPC/JitInterface.h +++ b/Source/Core/Core/PowerPC/JitInterface.h @@ -13,8 +13,7 @@ namespace JitInterface { enum { - EXCEPTIONS_FIFO_WRITE, - EXCEPTIONS_ARAM_DMA + EXCEPTIONS_FIFO_WRITE }; void DoState(PointerWrap &p);