From d09e2abb0d963d794b9ff04b17b161f68b61b84b Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 6 Sep 2014 18:59:38 +1000 Subject: [PATCH 1/6] Compile the ARAM DMA exception checks into the JIT block in a similar style to FIFO writes. This ensures that the ARAM DMA is handled soon after the DMA completes. Fixes issue 7122 and issue 7342. --- Source/Core/Core/HW/DSP.cpp | 30 +++++++++++--------- Source/Core/Core/PowerPC/Jit64/Jit.cpp | 12 ++++++-- Source/Core/Core/PowerPC/JitCommon/JitBase.h | 1 + 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/Source/Core/Core/HW/DSP.cpp b/Source/Core/Core/HW/DSP.cpp index 5b16f23139..82c7149c71 100644 --- a/Source/Core/Core/HW/DSP.cpp +++ b/Source/Core/Core/HW/DSP.cpp @@ -38,6 +38,7 @@ #include "Core/HW/MMIO.h" #include "Core/HW/ProcessorInterface.h" #include "Core/PowerPC/PowerPC.h" +#include "Core/PowerPC/JitCommon/JitBase.h" namespace DSP { @@ -441,6 +442,21 @@ 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_DSP | INT_ARAM | INT_AID)) + { + if (jit && PC != 0 && (jit->js.dspARAMAddresses.find(PC)) == (jit->js.dspARAMAddresses.end()) && (g_dspState.DSPControl.ARAM & g_dspState.DSPControl.ARAM_mask)) + { + int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; + if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_PS && GetOpInfo(Memory::ReadUnchecked_U32(PC))->opname == "psq_st")) + { + jit->js.dspARAMAddresses.insert(PC); + + // Invalidate the JIT block so that it gets recompiled with the external exception check included. + jit->GetBlockCache()->InvalidateICache(PC, 4); + } + } + } } static void GenerateDSPInterrupt(u64 DSPIntType, int cyclesLate) @@ -518,25 +534,11 @@ static void Do_ARAM_DMA() g_dspState.DSPControl.DMAState = 1; if (g_arDMA.Cnt.count == 32) { - // Beyond Good and Evil (GGEE41) sends count 32 - // Lost Kingdoms 2 needs the exception check here in DSP HLE mode CompleteARAM(0, 0); - CoreTiming::ForceExceptionCheck(100); } else { CoreTiming::ScheduleEvent_Threadsafe(0, et_CompleteARAM); - - // Force an early exception check on large transfers. Fixes RE2 audio. - // NFS:HP2 (<= 6144) - // Viewtiful Joe (<= 6144) - // Sonic Mega Collection (> 2048) - // Paper Mario battles (> 32) - // Mario Super Baseball (> 32) - // Knockout Kings 2003 loading (> 32) - // WWE DOR (> 32) - if (g_arDMA.Cnt.count > 2048 && g_arDMA.Cnt.count <= 6144) - CoreTiming::ForceExceptionCheck(100); } // Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks diff --git a/Source/Core/Core/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/PowerPC/Jit64/Jit.cpp index 9b88538935..89a61e59d3 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit.cpp @@ -697,7 +697,8 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc } // Add an external exception check if the instruction writes to the FIFO. - if (jit->js.fifoWriteAddresses.find(ops[i].address) != jit->js.fifoWriteAddresses.end()) + if (jit->js.fifoWriteAddresses.find(ops[i].address) != jit->js.fifoWriteAddresses.end() || + jit->js.dspARAMAddresses.find(ops[i].address) != jit->js.dspARAMAddresses.end()) { TEST(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_ISI | EXCEPTION_PROGRAM | EXCEPTION_SYSCALL | EXCEPTION_FPU_UNAVAILABLE | EXCEPTION_DSI | EXCEPTION_ALIGNMENT)); FixupBranch clearInt = J_CC(CC_NZ); @@ -707,7 +708,14 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc SetJumpTarget(extException); TEST(32, PPCSTATE(msr), Imm32(0x0008000)); FixupBranch noExtIntEnable = J_CC(CC_Z, true); - TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_CP | ProcessorInterface::INT_CAUSE_PE_TOKEN | ProcessorInterface::INT_CAUSE_PE_FINISH)); + if (jit->js.fifoWriteAddresses.find(ops[i].address) != jit->js.fifoWriteAddresses.end()) + { + TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_CP | ProcessorInterface::INT_CAUSE_PE_TOKEN | ProcessorInterface::INT_CAUSE_PE_FINISH)); + } + else + { + TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_DSP)); + } FixupBranch noCPInt = J_CC(CC_Z, true); gpr.Flush(FLUSH_MAINTAIN_STATE); diff --git a/Source/Core/Core/PowerPC/JitCommon/JitBase.h b/Source/Core/Core/PowerPC/JitCommon/JitBase.h index 52463ec619..fc40d3cfd0 100644 --- a/Source/Core/Core/PowerPC/JitCommon/JitBase.h +++ b/Source/Core/Core/PowerPC/JitCommon/JitBase.h @@ -97,6 +97,7 @@ protected: JitBlock *curBlock; std::unordered_set fifoWriteAddresses; + std::unordered_set dspARAMAddresses; }; PPCAnalyst::CodeBlock code_block; From 0f256715e0f41661bb3d28fa47b3de1acd48e663 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 7 Sep 2014 12:50:08 +1000 Subject: [PATCH 2/6] Re-added the ARAM DMA exception check. This fixes the audio cutting in and out of Resident Evil 2 and 3. Removed the special case for short transfers as it is no longer required. --- Source/Core/Core/HW/DSP.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Source/Core/Core/HW/DSP.cpp b/Source/Core/Core/HW/DSP.cpp index 82c7149c71..5eed344331 100644 --- a/Source/Core/Core/HW/DSP.cpp +++ b/Source/Core/Core/HW/DSP.cpp @@ -532,14 +532,10 @@ void UpdateAudioDMA() static void Do_ARAM_DMA() { g_dspState.DSPControl.DMAState = 1; - if (g_arDMA.Cnt.count == 32) - { - CompleteARAM(0, 0); - } - else - { - CoreTiming::ScheduleEvent_Threadsafe(0, et_CompleteARAM); - } + CoreTiming::ScheduleEvent_Threadsafe(0, et_CompleteARAM); + + // Force an early exception check on large transfers. Fixes RE2 audio. + CoreTiming::ForceExceptionCheck(250); // Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks if (g_arDMA.Cnt.dir) From 945d431171cc2814a5ec8bcc18d68f2b75912bdf Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 7 Sep 2014 15:46:29 +1000 Subject: [PATCH 3/6] Added OPTYPE_LOADPS and OPTYPE_STOREPS instruction types to the PPC table. Updated ARAM DMA and FIFO write exception checking to uses these types. Conflicts: Source/Core/Core/PowerPC/Interpreter/Interpreter_Tables.cpp Source/Core/Core/PowerPC/PPCTables.h --- Source/Core/Core/HW/DSP.cpp | 5 +++-- Source/Core/Core/HW/GPFifo.cpp | 2 +- .../Core/Core/PowerPC/Interpreter/Interpreter_Tables.cpp | 8 ++++---- Source/Core/Core/PowerPC/PPCTables.h | 3 +++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Source/Core/Core/HW/DSP.cpp b/Source/Core/Core/HW/DSP.cpp index 5eed344331..30051c7fc8 100644 --- a/Source/Core/Core/HW/DSP.cpp +++ b/Source/Core/Core/HW/DSP.cpp @@ -448,7 +448,7 @@ static void UpdateInterrupts() if (jit && PC != 0 && (jit->js.dspARAMAddresses.find(PC)) == (jit->js.dspARAMAddresses.end()) && (g_dspState.DSPControl.ARAM & g_dspState.DSPControl.ARAM_mask)) { int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; - if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_PS && GetOpInfo(Memory::ReadUnchecked_U32(PC))->opname == "psq_st")) + if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_STOREPS)) { jit->js.dspARAMAddresses.insert(PC); @@ -534,7 +534,8 @@ static void Do_ARAM_DMA() g_dspState.DSPControl.DMAState = 1; CoreTiming::ScheduleEvent_Threadsafe(0, et_CompleteARAM); - // Force an early exception check on large transfers. Fixes RE2 audio. + // 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); // Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks diff --git a/Source/Core/Core/HW/GPFifo.cpp b/Source/Core/Core/HW/GPFifo.cpp index 3643f88fca..01f3d1e40a 100644 --- a/Source/Core/Core/HW/GPFifo.cpp +++ b/Source/Core/Core/HW/GPFifo.cpp @@ -90,7 +90,7 @@ void STACKALIGN CheckGatherPipe() { // Log only stores, fp stores and ps stores, filtering out other instructions arrived via optimizeGatherPipe int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; - if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_PS && !strcmp(GetOpInfo(Memory::ReadUnchecked_U32(PC))->opname, "psq_st"))) + if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || type == OPTYPE_STOREPS) { jit->js.fifoWriteAddresses.insert(PC); diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Tables.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Tables.cpp index 6d60ab2a64..b98b90e013 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Tables.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Tables.cpp @@ -79,10 +79,10 @@ static GekkoOPTemplate primarytable[] = {54, Interpreter::stfd, {"stfd", OPTYPE_STOREFP, FL_IN_FLOAT_S | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, {55, Interpreter::stfdu, {"stfdu", OPTYPE_STOREFP, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, - {56, Interpreter::psq_l, {"psq_l", OPTYPE_PS, FL_OUT_FLOAT_S | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, - {57, Interpreter::psq_lu, {"psq_lu", OPTYPE_PS, FL_OUT_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, - {60, Interpreter::psq_st, {"psq_st", OPTYPE_PS, FL_IN_FLOAT_S | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, - {61, Interpreter::psq_stu, {"psq_stu", OPTYPE_PS, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, + {56, Interpreter::psq_l, {"psq_l", OPTYPE_LOADPS, FL_OUT_FLOAT_S | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, + {57, Interpreter::psq_lu, {"psq_lu", OPTYPE_LOADPS, FL_OUT_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, + {60, Interpreter::psq_st, {"psq_st", OPTYPE_STOREPS, FL_IN_FLOAT_S | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, + {61, Interpreter::psq_stu, {"psq_stu", OPTYPE_STOREPS, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}}, //missing: 0, 5, 6, 9, 22, 30, 62, 58 {0, Interpreter::unknown_instruction, {"unknown_instruction", OPTYPE_UNKNOWN, 0, 0, 0, 0, 0}}, diff --git a/Source/Core/Core/PowerPC/PPCTables.h b/Source/Core/Core/PowerPC/PPCTables.h index bfc5c5303a..ea6c2ac978 100644 --- a/Source/Core/Core/PowerPC/PPCTables.h +++ b/Source/Core/Core/PowerPC/PPCTables.h @@ -70,6 +70,9 @@ enum OPTYPE_STOREFP , OPTYPE_DOUBLEFP, OPTYPE_SINGLEFP, + OPTYPE_LOADPS , + OPTYPE_STOREPS , + OPTYPE_FPU , OPTYPE_PS , OPTYPE_DCACHE , OPTYPE_ICACHE , From 4b37fdfa45811bceac9681ea6e0082507cbbeaa6 Mon Sep 17 00:00:00 2001 From: skidau Date: Mon, 8 Sep 2014 00:40:43 +1000 Subject: [PATCH 4/6] Added a CompileExceptionCheck function to the JitInterface and re-routed the existing code to utilise the interface. --- Source/Core/Core/HW/DSP.cpp | 15 +++------ Source/Core/Core/HW/GPFifo.cpp | 15 ++------- Source/Core/Core/PowerPC/JitInterface.cpp | 38 ++++++++++++++++++++++- Source/Core/Core/PowerPC/JitInterface.h | 10 +++++- Source/Core/Core/PowerPC/PPCAnalyst.cpp | 2 +- 5 files changed, 53 insertions(+), 27 deletions(-) diff --git a/Source/Core/Core/HW/DSP.cpp b/Source/Core/Core/HW/DSP.cpp index 30051c7fc8..c8413658e4 100644 --- a/Source/Core/Core/HW/DSP.cpp +++ b/Source/Core/Core/HW/DSP.cpp @@ -37,8 +37,8 @@ #include "Core/HW/Memmap.h" #include "Core/HW/MMIO.h" #include "Core/HW/ProcessorInterface.h" +#include "Core/PowerPC/JitInterface.h" #include "Core/PowerPC/PowerPC.h" -#include "Core/PowerPC/JitCommon/JitBase.h" namespace DSP { @@ -443,18 +443,11 @@ static void UpdateInterrupts() ProcessorInterface::SetInterrupt(ProcessorInterface::INT_CAUSE_DSP, ints_set); - if ((g_dspState.DSPControl.Hex >> 1) & g_dspState.DSPControl.Hex & (INT_DSP | INT_ARAM | INT_AID)) + if ((g_dspState.DSPControl.Hex >> 1) & g_dspState.DSPControl.Hex & INT_ARAM) { - if (jit && PC != 0 && (jit->js.dspARAMAddresses.find(PC)) == (jit->js.dspARAMAddresses.end()) && (g_dspState.DSPControl.ARAM & g_dspState.DSPControl.ARAM_mask)) + if (g_dspState.DSPControl.ARAM & g_dspState.DSPControl.ARAM_mask) { - int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; - if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_STOREPS)) - { - jit->js.dspARAMAddresses.insert(PC); - - // Invalidate the JIT block so that it gets recompiled with the external exception check included. - jit->GetBlockCache()->InvalidateICache(PC, 4); - } + JitInterface::CompileExceptionCheck(JitInterface::EXCEPTIONS_ARAM_DMA); } } } diff --git a/Source/Core/Core/HW/GPFifo.cpp b/Source/Core/Core/HW/GPFifo.cpp index 01f3d1e40a..033d253510 100644 --- a/Source/Core/Core/HW/GPFifo.cpp +++ b/Source/Core/Core/HW/GPFifo.cpp @@ -8,8 +8,8 @@ #include "Core/HW/GPFifo.h" #include "Core/HW/Memmap.h" #include "Core/HW/ProcessorInterface.h" +#include "Core/PowerPC/JitInterface.h" #include "Core/PowerPC/PowerPC.h" -#include "Core/PowerPC/JitCommon/JitBase.h" #include "VideoCommon/VideoBackendBase.h" @@ -86,18 +86,7 @@ void STACKALIGN CheckGatherPipe() memmove(m_gatherPipe, m_gatherPipe + cnt, m_gatherPipeCount); // Profile where the FIFO writes are occurring. - if (jit && PC != 0 && (jit->js.fifoWriteAddresses.find(PC)) == (jit->js.fifoWriteAddresses.end())) - { - // Log only stores, fp stores and ps stores, filtering out other instructions arrived via optimizeGatherPipe - int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; - if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || type == OPTYPE_STOREPS) - { - jit->js.fifoWriteAddresses.insert(PC); - - // Invalidate the JIT block so that it gets recompiled with the external exception check included. - jit->GetBlockCache()->InvalidateICache(PC, 4); - } - } + JitInterface::CompileExceptionCheck(JitInterface::EXCEPTIONS_FIFO_WRITE); } } diff --git a/Source/Core/Core/PowerPC/JitInterface.cpp b/Source/Core/Core/PowerPC/JitInterface.cpp index 6bfd1c4009..7562789846 100644 --- a/Source/Core/Core/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/PowerPC/JitInterface.cpp @@ -216,7 +216,7 @@ namespace JitInterface jit->GetBlockCache()->InvalidateICache(address, size); } - u32 Read_Opcode_JIT(u32 _Address) + u32 ReadOpcodeJIT(u32 _Address) { if (bMMU && !bFakeVMEM && (_Address & Memory::ADDR_MASK_MEM1)) { @@ -237,6 +237,42 @@ namespace JitInterface return inst; } + void CompileExceptionCheck(int type) + { + if (!jit) + return; + + std::unordered_set *exception_addresses; + + switch (type) + { + case EXCEPTIONS_FIFO_WRITE: + { + 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)) + { + exception_addresses->insert(PC); + + // Invalidate the JIT block so that it gets recompiled with the external exception check included. + jit->GetBlockCache()->InvalidateICache(PC, 4); + } + } + } + void Shutdown() { if (jit) diff --git a/Source/Core/Core/PowerPC/JitInterface.h b/Source/Core/Core/PowerPC/JitInterface.h index a8ed783726..92a6533c44 100644 --- a/Source/Core/Core/PowerPC/JitInterface.h +++ b/Source/Core/Core/PowerPC/JitInterface.h @@ -11,6 +11,12 @@ namespace JitInterface { + enum + { + EXCEPTIONS_FIFO_WRITE, + EXCEPTIONS_ARAM_DMA + }; + void DoState(PointerWrap &p); CPUCoreBase *InitJitCore(int core); @@ -24,7 +30,7 @@ namespace JitInterface bool HandleFault(uintptr_t access_address, SContext* ctx); // used by JIT to read instructions - u32 Read_Opcode_JIT(const u32 _Address); + u32 ReadOpcodeJIT(const u32 _Address); // Clearing CodeCache void ClearCache(); @@ -33,6 +39,8 @@ namespace JitInterface void InvalidateICache(u32 address, u32 size); + void CompileExceptionCheck(int type); + void Shutdown(); } extern bool bMMU; diff --git a/Source/Core/Core/PowerPC/PPCAnalyst.cpp b/Source/Core/Core/PowerPC/PPCAnalyst.cpp index 6cd1043c38..923e621dcd 100644 --- a/Source/Core/Core/PowerPC/PPCAnalyst.cpp +++ b/Source/Core/Core/PowerPC/PPCAnalyst.cpp @@ -654,7 +654,7 @@ u32 PPCAnalyzer::Analyze(u32 address, CodeBlock *block, CodeBuffer *buffer, u32 for (u32 i = 0; i < blockSize; ++i) { - UGeckoInstruction inst = JitInterface::Read_Opcode_JIT(address); + UGeckoInstruction inst = JitInterface::ReadOpcodeJIT(address); if (inst.hex != 0) { From 86b6dfe4b38d6974f4ee4ab255eb89d5120f6d31 Mon Sep 17 00:00:00 2001 From: skidau Date: Sat, 27 Sep 2014 20:47:29 +1000 Subject: [PATCH 5/6] 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); From 718401909016c8c652c4ee276b9c0f094564d4e9 Mon Sep 17 00:00:00 2001 From: skidau Date: Sun, 28 Sep 2014 11:51:14 +1000 Subject: [PATCH 6/6] Increased the savestate internal version. Added a small note for instant dma. --- Source/Core/Core/PowerPC/Interpreter/Interpreter_LoadStore.cpp | 2 +- Source/Core/Core/State.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_LoadStore.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_LoadStore.cpp index ee28c5f540..86d42e3239 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_LoadStore.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_LoadStore.cpp @@ -339,7 +339,7 @@ void Interpreter::dcbi(UGeckoInstruction _inst) // 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. + // enables the DMA to complete instantly before the invalid data is written. Resident Evil 2 & 3 trigger this. u64 dma_in_progress = DSP::DMAInProgress(); if (dma_in_progress != 0) { diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 737a3eb50f..8d05d01fbc 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -63,7 +63,7 @@ static Common::Event g_compressAndDumpStateSyncEvent; static std::thread g_save_thread; // Don't forget to increase this after doing changes on the savestate system -static const u32 STATE_VERSION = 34; +static const u32 STATE_VERSION = 35; enum {