From 86b6dfe4b38d6974f4ee4ab255eb89d5120f6d31 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 27 Sep 2014 20:47:29 +1000 Subject: [PATCH] Added a instant ARAM DMA mode which is enabled automatically when required. 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. Added accurate ARAM DMA transfer timing. Removed the addition of DSP exception checking. --- Source/Core/Core/HW/DSP.cpp | 51 ++++++++++++++----- Source/Core/Core/HW/DSP.h | 2 + .../Interpreter/Interpreter_LoadStore.cpp | 29 ++++++++--- Source/Core/Core/PowerPC/JitInterface.cpp | 9 +--- Source/Core/Core/PowerPC/JitInterface.h | 3 +- 5 files changed, 67 insertions(+), 27 deletions(-) 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);