diff --git a/Source/Core/Common/Src/BreakPoints.cpp b/Source/Core/Common/Src/BreakPoints.cpp index 45a0b52daf..1700276d85 100644 --- a/Source/Core/Common/Src/BreakPoints.cpp +++ b/Source/Core/Common/Src/BreakPoints.cpp @@ -18,6 +18,7 @@ #include "Common.h" #include "DebugInterface.h" #include "BreakPoints.h" +#include "../../Core/Src/PowerPC/JitCommon/JitBase.h" #include bool BreakPoints::IsAddressBreakPoint(u32 _iAddress) @@ -70,7 +71,11 @@ void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) void BreakPoints::Add(const TBreakPoint& bp) { if (!IsAddressBreakPoint(bp.iAddress)) + { m_BreakPoints.push_back(bp); + if (jit) + jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); + } } void BreakPoints::Add(u32 em_address, bool temp) @@ -83,21 +88,35 @@ void BreakPoints::Add(u32 em_address, bool temp) pt.iAddress = em_address; m_BreakPoints.push_back(pt); + + if (jit) + jit->GetBlockCache()->InvalidateICache(em_address, 4); } } -void BreakPoints::Remove(u32 _iAddress) +void BreakPoints::Remove(u32 em_address) { for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i) { - if (i->iAddress == _iAddress) + if (i->iAddress == em_address) { m_BreakPoints.erase(i); + if (jit) + jit->GetBlockCache()->InvalidateICache(em_address, 4); return; } } } +void BreakPoints::Clear() +{ + for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i) + { + if (jit) + jit->GetBlockCache()->InvalidateICache(i->iAddress, 4); + m_BreakPoints.erase(i); + } +} MemChecks::TMemChecksStr MemChecks::GetStrings() const { diff --git a/Source/Core/Common/Src/BreakPoints.h b/Source/Core/Common/Src/BreakPoints.h index a97452cde6..c742f812a7 100644 --- a/Source/Core/Common/Src/BreakPoints.h +++ b/Source/Core/Common/Src/BreakPoints.h @@ -78,7 +78,7 @@ public: // Remove Breakpoint void Remove(u32 _iAddress); - void Clear() { m_BreakPoints.clear(); }; + void Clear(); void DeleteByAddress(u32 _Address); diff --git a/Source/Core/Common/Src/VideoBackendBase.h b/Source/Core/Common/Src/VideoBackendBase.h index 28c8f23246..96f639832b 100644 --- a/Source/Core/Common/Src/VideoBackendBase.h +++ b/Source/Core/Common/Src/VideoBackendBase.h @@ -119,6 +119,7 @@ public: virtual void Video_GatherPipeBursted() = 0; virtual bool Video_IsPossibleWaitingSetDrawDone() = 0; + virtual bool Video_IsHiWatermarkActive() = 0; virtual void Video_AbortFrame() = 0; virtual readFn16 Video_CPRead16() = 0; @@ -159,6 +160,7 @@ class VideoBackendHardware : public VideoBackend void Video_GatherPipeBursted(); bool Video_IsPossibleWaitingSetDrawDone(); + bool Video_IsHiWatermarkActive(); void Video_AbortFrame(); readFn16 Video_CPRead16(); diff --git a/Source/Core/Core/Src/HW/GPFifo.cpp b/Source/Core/Core/Src/HW/GPFifo.cpp index 4d72317e0d..3af387792f 100644 --- a/Source/Core/Core/Src/HW/GPFifo.cpp +++ b/Source/Core/Core/Src/HW/GPFifo.cpp @@ -19,9 +19,9 @@ #include "ChunkFile.h" #include "ProcessorInterface.h" #include "Memmap.h" -#include "../PowerPC/PowerPC.h" - #include "VideoBackendBase.h" +#include "../PowerPC/JitCommon/JitBase.h" +#include "../PowerPC/PowerPC.h" #include "GPFifo.h" @@ -96,6 +96,15 @@ void STACKALIGN CheckGatherPipe() // move back the spill bytes memmove(m_gatherPipe, m_gatherPipe + cnt, m_gatherPipeCount); + + // Profile where the FIFO writes are occurring. + if (jit && (jit->js.fifoWriteAddresses.find(PC)) == (jit->js.fifoWriteAddresses.end())) + { + 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); + } } } diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStore.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStore.cpp index a7955c0982..cad4761e55 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStore.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStore.cpp @@ -367,7 +367,7 @@ void Interpreter::dcbf(UGeckoInstruction _inst) if (jit) { u32 address = Helper_Get_EA_X(_inst); - jit->GetBlockCache()->InvalidateICache(address & ~0x1f); + jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32); } } @@ -378,7 +378,7 @@ void Interpreter::dcbi(UGeckoInstruction _inst) if (jit) { u32 address = Helper_Get_EA_X(_inst); - jit->GetBlockCache()->InvalidateICache(address & ~0x1f); + jit->GetBlockCache()->InvalidateICache(address & ~0x1f, 32); } } diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp index df1fd0f589..15bd5ca18a 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp @@ -414,8 +414,8 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc // Comment out the following to disable breakpoints (speed-up) if (!Profiler::g_ProfileBlocks) { - blockSize = 1; - broken_block = true; + if (GetState() == CPU_STEPPING) + blockSize = 1; Trace(); } } diff --git a/Source/Core/Core/Src/PowerPC/Jit64/JitAsm.cpp b/Source/Core/Core/Src/PowerPC/Jit64/JitAsm.cpp index b9aaa2df6a..04462872e6 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/JitAsm.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/JitAsm.cpp @@ -81,12 +81,15 @@ void Jit64AsmRoutineManager::Generate() if (Core::g_CoreStartupParameter.bEnableDebugging) { + TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(PowerPC::CPU_STEPPING)); + FixupBranch notStepping = J_CC(CC_Z); ABI_CallFunction(reinterpret_cast(&PowerPC::CheckBreakPoints)); TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF)); FixupBranch noBreakpoint = J_CC(CC_Z); ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); SetJumpTarget(noBreakpoint); + SetJumpTarget(notStepping); } SetJumpTarget(skipToRealDispatch); diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Integer.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Integer.cpp index 4980b4d7a4..5aaf4b99f1 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Integer.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Integer.cpp @@ -266,7 +266,7 @@ void Jit64::reg_imm(UGeckoInstruction inst) case 15: if (a == 0) { // lis // Merge with next instruction if loading a 32-bits immediate value (lis + addi, lis + ori) - if (!js.isLastInstruction) { + if (!js.isLastInstruction && !Core::g_CoreStartupParameter.bEnableDebugging) { if ((js.next_inst.OPCD == 14) && (js.next_inst.RD == d) && (js.next_inst.RA == d)) { // addi gpr.SetImmediate32(d, ((u32)inst.SIMM_16 << 16) + (u32)(s32)js.next_inst.SIMM_16); js.downcountAmount++; diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.cpp index 8ca83e7208..13c4b02f4d 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.cpp @@ -1260,8 +1260,8 @@ static const std::string opcodeNames[] = { "FResult_End", "StorePaired", "StoreSingle", "StoreDouble", "StoreFReg", "FDCmpCR", "CInt16", "CInt32", "SystemCall", "RFIExit", "InterpreterBranch", "IdleBranch", "ShortIdleLoop", - "FPExceptionCheckStart", "FPExceptionCheckEnd", "ISIException", "Tramp", - "BlockStart", "BlockEnd", "Int3", + "FPExceptionCheckStart", "FPExceptionCheckEnd", "ISIException", "ExtExceptionCheck", + "Tramp", "BlockStart", "BlockEnd", "Int3", }; static const unsigned alwaysUsedList[] = { InterpreterFallback, StoreGReg, StoreCR, StoreLink, StoreCTR, StoreMSR, @@ -1269,7 +1269,8 @@ static const unsigned alwaysUsedList[] = { Store16, Store32, StoreSingle, StoreDouble, StorePaired, StoreFReg, FDCmpCR, BlockStart, BlockEnd, IdleBranch, BranchCond, BranchUncond, ShortIdleLoop, SystemCall, InterpreterBranch, RFIExit, FPExceptionCheckStart, - FPExceptionCheckEnd, ISIException, Int3, Tramp, Nop + FPExceptionCheckEnd, ISIException, ExtExceptionCheck, BreakPointCheck, + Int3, Tramp, Nop }; static const unsigned extra8RegList[] = { LoadGReg, LoadCR, LoadGQR, LoadFReg, LoadFRegDENToZero, diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h index aca3dd0e37..f41742b01c 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h @@ -165,10 +165,10 @@ enum Opcode { ShortIdleLoop, // Idle loop seen in homebrew like wii mahjong, // just a branch - // used for MMU, at least until someone + // used for exception checking, at least until someone // has a better idea of integrating it FPExceptionCheckStart, FPExceptionCheckEnd, - ISIException, + ISIException, ExtExceptionCheck, BreakPointCheck, // "Opcode" representing a register too far away to // reference directly; this is a size optimization Tramp, @@ -411,6 +411,12 @@ public: InstLoc EmitISIException(InstLoc dest) { return EmitUOp(ISIException, dest); } + InstLoc EmitExtExceptionCheck(InstLoc pc) { + return EmitUOp(ExtExceptionCheck, pc); + } + InstLoc EmitBreakPointCheck(InstLoc pc) { + return EmitUOp(BreakPointCheck, pc); + } InstLoc EmitRFIExit() { return FoldZeroOp(RFIExit, 0); } diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp index e1807cfbb4..7579c69a03 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp @@ -50,6 +50,7 @@ The register allocation is linear scan allocation. #include "../../../../Common/Src/CPUDetect.h" #include "MathUtil.h" #include "../../Core.h" +#include "HW/ProcessorInterface.h" static ThunkManager thunks; @@ -761,6 +762,8 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak case FPExceptionCheckStart: case FPExceptionCheckEnd: case ISIException: + case ExtExceptionCheck: + case BreakPointCheck: case Int3: case Tramp: // No liveness effects @@ -1920,6 +1923,38 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak Jit->WriteExceptionExit(); break; } + case ExtExceptionCheck: { + unsigned InstLoc = ibuild->GetImmValue(getOp1(I)); + + Jit->TEST(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_ISI | EXCEPTION_PROGRAM | EXCEPTION_SYSCALL | EXCEPTION_FPU_UNAVAILABLE | EXCEPTION_DSI | EXCEPTION_ALIGNMENT | EXCEPTION_DECREMENTER)); + FixupBranch clearInt = Jit->J_CC(CC_NZ); + Jit->TEST(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_EXTERNAL_INT)); + FixupBranch noExtException = Jit->J_CC(CC_Z); + Jit->TEST(32, M((void *)&PowerPC::ppcState.msr), Imm32(0x0008000)); + FixupBranch noExtIntEnable = Jit->J_CC(CC_Z); + Jit->TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_CP || ProcessorInterface::INT_CAUSE_PE_TOKEN || ProcessorInterface::INT_CAUSE_PE_FINISH)); + FixupBranch noCPInt = Jit->J_CC(CC_Z); + + Jit->MOV(32, M(&PC), Imm32(InstLoc)); + Jit->WriteExceptionExit(); + + Jit->SetJumpTarget(noCPInt); + Jit->SetJumpTarget(noExtIntEnable); + Jit->SetJumpTarget(noExtException); + Jit->SetJumpTarget(clearInt); + break; + } + case BreakPointCheck: { + unsigned InstLoc = ibuild->GetImmValue(getOp1(I)); + + Jit->MOV(32, M(&PC), Imm32(InstLoc)); + Jit->ABI_CallFunction(reinterpret_cast(&PowerPC::CheckBreakPoints)); + Jit->TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF)); + FixupBranch noBreakpoint = Jit->J_CC(CC_Z); + Jit->WriteExit(InstLoc, 0); + Jit->SetJumpTarget(noBreakpoint); + break; + } case Int3: { Jit->INT3(); break; diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp index 77c7fb6169..0c6fc41366 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp @@ -526,7 +526,8 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc // Comment out the following to disable breakpoints (speed-up) if (!Profiler::g_ProfileBlocks) { - blockSize = 1; + if (GetState() == CPU_STEPPING) + blockSize = 1; Trace(); } } diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitILAsm.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/JitILAsm.cpp index da94c881e3..ac533dd473 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitILAsm.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitILAsm.cpp @@ -83,12 +83,15 @@ void JitILAsmRoutineManager::Generate() if (Core::g_CoreStartupParameter.bEnableDebugging) { + TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(PowerPC::CPU_STEPPING)); + FixupBranch notStepping = J_CC(CC_Z); ABI_CallFunction(reinterpret_cast(&PowerPC::CheckBreakPoints)); TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF)); FixupBranch noBreakpoint = J_CC(CC_Z); ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); SetJumpTarget(noBreakpoint); + SetJumpTarget(notStepping); } SetJumpTarget(skipToRealDispatch); diff --git a/Source/Core/Core/Src/PowerPC/JitCommon/JitBase.cpp b/Source/Core/Core/Src/PowerPC/JitCommon/JitBase.cpp index 7f6cac8dd3..6203a81e15 100644 --- a/Source/Core/Core/Src/PowerPC/JitCommon/JitBase.cpp +++ b/Source/Core/Core/Src/PowerPC/JitCommon/JitBase.cpp @@ -13,6 +13,8 @@ // If not, see http://www.gnu.org/licenses/ #include "JitBase.h" +#include "PowerPCDisasm.h" +#include "disasm.h" JitBase *jit; diff --git a/Source/Core/Core/Src/PowerPC/JitCommon/JitBase.h b/Source/Core/Core/Src/PowerPC/JitCommon/JitBase.h index b832e96e1f..c2febde722 100644 --- a/Source/Core/Core/Src/PowerPC/JitCommon/JitBase.h +++ b/Source/Core/Core/Src/PowerPC/JitCommon/JitBase.h @@ -28,8 +28,7 @@ #include "JitBackpatch.h" // for EmuCodeBlock #include "JitAsmCommon.h" -#include "PowerPCDisasm.h" -#include "disasm.h" +#include #define JIT_OPCODE 0 @@ -75,6 +74,8 @@ protected: u8* rewriteStart; JitBlock *curBlock; + + std::set fifoWriteAddresses; }; public: diff --git a/Source/Core/Core/Src/PowerPC/JitCommon/JitCache.cpp b/Source/Core/Core/Src/PowerPC/JitCommon/JitCache.cpp index 422369d307..8cc85e3284 100644 --- a/Source/Core/Core/Src/PowerPC/JitCommon/JitCache.cpp +++ b/Source/Core/Core/Src/PowerPC/JitCommon/JitCache.cpp @@ -353,6 +353,24 @@ bool JitBlock::ContainsAddress(u32 em_address) } } + void JitBlockCache::UnlinkBlock(int i) + { + JitBlock &b = blocks[i]; + std::map::iterator iter; + pair::iterator, multimap::iterator> ppp; + ppp = links_to.equal_range(b.originalAddress); + if (ppp.first == ppp.second) + return; + for (multimap::iterator iter2 = ppp.first; iter2 != ppp.second; ++iter2) { + JitBlock &sourceBlock = blocks[iter2->second]; + for (int e = 0; e < 2; e++) + { + if (sourceBlock.exitAddress[e] == b.originalAddress) + sourceBlock.linkStatus[e] = false; + } + } + } + void JitBlockCache::DestroyBlock(int block_num, bool invalidate) { if (block_num < 0 || block_num >= num_blocks) @@ -375,7 +393,9 @@ bool JitBlock::ContainsAddress(u32 em_address) Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress); #endif - // We don't unlink blocks, we just send anyone who tries to run them back to the dispatcher. + UnlinkBlock(block_num); + + // Send anyone who tries to run this block back to the dispatcher. // Not entirely ideal, but .. pretty good. // Spurious entrances from previously linked blocks can only come through checkedEntry XEmitter emit((u8 *)b.checkedEntry); @@ -390,14 +410,31 @@ bool JitBlock::ContainsAddress(u32 em_address) } - void JitBlockCache::InvalidateICache(u32 address) + void JitBlockCache::InvalidateICache(u32 address, const u32 length) { - address &= ~0x1f; // destroy JIT blocks // !! this works correctly under assumption that any two overlapping blocks end at the same address std::map, u32>::iterator it1 = block_map.lower_bound(std::make_pair(address, 0)), it2 = it1, it; - while (it2 != block_map.end() && it2->first.second < address + 0x20) + while (it2 != block_map.end() && it2->first.second < address + length) { +#ifdef JIT_UNLIMITED_ICACHE + JitBlock &b = blocks[it2->second]; + if (b.originalAddress & JIT_ICACHE_VMEM_BIT) + { + u32 cacheaddr = b.originalAddress & JIT_ICACHE_MASK; + memset(iCacheVMEM + cacheaddr, JIT_ICACHE_INVALID_BYTE, 4); + } + else if (b.originalAddress & JIT_ICACHE_EXRAM_BIT) + { + u32 cacheaddr = b.originalAddress & JIT_ICACHEEX_MASK; + memset(iCacheEx + cacheaddr, JIT_ICACHE_INVALID_BYTE, 4); + } + else + { + u32 cacheaddr = b.originalAddress & JIT_ICACHE_MASK; + memset(iCache + cacheaddr, JIT_ICACHE_INVALID_BYTE, 4); + } +#endif DestroyBlock(it2->second, true); it2++; } @@ -418,17 +455,17 @@ bool JitBlock::ContainsAddress(u32 em_address) if (address & JIT_ICACHE_VMEM_BIT) { u32 cacheaddr = address & JIT_ICACHE_MASK; - memset(iCacheVMEM + cacheaddr, JIT_ICACHE_INVALID_BYTE, 32); + memset(iCacheVMEM + cacheaddr, JIT_ICACHE_INVALID_BYTE, length); } else if (address & JIT_ICACHE_EXRAM_BIT) { u32 cacheaddr = address & JIT_ICACHEEX_MASK; - memset(iCacheEx + cacheaddr, JIT_ICACHE_INVALID_BYTE, 32); + memset(iCacheEx + cacheaddr, JIT_ICACHE_INVALID_BYTE, length); } else { u32 cacheaddr = address & JIT_ICACHE_MASK; - memset(iCache + cacheaddr, JIT_ICACHE_INVALID_BYTE, 32); + memset(iCache + cacheaddr, JIT_ICACHE_INVALID_BYTE, length); } #endif } diff --git a/Source/Core/Core/Src/PowerPC/JitCommon/JitCache.h b/Source/Core/Core/Src/PowerPC/JitCommon/JitCache.h index 91d47a3d0e..16f16d6d14 100644 --- a/Source/Core/Core/Src/PowerPC/JitCommon/JitCache.h +++ b/Source/Core/Core/Src/PowerPC/JitCommon/JitCache.h @@ -87,6 +87,7 @@ class JitBlockCache bool RangeIntersect(int s1, int e1, int s2, int e2) const; void LinkBlockExits(int i); void LinkBlock(int i); + void UnlinkBlock(int i); public: JitBlockCache() : @@ -129,7 +130,7 @@ public: CompiledCode GetCompiledCodeFromBlock(int block_num); // DOES NOT WORK CORRECTLY WITH INLINING - void InvalidateICache(u32 em_address); + void InvalidateICache(u32 address, const u32 length); void DestroyBlock(int block_num, bool invalidate); // Not currently used diff --git a/Source/Core/Core/Src/PowerPC/PPCCache.cpp b/Source/Core/Core/Src/PowerPC/PPCCache.cpp index 704caa4a2c..6956e9db1c 100644 --- a/Source/Core/Core/Src/PowerPC/PPCCache.cpp +++ b/Source/Core/Core/Src/PowerPC/PPCCache.cpp @@ -110,7 +110,7 @@ namespace PowerPC #endif valid[set] = 0; if (jit) - jit->GetBlockCache()->InvalidateICache(addr); + jit->GetBlockCache()->InvalidateICache(addr & ~0x1f, 32); } u32 InstructionCache::ReadInstruction(u32 addr) diff --git a/Source/Core/DolphinWX/Src/Debugger/CodeWindow.cpp b/Source/Core/DolphinWX/Src/Debugger/CodeWindow.cpp index 37ff81772a..0b6b366c88 100644 --- a/Source/Core/DolphinWX/Src/Debugger/CodeWindow.cpp +++ b/Source/Core/DolphinWX/Src/Debugger/CodeWindow.cpp @@ -263,6 +263,8 @@ void CCodeWindow::SingleStep() { if (CCPU::IsStepping()) { + if (jit) + jit->GetBlockCache()->InvalidateICache(PC, 4); CCPU::StepOpcode(&sync_event); wxThread::Sleep(20); // need a short wait here diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index fc85b8e39d..a4dddf537f 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -56,11 +56,12 @@ static bool bProcessFifoToLoWatermark = false; static bool bProcessFifoAllDistance = false; volatile bool isPossibleWaitingSetDrawDone = false; +volatile bool isHiWatermarkActive = false; volatile bool interruptSet= false; volatile bool interruptWaiting= false; volatile bool interruptTokenWaiting = false; volatile bool interruptFinishWaiting = false; -volatile bool OnOverflow = false; +volatile bool waitingForPEInterruptDisable = false; bool IsOnThread() { @@ -86,13 +87,12 @@ void DoState(PointerWrap &p) p.Do(bProcessFifoToLoWatermark); p.Do(bProcessFifoAllDistance); - + p.Do(isHiWatermarkActive); p.Do(isPossibleWaitingSetDrawDone); p.Do(interruptSet); p.Do(interruptWaiting); p.Do(interruptTokenWaiting); p.Do(interruptFinishWaiting); - p.Do(OnOverflow); } inline void WriteLow (volatile u32& _reg, u16 lowbits) {Common::AtomicStore(_reg,(_reg & 0xFFFF0000) | lowbits);} @@ -135,16 +135,14 @@ void Init() bProcessFifoToLoWatermark = false; bProcessFifoAllDistance = false; isPossibleWaitingSetDrawDone = false; - OnOverflow = false; + isHiWatermarkActive = false; et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper); } void Read16(u16& _rReturnValue, const u32 _Address) { - INFO_LOG(COMMANDPROCESSOR, "(r): 0x%08x", _Address); - ProcessFifoEvents(); switch (_Address & 0xFFF) { case STATUS_REGISTER: @@ -173,11 +171,23 @@ void Read16(u16& _rReturnValue, const u32 _Address) case FIFO_LO_WATERMARK_HI: _rReturnValue = ReadHigh(fifo.CPLoWatermark); return; case FIFO_RW_DISTANCE_LO: - _rReturnValue = ReadLow (fifo.CPReadWriteDistance); + if (IsOnThread()) + if(fifo.CPWritePointer >= fifo.SafeCPReadPointer) + _rReturnValue = ReadLow (fifo.CPWritePointer - fifo.SafeCPReadPointer); + else + _rReturnValue = ReadLow (fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32); + else + _rReturnValue = ReadLow (fifo.CPReadWriteDistance); DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_LO : %04x", _rReturnValue); return; case FIFO_RW_DISTANCE_HI: - _rReturnValue = ReadHigh(fifo.CPReadWriteDistance); + if (IsOnThread()) + if(fifo.CPWritePointer >= fifo.SafeCPReadPointer) + _rReturnValue = ReadHigh (fifo.CPWritePointer - fifo.SafeCPReadPointer); + else + _rReturnValue = ReadHigh (fifo.CPEnd - fifo.SafeCPReadPointer + fifo.CPWritePointer - fifo.CPBase + 32); + else + _rReturnValue = ReadHigh(fifo.CPReadWriteDistance); DEBUG_LOG(COMMANDPROCESSOR, "read FIFO_RW_DISTANCE_HI : %04x", _rReturnValue); return; case FIFO_WRITE_POINTER_LO: @@ -358,6 +368,7 @@ void Write16(const u16 _Value, const u32 _Address) break; case FIFO_READ_POINTER_HI: WriteHigh((u32 &)fifo.CPReadPointer, _Value); + fifo.SafeCPReadPointer = fifo.CPReadPointer; DEBUG_LOG(COMMANDPROCESSOR,"\t write to FIFO_READ_POINTER_HI : %04x", _Value); break; @@ -390,10 +401,6 @@ void Write16(const u16 _Value, const u32 _Address) case FIFO_RW_DISTANCE_HI: WriteHigh((u32 &)fifo.CPReadWriteDistance, _Value); - DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value); - break; - case FIFO_RW_DISTANCE_LO: - WriteLow((u32 &)fifo.CPReadWriteDistance, _Value & 0xFFE0); if (fifo.CPReadWriteDistance == 0) { GPFifo::ResetGatherPipe(); @@ -403,6 +410,10 @@ void Write16(const u16 _Value, const u32 _Address) ResetVideoBuffer(); } IncrementCheckContextId(); + DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value); + break; + case FIFO_RW_DISTANCE_LO: + WriteLow((u32 &)fifo.CPReadWriteDistance, _Value & 0xFFE0); DEBUG_LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_LO : %04x", _Value); break; @@ -412,7 +423,6 @@ void Write16(const u16 _Value, const u32 _Address) if (!IsOnThread()) RunGpu(); - ProcessFifoEvents(); } void Read32(u32& _rReturnValue, const u32 _Address) @@ -434,6 +444,19 @@ void STACKALIGN GatherPipeBursted() { if (!IsOnThread()) RunGpu(); + else + { + // In multibuffer mode is not allowed write in the same fifo attached to the GPU. + // Fix Pokemon XD in DC mode. + if((ProcessorInterface::Fifo_CPUEnd == fifo.CPEnd) && (ProcessorInterface::Fifo_CPUBase == fifo.CPBase) + && fifo.CPReadWriteDistance > 0) + { + waitingForPEInterruptDisable = true; + ProcessFifoAllDistance(); + waitingForPEInterruptDisable = false; + } + + } return; } @@ -449,26 +472,7 @@ void STACKALIGN GatherPipeBursted() Common::AtomicAdd(fifo.CPReadWriteDistance, GATHER_PIPE_SIZE); if (!IsOnThread()) - { RunGpu(); - } - else - { - if(fifo.CPReadWriteDistance == fifo.CPEnd - fifo.CPBase - 32) - { - if(!OnOverflow) - NOTICE_LOG(COMMANDPROCESSOR,"FIFO is almost in overflown, BreakPoint: %i", fifo.bFF_Breakpoint); - OnOverflow = true; - while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && - fifo.CPReadWriteDistance > fifo.CPEnd - fifo.CPBase - 64) - Common::YieldCPU(); - } - else - { - OnOverflow = false; - } - } - _assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance <= fifo.CPEnd - fifo.CPBase, "FIFO is overflown by GatherPipe !\nCPU thread is too fast!"); @@ -509,17 +513,15 @@ void AbortFrame() void SetOverflowStatusFromGatherPipe() { - if (!fifo.bFF_HiWatermarkInt) return; - fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark); - fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); - - bool interrupt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && - m_CPCtrlReg.GPLinkEnable && m_CPCtrlReg.GPReadEnable; + isHiWatermarkActive = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && m_CPCtrlReg.GPReadEnable; - if (interrupt != interruptSet && interrupt) - CommandProcessor::UpdateInterrupts(true); - + if (isHiWatermarkActive) + { + interruptSet = true; + INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); + ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true); + } } void SetCpStatus() @@ -527,14 +529,12 @@ void SetCpStatus() // overflow & underflow check fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark); fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); - - // breakpoint + // breakpoint if (fifo.bFF_BPEnable) { if (fifo.CPBreakpoint == fifo.CPReadPointer) - { - + { if (!fifo.bFF_Breakpoint) { INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer); @@ -562,13 +562,18 @@ void SetCpStatus() bool interrupt = (bpInt || ovfInt || undfInt) && m_CPCtrlReg.GPReadEnable; + isHiWatermarkActive = ovfInt && m_CPCtrlReg.GPReadEnable; + if (interrupt != interruptSet && !interruptWaiting) { u64 userdata = interrupt?1:0; if (IsOnThread()) { - interruptWaiting = true; - CommandProcessor::UpdateInterruptsFromVideoBackend(userdata); + if(!interrupt || bpInt || undfInt) + { + interruptWaiting = true; + CommandProcessor::UpdateInterruptsFromVideoBackend(userdata); + } } else CommandProcessor::UpdateInterrupts(userdata); @@ -591,7 +596,7 @@ void ProcessFifoAllDistance() if (IsOnThread()) { while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && - fifo.CPReadWriteDistance && !AtBreakpoint()) + fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) Common::YieldCPU(); } bProcessFifoAllDistance = false; @@ -611,13 +616,16 @@ void Shutdown() void SetCpStatusRegister() { // Here always there is one fifo attached to the GPU - m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; - m_CPStatusReg.ReadIdle = (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint); + m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance; m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; + // HACK to compensate for slow response to PE interrupts in Time Splitters: Future Perfect + if (IsOnThread()) + PixelEngine::ResumeWaitingForPEInterrupt(); + INFO_LOG(COMMANDPROCESSOR,"\t Read from STATUS_REGISTER : %04x", m_CPStatusReg.Hex); DEBUG_LOG(COMMANDPROCESSOR, "(r) status: iBP %s | fReadIdle %s | fCmdIdle %s | iOvF %s | iUndF %s" , m_CPStatusReg.Breakpoint ? "ON" : "OFF" @@ -630,14 +638,14 @@ void SetCpStatusRegister() void SetCpControlRegister() { - // If the new fifo is being attached We make sure there wont be SetFinish event pending. // This protection fix eternal darkness booting, because the second SetFinish event when it is booting // seems invalid or has a bug and hang the game. if (!fifo.bFF_GPReadEnable && m_CPCtrlReg.GPReadEnable && !m_CPCtrlReg.BPEnable) { - PixelEngine::ResetSetFinish(); + ProcessFifoEvents(); + PixelEngine::ResetSetFinish(); } fifo.bFF_BPInt = m_CPCtrlReg.BPInt; @@ -652,9 +660,6 @@ void SetCpControlRegister() ProcessorInterface::Fifo_CPUBase = fifo.CPBase; ProcessorInterface::Fifo_CPUEnd = fifo.CPEnd; } - // If overflown happens process the fifo to LoWatemark - if (bProcessFifoToLoWatermark) - ProcessFifoToLoWatermark(); if(fifo.bFF_GPReadEnable && !m_CPCtrlReg.GPReadEnable) { @@ -666,7 +671,6 @@ void SetCpControlRegister() fifo.bFF_GPReadEnable = m_CPCtrlReg.GPReadEnable; } - DEBUG_LOG(COMMANDPROCESSOR, "\t GPREAD %s | BP %s | Int %s | OvF %s | UndF %s | LINK %s" , fifo.bFF_GPReadEnable ? "ON" : "OFF" , fifo.bFF_BPEnable ? "ON" : "OFF" diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index db6772d66d..5d31453537 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -25,18 +25,18 @@ class PointerWrap; extern bool MT; - namespace CommandProcessor { extern SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread. extern volatile bool isPossibleWaitingSetDrawDone; //This one is used for sync gfx thread and emulator thread. +extern volatile bool isHiWatermarkActive; extern volatile bool interruptSet; extern volatile bool interruptWaiting; extern volatile bool interruptTokenWaiting; extern volatile bool interruptFinishWaiting; -extern volatile bool OnOverflow; - +extern volatile bool waitingForPEInterruptDisable; + // internal hardware addresses enum { diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index 842ff49e78..2e60055c97 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -22,6 +22,7 @@ #include "Atomic.h" #include "OpcodeDecoding.h" #include "CommandProcessor.h" +#include "PixelEngine.h" #include "ChunkFile.h" #include "Fifo.h" #include "HW/Memmap.h" @@ -137,8 +138,7 @@ void RunGpuLoop() CommandProcessor::SetCpStatus(); // check if we are able to run this buffer - while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && - fifo.CPReadWriteDistance && (!AtBreakpoint() || CommandProcessor::OnOverflow)) + while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) { if (!GpuRunningState) break; diff --git a/Source/Core/VideoCommon/Src/MainBase.cpp b/Source/Core/VideoCommon/Src/MainBase.cpp index af21ebbb94..0b1258a662 100644 --- a/Source/Core/VideoCommon/Src/MainBase.cpp +++ b/Source/Core/VideoCommon/Src/MainBase.cpp @@ -250,6 +250,11 @@ bool VideoBackendHardware::Video_IsPossibleWaitingSetDrawDone() return CommandProcessor::isPossibleWaitingSetDrawDone; } +bool VideoBackendHardware::Video_IsHiWatermarkActive() +{ + return CommandProcessor::isHiWatermarkActive; +} + void VideoBackendHardware::Video_AbortFrame() { CommandProcessor::AbortFrame(); diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 8164bc41da..03a3a7547a 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -180,7 +180,6 @@ void Init() void Read16(u16& _uReturnValue, const u32 _iAddress) { DEBUG_LOG(PIXELENGINE, "(r16) 0x%08x", _iAddress); - CommandProcessor::ProcessFifoEvents(); switch (_iAddress & 0xFFF) { // CPU Direct Access EFB Raster State Config @@ -327,7 +326,6 @@ void Write16(const u16 _iValue, const u32 _iAddress) break; case PE_TOKEN_REG: - //LOG(PIXELENGINE,"WEIRD: program wrote token: %i",_iValue); PanicAlert("(w16) WTF? PowerPC program wrote token: %i", _iValue); //only the gx pipeline is supposed to be able to write here //g_token = _iValue; @@ -338,7 +336,6 @@ void Write16(const u16 _iValue, const u32 _iAddress) break; } - CommandProcessor::ProcessFifoEvents(); } void Write32(const u32 _iValue, const u32 _iAddress) @@ -362,22 +359,16 @@ void UpdateInterrupts() void UpdateTokenInterrupt(bool active) { - if(interruptSetToken != active) - { ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active); interruptSetToken = active; - } } void UpdateFinishInterrupt(bool active) { - if(interruptSetFinish != active) - { ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active); interruptSetFinish = active; if (active) State::ProcessRequestedStates(0); - } } // TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate). @@ -396,8 +387,6 @@ void SetToken_OnMainThread(u64 userdata, int cyclesLate) CommandProcessor::interruptTokenWaiting = false; IncrementCheckContextId(); //} - //else - // LOGV(PIXELENGINE, 1, "VIDEO Backend wrote token: %i", CommandProcessor::fifo.PEToken); } void SetFinish_OnMainThread(u64 userdata, int cyclesLate) @@ -474,4 +463,17 @@ void ResetSetToken() } CommandProcessor::interruptTokenWaiting = false; } + +bool WaitingForPEInterrupt() +{ + return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken); +} + +void ResumeWaitingForPEInterrupt() +{ + interruptSetFinish = false; + interruptSetToken = false; + CommandProcessor::interruptFinishWaiting = false; + CommandProcessor::interruptTokenWaiting = false; +} } // end of namespace PixelEngine diff --git a/Source/Core/VideoCommon/Src/PixelEngine.h b/Source/Core/VideoCommon/Src/PixelEngine.h index dd0304fbe1..64f959009f 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.h +++ b/Source/Core/VideoCommon/Src/PixelEngine.h @@ -80,7 +80,8 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge); void SetFinish(void); void ResetSetFinish(void); void ResetSetToken(void); -bool AllowIdleSkipping(); +bool WaitingForPEInterrupt(); +void ResumeWaitingForPEInterrupt(); // Bounding box functionality. Paper Mario (both) are a couple of the few games that use it. extern u16 bbox[4]; diff --git a/Source/Plugins/Plugin_VideoSoftware/Src/SWmain.cpp b/Source/Plugins/Plugin_VideoSoftware/Src/SWmain.cpp index abfd9155f5..c591bd0957 100644 --- a/Source/Plugins/Plugin_VideoSoftware/Src/SWmain.cpp +++ b/Source/Plugins/Plugin_VideoSoftware/Src/SWmain.cpp @@ -215,6 +215,12 @@ bool VideoSoftware::Video_IsPossibleWaitingSetDrawDone(void) return false; } +bool VideoSoftware::Video_IsHiWatermarkActive(void) +{ + return false; +} + + void VideoSoftware::Video_AbortFrame(void) { } diff --git a/Source/Plugins/Plugin_VideoSoftware/Src/VideoBackend.h b/Source/Plugins/Plugin_VideoSoftware/Src/VideoBackend.h index 08b4c155ad..7c52e34291 100644 --- a/Source/Plugins/Plugin_VideoSoftware/Src/VideoBackend.h +++ b/Source/Plugins/Plugin_VideoSoftware/Src/VideoBackend.h @@ -36,7 +36,7 @@ class VideoSoftware : public VideoBackend void Video_SetRendering(bool bEnabled); void Video_GatherPipeBursted(); - + bool Video_IsHiWatermarkActive(); bool Video_IsPossibleWaitingSetDrawDone(); void Video_AbortFrame();