// Copyright 2014 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include "Core/PowerPC/CachedInterpreter.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Core/ConfigManager.h" #include "Core/CoreTiming.h" #include "Core/HLE/HLE.h" #include "Core/HW/CPU.h" #include "Core/PowerPC/Gekko.h" #include "Core/PowerPC/Jit64Common/Jit64Base.h" #include "Core/PowerPC/PPCAnalyst.h" #include "Core/PowerPC/PowerPC.h" void CachedInterpreter::Init() { m_code.reserve(CODE_SIZE / sizeof(Instruction)); jo.enableBlocklink = false; JitBaseBlockCache::Init(); UpdateMemoryOptions(); code_block.m_stats = &js.st; code_block.m_gpa = &js.gpa; code_block.m_fpa = &js.fpa; } void CachedInterpreter::Shutdown() { JitBaseBlockCache::Shutdown(); } void CachedInterpreter::ExecuteOneBlock() { const u8* normal_entry = JitBaseBlockCache::Dispatch(); const Instruction* code = reinterpret_cast(normal_entry); for (; code->type != Instruction::INSTRUCTION_ABORT; ++code) { switch (code->type) { case Instruction::INSTRUCTION_TYPE_COMMON: code->common_callback(UGeckoInstruction(code->data)); break; case Instruction::INSTRUCTION_TYPE_CONDITIONAL: if (code->conditional_callback(code->data)) return; break; default: ERROR_LOG(POWERPC, "Unknown CachedInterpreter Instruction: %d", code->type); break; } } } void CachedInterpreter::Run() { while (CPU::GetState() == CPU::CPU_RUNNING) { // Start new timing slice // NOTE: Exceptions may change PC CoreTiming::Advance(); do { ExecuteOneBlock(); } while (PowerPC::ppcState.downcount > 0); } } void CachedInterpreter::SingleStep() { // Enter new timing slice CoreTiming::Advance(); ExecuteOneBlock(); } static void EndBlock(UGeckoInstruction data) { PC = NPC; PowerPC::ppcState.downcount -= data.hex; } static void WritePC(UGeckoInstruction data) { PC = data.hex; NPC = data.hex + 4; } static void WriteBrokenBlockNPC(UGeckoInstruction data) { NPC = data.hex; } static bool CheckFPU(u32 data) { UReg_MSR& msr = (UReg_MSR&)MSR; if (!msr.FP) { PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; PowerPC::CheckExceptions(); PowerPC::ppcState.downcount -= data; return true; } return false; } static bool CheckDSI(u32 data) { if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { PowerPC::CheckExceptions(); PowerPC::ppcState.downcount -= data; return true; } return false; } void CachedInterpreter::Jit(u32 address) { if (m_code.size() >= CODE_SIZE / sizeof(Instruction) - 0x1000 || IsFull() || SConfig::GetInstance().bJITNoBlockCache) { ClearCache(); } u32 nextPC = analyzer.Analyze(PC, &code_block, &code_buffer, code_buffer.GetSize()); if (code_block.m_memory_exception) { // Address of instruction could not be translated NPC = nextPC; PowerPC::ppcState.Exceptions |= EXCEPTION_ISI; PowerPC::CheckExceptions(); WARN_LOG(POWERPC, "ISI exception at 0x%08x", nextPC); return; } int block_num = AllocateBlock(PC); JitBlock* b = GetBlock(block_num); js.blockStart = PC; js.firstFPInstructionFound = false; js.fifoBytesSinceCheck = 0; js.downcountAmount = 0; js.curBlock = b; PPCAnalyst::CodeOp* ops = code_buffer.codebuffer; b->checkedEntry = GetCodePtr(); b->normalEntry = GetCodePtr(); b->runCount = 0; for (u32 i = 0; i < code_block.m_num_instructions; i++) { js.downcountAmount += ops[i].opinfo->numCycles; u32 function = HLE::GetFunctionIndex(ops[i].address); if (function != 0) { int type = HLE::GetFunctionTypeByIndex(function); if (type == HLE::HLE_HOOK_START || type == HLE::HLE_HOOK_REPLACE) { int flags = HLE::GetFunctionFlagsByIndex(function); if (HLE::IsEnabled(flags)) { m_code.emplace_back(WritePC, ops[i].address); m_code.emplace_back(Interpreter::HLEFunction, ops[i].inst); if (type == HLE::HLE_HOOK_REPLACE) { m_code.emplace_back(EndBlock, js.downcountAmount); m_code.emplace_back(); break; } } } } if (!ops[i].skip) { bool check_fpu = (ops[i].opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound; bool endblock = (ops[i].opinfo->flags & FL_ENDBLOCK) != 0; bool memcheck = (ops[i].opinfo->flags & FL_LOADSTORE) && jo.memcheck; if (check_fpu) { m_code.emplace_back(WritePC, ops[i].address); m_code.emplace_back(CheckFPU, js.downcountAmount); js.firstFPInstructionFound = true; } if (endblock || memcheck) m_code.emplace_back(WritePC, ops[i].address); m_code.emplace_back(GetInterpreterOp(ops[i].inst), ops[i].inst); if (memcheck) m_code.emplace_back(CheckDSI, js.downcountAmount); if (endblock) m_code.emplace_back(EndBlock, js.downcountAmount); } } if (code_block.m_broken) { m_code.emplace_back(WriteBrokenBlockNPC, nextPC); m_code.emplace_back(EndBlock, js.downcountAmount); } m_code.emplace_back(); b->codeSize = (u32)(GetCodePtr() - b->checkedEntry); b->originalSize = code_block.m_num_instructions; FinalizeBlock(block_num, jo.enableBlocklink, b->checkedEntry); } void CachedInterpreter::ClearCache() { m_code.clear(); JitBaseBlockCache::Clear(); UpdateMemoryOptions(); }