void CCPU::Run() { std::lock_guard<std::mutex> lk(m_csCpuOccupied); Host_UpdateDisasmDialog(); while (true) { reswitch: switch (PowerPC::GetState()) { case PowerPC::CPU_RUNNING: //1: enter a fast runloop PowerPC::RunLoop(); break; case PowerPC::CPU_STEPPING: m_csCpuOccupied.unlock(); //1: wait for step command.. m_StepEvent.Wait(); m_csCpuOccupied.lock(); if (PowerPC::GetState() == PowerPC::CPU_POWERDOWN) return; if (PowerPC::GetState() != PowerPC::CPU_STEPPING) goto reswitch; //3: do a step PowerPC::SingleStep(); //4: update disasm dialog if (m_SyncEvent) { m_SyncEvent->Set(); m_SyncEvent = nullptr; } Host_UpdateDisasmDialog(); break; case PowerPC::CPU_POWERDOWN: //1: Exit loop!! return; } } }
void Stop() { volatile CPUState old_state = state; state = CPU_POWERDOWN; // Wait for the CPU core to leave if (old_state == CPU_RUNNING) s_state_change.WaitFor(std::chrono::seconds(1)); Host_UpdateDisasmDialog(); }
void gdb_handle_exception() { while (gdb_active()) { if (!gdb_data_available()) continue; gdb_read_command(); if (cmd_len == 0) continue; switch (cmd_bfr[0]) { case 'q': gdb_handle_query(); break; case 'H': gdb_handle_set_thread(); break; case '?': gdb_handle_signal(); break; case 'k': gdb_deinit(); INFO_LOG(GDB_STUB, "killed by gdb"); return; case 'g': gdb_read_registers(); break; case 'G': gdb_write_registers(); break; case 'p': gdb_read_register(); break; case 'P': gdb_write_register(); break; case 'm': gdb_read_mem(); break; case 'M': gdb_write_mem(); PowerPC::ppcState.iCache.Reset(); Host_UpdateDisasmDialog(); break; case 's': gdb_step(); return; case 'C': case 'c': gdb_continue(); return; case 'z': gdb_remove_bp(); break; case 'Z': _gdb_add_bp(); break; default: gdb_reply(""); break; } } }
void Start() { state = CPU_RUNNING; Host_UpdateDisasmDialog(); }
void RunLoop() { state = CPU_RUNNING; cpu_core_base->Run(); Host_UpdateDisasmDialog(); }
void RunLoop() { s_cpu_core_base->Run(); Host_UpdateDisasmDialog(); }
// Initialize and create emulation thread // Call browser: Init():s_emu_thread(). // See the BootManager.cpp file description for a complete call schedule. void EmuThread() { const SCoreStartupParameter& core_parameter = SConfig::GetInstance().m_LocalCoreStartupParameter; Common::SetCurrentThreadName("Emuthread - Starting"); if (SConfig::GetInstance().m_OCEnable) DisplayMessage("WARNING: running at non-native CPU clock! Game may not be stable.", 8000); DisplayMessage(cpu_info.brand_string, 8000); DisplayMessage(cpu_info.Summarize(), 8000); DisplayMessage(core_parameter.m_strFilename, 3000); Movie::Init(); HW::Init(); if (!g_video_backend->Initialize(s_window_handle)) { PanicAlert("Failed to initialize video backend!"); Host_Message(WM_USER_STOP); return; } OSD::AddMessage("Dolphin " + g_video_backend->GetName() + " Video Backend.", 5000); if (cpu_info.HTT) SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPThread = cpu_info.num_cores > 4; else SConfig::GetInstance().m_LocalCoreStartupParameter.bDSPThread = cpu_info.num_cores > 2; if (!DSP::GetDSPEmulator()->Initialize(core_parameter.bWii, core_parameter.bDSPThread)) { HW::Shutdown(); g_video_backend->Shutdown(); PanicAlert("Failed to initialize DSP emulator!"); Host_Message(WM_USER_STOP); return; } Keyboard::Initialize(s_window_handle); Pad::Initialize(s_window_handle); // Load and Init Wiimotes - only if we are booting in Wii mode if (core_parameter.bWii) { Wiimote::Initialize(s_window_handle, !s_state_filename.empty()); // Activate Wiimotes which don't have source set to "None" for (unsigned int i = 0; i != MAX_BBMOTES; ++i) if (g_wiimote_sources[i]) GetUsbPointer()->AccessWiiMote(i | 0x100)->Activate(true); } AudioCommon::InitSoundStream(); // The hardware is initialized. s_hardware_initialized = true; // Boot to pause or not Core::SetState(core_parameter.bBootToPause ? Core::CORE_PAUSE : Core::CORE_RUN); // Load GCM/DOL/ELF whatever ... we boot with the interpreter core PowerPC::SetMode(PowerPC::MODE_INTERPRETER); CBoot::BootUp(); // Setup our core, but can't use dynarec if we are compare server if (core_parameter.iCPUCore != SCoreStartupParameter::CORE_INTERPRETER && (!core_parameter.bRunCompareServer || core_parameter.bRunCompareClient)) { PowerPC::SetMode(PowerPC::MODE_JIT); } else { PowerPC::SetMode(PowerPC::MODE_INTERPRETER); } // Update the window again because all stuff is initialized Host_UpdateDisasmDialog(); Host_UpdateMainFrame(); // Determine the CPU thread function void (*cpuThreadFunc)(void); if (core_parameter.m_BootType == SCoreStartupParameter::BOOT_DFF) cpuThreadFunc = FifoPlayerThread; else cpuThreadFunc = CpuThread; // ENTER THE VIDEO THREAD LOOP if (core_parameter.bCPUThread) { // This thread, after creating the EmuWindow, spawns a CPU // thread, and then takes over and becomes the video thread Common::SetCurrentThreadName("Video thread"); g_video_backend->Video_Prepare(); // Spawn the CPU thread s_cpu_thread = std::thread(cpuThreadFunc); // become the GPU thread g_video_backend->Video_EnterLoop(); // We have now exited the Video Loop INFO_LOG(CONSOLE, "%s", StopMessage(false, "Video Loop Ended").c_str()); } else // SingleCore mode { // The spawned CPU Thread also does the graphics. // The EmuThread is thus an idle thread, which sleeps while // waiting for the program to terminate. Without this extra // thread, the video backend window hangs in single core mode // because no one is pumping messages. Common::SetCurrentThreadName("Emuthread - Idle"); // Spawn the CPU+GPU thread s_cpu_thread = std::thread(cpuThreadFunc); while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) { g_video_backend->PeekMessages(); Common::SleepCurrentThread(20); } } INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str()); // Wait for s_cpu_thread to exit INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str()); #ifdef USE_GDBSTUB INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB ...").c_str()); gdb_deinit(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB stopped.").c_str()); #endif s_cpu_thread.join(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str()); if (core_parameter.bCPUThread) g_video_backend->Video_Cleanup(); VolumeHandler::EjectVolume(); FileMon::Close(); // Stop audio thread - Actually this does nothing when using HLE // emulation, but stops the DSP Interpreter when using LLE emulation. DSP::GetDSPEmulator()->DSP_StopSoundStream(); // We must set up this flag before executing HW::Shutdown() s_hardware_initialized = false; INFO_LOG(CONSOLE, "%s", StopMessage(false, "Shutting down HW").c_str()); HW::Shutdown(); INFO_LOG(CONSOLE, "%s", StopMessage(false, "HW shutdown").c_str()); Wiimote::Shutdown(); Keyboard::Shutdown(); Pad::Shutdown(); g_video_backend->Shutdown(); AudioCommon::ShutdownSoundStream(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str()); // Clear on screen messages that haven't expired g_video_backend->Video_ClearMessages(); // Reload sysconf file in order to see changes committed during emulation if (core_parameter.bWii) SConfig::GetInstance().m_SYSCONF->Reload(); INFO_LOG(CONSOLE, "Stop [Video Thread]\t\t---- Shutdown complete ----"); Movie::Shutdown(); PatchEngine::Shutdown(); s_is_stopping = false; if (s_on_stopped_callback) s_on_stopped_callback(); }
int Interpreter::SingleStepInner() { static UGeckoInstruction instCode; u32 function = HLE::GetFunctionIndex(PC); 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)) { HLEFunction(function); if (type == HLE::HLE_HOOK_START) { // Run the original. function = 0; } } else { function = 0; } } } if (function == 0) { #ifdef USE_GDBSTUB if (gdb_active() && gdb_bp_x(PC)) { Host_UpdateDisasmDialog(); gdb_signal(SIGTRAP); gdb_handle_exception(); } #endif NPC = PC + sizeof(UGeckoInstruction); instCode.hex = PowerPC::Read_Opcode(PC); // Uncomment to trace the interpreter //if ((PC & 0xffffff)>=0x0ab54c && (PC & 0xffffff)<=0x0ab624) // startTrace = 1; //else // startTrace = 0; if (startTrace) { Trace(instCode); } if (instCode.hex != 0) { UReg_MSR& msr = (UReg_MSR&)MSR; if (msr.FP) //If FPU is enabled, just execute { m_opTable[instCode.OPCD](instCode); if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { PowerPC::CheckExceptions(); m_EndBlock = true; } } else { // check if we have to generate a FPU unavailable exception if (!PPCTables::UsesFPU(instCode)) { m_opTable[instCode.OPCD](instCode); if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { PowerPC::CheckExceptions(); m_EndBlock = true; } } else { PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; PowerPC::CheckExceptions(); m_EndBlock = true; } } } else { // Memory exception on instruction fetch PowerPC::CheckExceptions(); m_EndBlock = true; } } last_pc = PC; PC = NPC; GekkoOPInfo *opinfo = GetOpInfo(instCode); return opinfo->numCycles; }
// FastRun - inspired by GCemu (to imitate the JIT so that they can be compared). void Interpreter::Run() { while (!PowerPC::GetState()) { //we have to check exceptions at branches apparently (or maybe just rfi?) if (SConfig::GetInstance().bEnableDebugging) { #ifdef SHOW_HISTORY PCBlockVec.push_back(PC); if (PCBlockVec.size() > ShowBlocks) PCBlockVec.erase(PCBlockVec.begin()); #endif // Debugging friendly version of inner loop. Tries to do the timing as similarly to the // JIT as possible. Does not take into account that some instructions take multiple cycles. while (PowerPC::ppcState.downcount > 0) { m_EndBlock = false; int i; for (i = 0; !m_EndBlock; i++) { #ifdef SHOW_HISTORY PCVec.push_back(PC); if (PCVec.size() > ShowSteps) PCVec.erase(PCVec.begin()); #endif //2: check for breakpoint if (PowerPC::breakpoints.IsAddressBreakPoint(PC)) { #ifdef SHOW_HISTORY NOTICE_LOG(POWERPC, "----------------------------"); NOTICE_LOG(POWERPC, "Blocks:"); for (int j = 0; j < PCBlockVec.size(); j++) NOTICE_LOG(POWERPC, "PC: 0x%08x", PCBlockVec.at(j)); NOTICE_LOG(POWERPC, "----------------------------"); NOTICE_LOG(POWERPC, "Steps:"); for (int j = 0; j < PCVec.size(); j++) { // Write space if (j > 0) { if (PCVec.at(j) != PCVec.at(j-1) + 4) NOTICE_LOG(POWERPC, ""); } NOTICE_LOG(POWERPC, "PC: 0x%08x", PCVec.at(j)); } #endif INFO_LOG(POWERPC, "Hit Breakpoint - %08x", PC); CCPU::Break(); if (PowerPC::breakpoints.IsTempBreakPoint(PC)) PowerPC::breakpoints.Remove(PC); Host_UpdateDisasmDialog(); return; } SingleStepInner(); } PowerPC::ppcState.downcount -= i; } } else { // "fast" version of inner loop. well, it's not so fast. while (PowerPC::ppcState.downcount > 0) { m_EndBlock = false; int cycles = 0; while (!m_EndBlock) { cycles += SingleStepInner(); } PowerPC::ppcState.downcount -= cycles; } } CoreTiming::Advance(); if (PowerPC::ppcState.Exceptions) { PowerPC::CheckExceptions(); PC = NPC; } } // Let the waiting thread know we are done leaving PowerPC::FinishStateMove(); }
void Stop() { state = CPU_POWERDOWN; Host_UpdateDisasmDialog(); }
void Pause() { state = CPU_STEPPING; Host_UpdateDisasmDialog(); }
// FastRun - inspired by GCemu (to imitate the JIT so that they can be compared). void Interpreter::Run() { while (CPU::GetState() == CPU::State::Running) { // CoreTiming Advance() ends the previous slice and declares the start of the next // one so it must always be called at the start. At boot, we are in slice -1 and must // advance into slice 0 to get a correct slice length before executing any cycles. CoreTiming::Advance(); // we have to check exceptions at branches apparently (or maybe just rfi?) if (SConfig::GetInstance().bEnableDebugging) { #ifdef SHOW_HISTORY PCBlockVec.push_back(PC); if (PCBlockVec.size() > ShowBlocks) PCBlockVec.erase(PCBlockVec.begin()); #endif // Debugging friendly version of inner loop. Tries to do the timing as similarly to the // JIT as possible. Does not take into account that some instructions take multiple cycles. while (PowerPC::ppcState.downcount > 0) { m_end_block = false; int i; for (i = 0; !m_end_block; i++) { #ifdef SHOW_HISTORY PCVec.push_back(PC); if (PCVec.size() > ShowSteps) PCVec.erase(PCVec.begin()); #endif // 2: check for breakpoint if (PowerPC::breakpoints.IsAddressBreakPoint(PC)) { #ifdef SHOW_HISTORY NOTICE_LOG(POWERPC, "----------------------------"); NOTICE_LOG(POWERPC, "Blocks:"); for (int j = 0; j < PCBlockVec.size(); j++) NOTICE_LOG(POWERPC, "PC: 0x%08x", PCBlockVec.at(j)); NOTICE_LOG(POWERPC, "----------------------------"); NOTICE_LOG(POWERPC, "Steps:"); for (int j = 0; j < PCVec.size(); j++) { // Write space if (j > 0) { if (PCVec.at(j) != PCVec.at(j - 1) + 4) NOTICE_LOG(POWERPC, ""); } NOTICE_LOG(POWERPC, "PC: 0x%08x", PCVec.at(j)); } #endif INFO_LOG(POWERPC, "Hit Breakpoint - %08x", PC); CPU::Break(); if (PowerPC::breakpoints.IsTempBreakPoint(PC)) PowerPC::breakpoints.Remove(PC); Host_UpdateDisasmDialog(); return; } SingleStepInner(); } PowerPC::ppcState.downcount -= i; } } else { // "fast" version of inner loop. well, it's not so fast. while (PowerPC::ppcState.downcount > 0) { m_end_block = false; int cycles = 0; while (!m_end_block) { cycles += SingleStepInner(); } PowerPC::ppcState.downcount -= cycles; } } } }
// Initalize and create emulation thread // Call browser: Init():g_EmuThread(). // See the BootManager.cpp file description for a complete call schedule. void EmuThread() { const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter; Common::SetCurrentThreadName("Emuthread - Starting"); DisplayMessage(cpu_info.brand_string, 8000); DisplayMessage(cpu_info.Summarize(), 8000); DisplayMessage(_CoreParameter.m_strFilename, 3000); if (cpu_info.IsUnsafe() && (NetPlay::IsNetPlayRunning() || Movie::IsRecordingInput() || Movie::IsPlayingInput())) { PanicAlertT("Warning: Netplay/movies will desync because your CPU does not support DAZ and Dolphin does not emulate it anymore."); } Movie::Init(); HW::Init(); if (!g_video_backend->Initialize(g_pWindowHandle)) { PanicAlert("Failed to initialize video backend!"); Host_Message(WM_USER_STOP); return; } OSD::AddMessage("Dolphin " + g_video_backend->GetName() + " Video Backend.", 5000); if (!DSP::GetDSPEmulator()->Initialize(g_pWindowHandle, _CoreParameter.bWii, _CoreParameter.bDSPThread)) { HW::Shutdown(); g_video_backend->Shutdown(); PanicAlert("Failed to initialize DSP emulator!"); Host_Message(WM_USER_STOP); return; } Pad::Initialize(g_pWindowHandle); // Load and Init Wiimotes - only if we are booting in wii mode if (g_CoreStartupParameter.bWii) { Wiimote::Initialize(g_pWindowHandle, !g_stateFileName.empty()); // Activate wiimotes which don't have source set to "None" for (unsigned int i = 0; i != MAX_BBMOTES; ++i) if (g_wiimote_sources[i]) GetUsbPointer()->AccessWiiMote(i | 0x100)->Activate(true); } AudioCommon::InitSoundStream(g_pWindowHandle); // The hardware is initialized. g_bHwInit = true; // Boot to pause or not Core::SetState(_CoreParameter.bBootToPause ? Core::CORE_PAUSE : Core::CORE_RUN); // Load GCM/DOL/ELF whatever ... we boot with the interpreter core PowerPC::SetMode(PowerPC::MODE_INTERPRETER); CBoot::BootUp(); // Setup our core, but can't use dynarec if we are compare server if (_CoreParameter.iCPUCore && (!_CoreParameter.bRunCompareServer || _CoreParameter.bRunCompareClient)) PowerPC::SetMode(PowerPC::MODE_JIT); else PowerPC::SetMode(PowerPC::MODE_INTERPRETER); // Update the window again because all stuff is initialized Host_UpdateDisasmDialog(); Host_UpdateMainFrame(); // Determine the cpu thread function void (*cpuThreadFunc)(void); if (_CoreParameter.m_BootType == SCoreStartupParameter::BOOT_DFF) cpuThreadFunc = FifoPlayerThread; else cpuThreadFunc = CpuThread; // ENTER THE VIDEO THREAD LOOP if (_CoreParameter.bCPUThread) { // This thread, after creating the EmuWindow, spawns a CPU // thread, and then takes over and becomes the video thread Common::SetCurrentThreadName("Video thread"); g_video_backend->Video_Prepare(); // Spawn the CPU thread g_cpu_thread = std::thread(cpuThreadFunc); // become the GPU thread g_video_backend->Video_EnterLoop(); // We have now exited the Video Loop INFO_LOG(CONSOLE, "%s", StopMessage(false, "Video Loop Ended").c_str()); } else // SingleCore mode { // The spawned CPU Thread also does the graphics. // The EmuThread is thus an idle thread, which sleeps while // waiting for the program to terminate. Without this extra // thread, the video backend window hangs in single core mode // because noone is pumping messages. Common::SetCurrentThreadName("Emuthread - Idle"); // Spawn the CPU+GPU thread g_cpu_thread = std::thread(cpuThreadFunc); while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) { g_video_backend->PeekMessages(); Common::SleepCurrentThread(20); } } // Wait for g_cpu_thread to exit INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str()); #ifdef USE_GDBSTUB INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping GDB ...").c_str()); gdb_deinit(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "GDB stopped.").c_str()); #endif g_cpu_thread.join(); INFO_LOG(CONSOLE, "%s", StopMessage(true, "CPU thread stopped.").c_str()); if (_CoreParameter.bCPUThread) g_video_backend->Video_Cleanup(); VolumeHandler::EjectVolume(); FileMon::Close(); // Stop audio thread - Actually this does nothing when using HLE // emulation, but stops the DSP Interpreter when using LLE emulation. DSP::GetDSPEmulator()->DSP_StopSoundStream(); // We must set up this flag before executing HW::Shutdown() g_bHwInit = false; INFO_LOG(CONSOLE, "%s", StopMessage(false, "Shutting down HW").c_str()); HW::Shutdown(); INFO_LOG(CONSOLE, "%s", StopMessage(false, "HW shutdown").c_str()); Pad::Shutdown(); Wiimote::Shutdown(); g_video_backend->Shutdown(); AudioCommon::ShutdownSoundStream(); }
int Interpreter::SingleStepInner(void) { static UGeckoInstruction instCode; u32 function = m_EndBlock ? HLE::GetFunctionIndex(PC) : 0; // Check for HLE functions after branches 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)) { HLEFunction(function); if (type == HLE::HLE_HOOK_START) { // Run the original. function = 0; } } else { function = 0; } } } if (function == 0) { #ifdef USE_GDBSTUB if (gdb_active() && gdb_bp_x(PC)) { Host_UpdateDisasmDialog(); gdb_signal(SIGTRAP); gdb_handle_exception(); } #endif NPC = PC + sizeof(UGeckoInstruction); instCode.hex = Memory::Read_Opcode(PC); // Uncomment to trace the interpreter //if ((PC & 0xffffff)>=0x0ab54c && (PC & 0xffffff)<=0x0ab624) // startTrace = 1; //else // startTrace = 0; if (startTrace) { Trace(instCode); } if (instCode.hex != 0) { UReg_MSR& msr = (UReg_MSR&)MSR; if (msr.FP) //If FPU is enabled, just execute { m_opTable[instCode.OPCD](instCode); if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { PowerPC::CheckExceptions(); m_EndBlock = true; } } else { // check if we have to generate a FPU unavailable exception if (!PPCTables::UsesFPU(instCode)) { m_opTable[instCode.OPCD](instCode); if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { PowerPC::CheckExceptions(); m_EndBlock = true; } } else { Common::AtomicOr(PowerPC::ppcState.Exceptions, EXCEPTION_FPU_UNAVAILABLE); PowerPC::CheckExceptions(); m_EndBlock = true; } } } else { // Memory exception on instruction fetch PowerPC::CheckExceptions(); m_EndBlock = true; } } last_pc = PC; PC = NPC; #if defined(_DEBUG) || defined(DEBUGFAST) if (PowerPC::ppcState.gpr[1] == 0) { WARN_LOG(POWERPC, "%i Corrupt stack", PowerPC::ppcState.DebugCount); } PowerPC::ppcState.DebugCount++; #endif patches(); GekkoOPInfo *opinfo = GetOpInfo(instCode); return opinfo->numCyclesMinusOne + 1; }
int Interpreter::SingleStepInner() { if (HandleFunctionHooking(PC)) { UpdatePC(); return PPCTables::GetOpInfo(m_prev_inst)->numCycles; } #ifdef USE_GDBSTUB if (gdb_active() && gdb_bp_x(PC)) { Host_UpdateDisasmDialog(); gdb_signal(GDB_SIGTRAP); gdb_handle_exception(); } #endif NPC = PC + sizeof(UGeckoInstruction); m_prev_inst.hex = PowerPC::Read_Opcode(PC); // Uncomment to trace the interpreter // if ((PC & 0xffffff)>=0x0ab54c && (PC & 0xffffff)<=0x0ab624) // startTrace = 1; // else // startTrace = 0; if (startTrace) { Trace(m_prev_inst); } if (m_prev_inst.hex != 0) { if (IsInvalidPairedSingleExecution(m_prev_inst)) { GenerateProgramException(); CheckExceptions(); } else if (MSR.FP) { m_op_table[m_prev_inst.OPCD](m_prev_inst); if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { CheckExceptions(); } } else { // check if we have to generate a FPU unavailable exception or a program exception. if (PPCTables::UsesFPU(m_prev_inst)) { PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; CheckExceptions(); } else { m_op_table[m_prev_inst.OPCD](m_prev_inst); if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { CheckExceptions(); } } } } else { // Memory exception on instruction fetch CheckExceptions(); } UpdatePC(); return PPCTables::GetOpInfo(m_prev_inst)->numCycles; }
int Interpreter::SingleStepInner() { if (!HandleFunctionHooking(PC)) { #ifdef USE_GDBSTUB if (gdb_active() && gdb_bp_x(PC)) { Host_UpdateDisasmDialog(); gdb_signal(SIGTRAP); gdb_handle_exception(); } #endif NPC = PC + sizeof(UGeckoInstruction); m_prev_inst.hex = PowerPC::Read_Opcode(PC); // Uncomment to trace the interpreter // if ((PC & 0xffffff)>=0x0ab54c && (PC & 0xffffff)<=0x0ab624) // startTrace = 1; // else // startTrace = 0; if (startTrace) { Trace(m_prev_inst); } if (m_prev_inst.hex != 0) { if (MSR.FP) // If FPU is enabled, just execute { m_op_table[m_prev_inst.OPCD](m_prev_inst); if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { PowerPC::CheckExceptions(); m_end_block = true; } } else { // check if we have to generate a FPU unavailable exception if (!PPCTables::UsesFPU(m_prev_inst)) { m_op_table[m_prev_inst.OPCD](m_prev_inst); if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) { PowerPC::CheckExceptions(); m_end_block = true; } } else { PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; PowerPC::CheckExceptions(); m_end_block = true; } } } else { // Memory exception on instruction fetch PowerPC::CheckExceptions(); m_end_block = true; } } last_pc = PC; PC = NPC; const GekkoOPInfo* opinfo = PPCTables::GetOpInfo(m_prev_inst); return opinfo->numCycles; }