diff options
Diffstat (limited to '')
| -rw-r--r-- | src/citra_qt/bootmanager.cpp | 43 | ||||
| -rw-r--r-- | src/citra_qt/bootmanager.h | 44 | ||||
| -rw-r--r-- | src/citra_qt/debugger/disassembler.cpp | 10 | ||||
| -rw-r--r-- | src/citra_qt/main.cpp | 33 |
4 files changed, 40 insertions, 90 deletions
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index fa4e976f4..1e902a8b6 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp | |||
| @@ -28,9 +28,8 @@ | |||
| 28 | #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" | 28 | #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" |
| 29 | 29 | ||
| 30 | EmuThread::EmuThread(GRenderWindow* render_window) : | 30 | EmuThread::EmuThread(GRenderWindow* render_window) : |
| 31 | exec_cpu_step(false), cpu_running(false), stop_run(false), render_window(render_window) { | 31 | exec_step(false), running(false), stop_run(false), render_window(render_window) { |
| 32 | 32 | ||
| 33 | shutdown_event.Reset(); | ||
| 34 | connect(this, SIGNAL(started()), render_window, SLOT(moveContext())); | 33 | connect(this, SIGNAL(started()), render_window, SLOT(moveContext())); |
| 35 | } | 34 | } |
| 36 | 35 | ||
| @@ -42,20 +41,20 @@ void EmuThread::run() { | |||
| 42 | // next execution step | 41 | // next execution step |
| 43 | bool was_active = false; | 42 | bool was_active = false; |
| 44 | while (!stop_run) { | 43 | while (!stop_run) { |
| 45 | if (cpu_running) { | 44 | if (running) { |
| 46 | if (!was_active) | 45 | if (!was_active) |
| 47 | emit DebugModeLeft(); | 46 | emit DebugModeLeft(); |
| 48 | 47 | ||
| 49 | Core::RunLoop(); | 48 | Core::RunLoop(); |
| 50 | 49 | ||
| 51 | was_active = cpu_running || exec_cpu_step; | 50 | was_active = running || exec_step; |
| 52 | if (!was_active) | 51 | if (!was_active) |
| 53 | emit DebugModeEntered(); | 52 | emit DebugModeEntered(); |
| 54 | } else if (exec_cpu_step) { | 53 | } else if (exec_step) { |
| 55 | if (!was_active) | 54 | if (!was_active) |
| 56 | emit DebugModeLeft(); | 55 | emit DebugModeLeft(); |
| 57 | 56 | ||
| 58 | exec_cpu_step = false; | 57 | exec_step = false; |
| 59 | Core::SingleStep(); | 58 | Core::SingleStep(); |
| 60 | emit DebugModeEntered(); | 59 | emit DebugModeEntered(); |
| 61 | yieldCurrentThread(); | 60 | yieldCurrentThread(); |
| @@ -65,40 +64,8 @@ void EmuThread::run() { | |||
| 65 | } | 64 | } |
| 66 | 65 | ||
| 67 | render_window->moveContext(); | 66 | render_window->moveContext(); |
| 68 | |||
| 69 | shutdown_event.Set(); | ||
| 70 | } | 67 | } |
| 71 | 68 | ||
| 72 | void EmuThread::Stop() { | ||
| 73 | if (!isRunning()) { | ||
| 74 | LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning..."); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | stop_run = true; | ||
| 78 | |||
| 79 | // Release emu threads from any breakpoints, so that this doesn't hang forever. | ||
| 80 | Pica::g_debug_context->ClearBreakpoints(); | ||
| 81 | |||
| 82 | //core::g_state = core::SYS_DIE; | ||
| 83 | |||
| 84 | // TODO: Waiting here is just a bad workaround for retarded shutdown logic. | ||
| 85 | wait(1000); | ||
| 86 | if (isRunning()) { | ||
| 87 | LOG_WARNING(Frontend, "EmuThread still running, terminating..."); | ||
| 88 | quit(); | ||
| 89 | |||
| 90 | // TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam | ||
| 91 | // queued... This should be fixed. | ||
| 92 | wait(50000); | ||
| 93 | if (isRunning()) { | ||
| 94 | LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here..."); | ||
| 95 | terminate(); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | LOG_INFO(Frontend, "EmuThread stopped"); | ||
| 99 | } | ||
| 100 | |||
| 101 | |||
| 102 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context. | 69 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context. |
| 103 | // The corresponding functionality is handled in EmuThread instead | 70 | // The corresponding functionality is handled in EmuThread instead |
| 104 | class GGLWidgetInternal : public QGLWidget | 71 | class GGLWidgetInternal : public QGLWidget |
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h index f6f09773c..e9b3ea664 100644 --- a/src/citra_qt/bootmanager.h +++ b/src/citra_qt/bootmanager.h | |||
| @@ -25,66 +25,46 @@ public: | |||
| 25 | 25 | ||
| 26 | /** | 26 | /** |
| 27 | * Start emulation (on new thread) | 27 | * Start emulation (on new thread) |
| 28 | * | ||
| 29 | * @warning Only call when not running! | 28 | * @warning Only call when not running! |
| 30 | */ | 29 | */ |
| 31 | void run() override; | 30 | void run() override; |
| 32 | 31 | ||
| 33 | /** | 32 | /** |
| 34 | * Allow the CPU to process a single instruction (if cpu is not running) | 33 | * Steps the emulation thread by a single CPU instruction (if the CPU is not already running) |
| 35 | * | ||
| 36 | * @note This function is thread-safe | 34 | * @note This function is thread-safe |
| 37 | */ | 35 | */ |
| 38 | void ExecStep() { exec_cpu_step = true; } | 36 | void ExecStep() { exec_step = true; } |
| 39 | 37 | ||
| 40 | /** | 38 | /** |
| 41 | * Sets whether the CPU is running | 39 | * Sets whether the emulation thread is running or not |
| 42 | * | 40 | * @param running Boolean value, set the emulation thread to running if true |
| 43 | * @note This function is thread-safe | 41 | * @note This function is thread-safe |
| 44 | */ | 42 | */ |
| 45 | void SetCpuRunning(bool running) { cpu_running = running; } | 43 | void SetRunning(bool running) { this->running = running; } |
| 46 | 44 | ||
| 47 | /** | 45 | /** |
| 48 | * Allow the CPU to continue processing instructions without interruption | 46 | * Check if the emulation thread is running or not |
| 49 | * | 47 | * @return True if the emulation thread is running, otherwise false |
| 50 | * @note This function is thread-safe | 48 | * @note This function is thread-safe |
| 51 | */ | 49 | */ |
| 52 | bool IsCpuRunning() { return cpu_running; } | 50 | bool IsRunning() { return running; } |
| 53 | |||
| 54 | |||
| 55 | /** | ||
| 56 | * Shutdown (permantently stops) the CPU | ||
| 57 | */ | ||
| 58 | void ShutdownCpu() { stop_run = true; }; | ||
| 59 | 51 | ||
| 60 | /** | 52 | /** |
| 61 | * Waits for the CPU shutdown to complete | 53 | * Shutdown (permanently stops) the emulation thread |
| 62 | */ | 54 | */ |
| 63 | void WaitForCpuShutdown() { shutdown_event.Wait(); } | 55 | void Shutdown() { stop_run = true; }; |
| 64 | |||
| 65 | |||
| 66 | public slots: | ||
| 67 | /** | ||
| 68 | * Stop emulation and wait for the thread to finish. | ||
| 69 | * | ||
| 70 | * @details: This function will wait a second for the thread to finish; if it hasn't finished until then, we'll terminate() it and wait another second, hoping that it will be terminated by then. | ||
| 71 | * @note: This function is thread-safe. | ||
| 72 | */ | ||
| 73 | void Stop(); | ||
| 74 | 56 | ||
| 75 | private: | 57 | private: |
| 76 | friend class GMainWindow; | 58 | friend class GMainWindow; |
| 77 | 59 | ||
| 78 | EmuThread(GRenderWindow* render_window); | 60 | EmuThread(GRenderWindow* render_window); |
| 79 | 61 | ||
| 80 | bool exec_cpu_step; | 62 | bool exec_step; |
| 81 | bool cpu_running; | 63 | bool running; |
| 82 | std::atomic<bool> stop_run; | 64 | std::atomic<bool> stop_run; |
| 83 | 65 | ||
| 84 | GRenderWindow* render_window; | 66 | GRenderWindow* render_window; |
| 85 | 67 | ||
| 86 | Common::Event shutdown_event; | ||
| 87 | |||
| 88 | signals: | 68 | signals: |
| 89 | /** | 69 | /** |
| 90 | * Emitted when the CPU has halted execution | 70 | * Emitted when the CPU has halted execution |
diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp index b58edafe7..f9423e1d6 100644 --- a/src/citra_qt/debugger/disassembler.cpp +++ b/src/citra_qt/debugger/disassembler.cpp | |||
| @@ -201,7 +201,7 @@ void DisassemblerWidget::Init() | |||
| 201 | 201 | ||
| 202 | void DisassemblerWidget::OnContinue() | 202 | void DisassemblerWidget::OnContinue() |
| 203 | { | 203 | { |
| 204 | main_window.GetEmuThread()->SetCpuRunning(true); | 204 | main_window.GetEmuThread()->SetRunning(true); |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | void DisassemblerWidget::OnStep() | 207 | void DisassemblerWidget::OnStep() |
| @@ -211,13 +211,13 @@ void DisassemblerWidget::OnStep() | |||
| 211 | 211 | ||
| 212 | void DisassemblerWidget::OnStepInto() | 212 | void DisassemblerWidget::OnStepInto() |
| 213 | { | 213 | { |
| 214 | main_window.GetEmuThread()->SetCpuRunning(false); | 214 | main_window.GetEmuThread()->SetRunning(false); |
| 215 | main_window.GetEmuThread()->ExecStep(); | 215 | main_window.GetEmuThread()->ExecStep(); |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | void DisassemblerWidget::OnPause() | 218 | void DisassemblerWidget::OnPause() |
| 219 | { | 219 | { |
| 220 | main_window.GetEmuThread()->SetCpuRunning(false); | 220 | main_window.GetEmuThread()->SetRunning(false); |
| 221 | 221 | ||
| 222 | // TODO: By now, the CPU might not have actually stopped... | 222 | // TODO: By now, the CPU might not have actually stopped... |
| 223 | if (Core::g_app_core) { | 223 | if (Core::g_app_core) { |
| @@ -227,7 +227,7 @@ void DisassemblerWidget::OnPause() | |||
| 227 | 227 | ||
| 228 | void DisassemblerWidget::OnToggleStartStop() | 228 | void DisassemblerWidget::OnToggleStartStop() |
| 229 | { | 229 | { |
| 230 | main_window.GetEmuThread()->SetCpuRunning(!main_window.GetEmuThread()->IsCpuRunning()); | 230 | main_window.GetEmuThread()->SetRunning(!main_window.GetEmuThread()->IsRunning()); |
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | void DisassemblerWidget::OnDebugModeEntered() | 233 | void DisassemblerWidget::OnDebugModeEntered() |
| @@ -235,7 +235,7 @@ void DisassemblerWidget::OnDebugModeEntered() | |||
| 235 | ARMword next_instr = Core::g_app_core->GetPC(); | 235 | ARMword next_instr = Core::g_app_core->GetPC(); |
| 236 | 236 | ||
| 237 | if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) | 237 | if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) |
| 238 | main_window.GetEmuThread()->SetCpuRunning(false); | 238 | main_window.GetEmuThread()->SetRunning(false); |
| 239 | 239 | ||
| 240 | model->SetNextInstruction(next_instr); | 240 | model->SetNextInstruction(next_instr); |
| 241 | 241 | ||
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 5441c17f1..dd180baa4 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp | |||
| @@ -199,10 +199,6 @@ void GMainWindow::OnDisplayTitleBars(bool show) | |||
| 199 | void GMainWindow::BootGame(std::string filename) { | 199 | void GMainWindow::BootGame(std::string filename) { |
| 200 | LOG_INFO(Frontend, "Citra starting...\n"); | 200 | LOG_INFO(Frontend, "Citra starting...\n"); |
| 201 | 201 | ||
| 202 | // Shutdown previous session if the emu thread is still active... | ||
| 203 | if (emu_thread != nullptr) | ||
| 204 | ShutdownGame(); | ||
| 205 | |||
| 206 | System::Init(render_window); | 202 | System::Init(render_window); |
| 207 | 203 | ||
| 208 | // Load a game or die... | 204 | // Load a game or die... |
| @@ -222,29 +218,36 @@ void GMainWindow::BootGame(std::string filename) { | |||
| 222 | } | 218 | } |
| 223 | 219 | ||
| 224 | void GMainWindow::ShutdownGame() { | 220 | void GMainWindow::ShutdownGame() { |
| 225 | emu_thread->SetCpuRunning(false); | 221 | // Shutdown the emulation thread and wait for it to complete |
| 226 | 222 | emu_thread->SetRunning(false); | |
| 227 | emu_thread->ShutdownCpu(); | 223 | emu_thread->Shutdown(); |
| 228 | emu_thread->WaitForCpuShutdown(); | 224 | emu_thread->wait(); |
| 229 | emu_thread->Stop(); | ||
| 230 | |||
| 231 | delete emu_thread; | 225 | delete emu_thread; |
| 232 | emu_thread = nullptr; | 226 | emu_thread = nullptr; |
| 233 | 227 | ||
| 228 | // Release emu threads from any breakpoints | ||
| 229 | Pica::g_debug_context->ClearBreakpoints(); | ||
| 230 | |||
| 231 | // Shutdown the core emulation | ||
| 234 | System::Shutdown(); | 232 | System::Shutdown(); |
| 235 | 233 | ||
| 234 | // Update the GUI | ||
| 236 | ui.action_Start->setEnabled(true); | 235 | ui.action_Start->setEnabled(true); |
| 237 | ui.action_Pause->setEnabled(false); | 236 | ui.action_Pause->setEnabled(false); |
| 238 | ui.action_Stop->setEnabled(false); | 237 | ui.action_Stop->setEnabled(false); |
| 239 | |||
| 240 | render_window->hide(); | 238 | render_window->hide(); |
| 241 | } | 239 | } |
| 242 | 240 | ||
| 243 | void GMainWindow::OnMenuLoadFile() | 241 | void GMainWindow::OnMenuLoadFile() |
| 244 | { | 242 | { |
| 245 | QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)")); | 243 | QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)")); |
| 246 | if (filename.size()) | 244 | if (filename.size()) { |
| 247 | BootGame(filename.toLatin1().data()); | 245 | // Shutdown previous session if the emu thread is still active... |
| 246 | if (emu_thread != nullptr) | ||
| 247 | ShutdownGame(); | ||
| 248 | |||
| 249 | BootGame(filename.toLatin1().data()); | ||
| 250 | } | ||
| 248 | } | 251 | } |
| 249 | 252 | ||
| 250 | void GMainWindow::OnMenuLoadSymbolMap() { | 253 | void GMainWindow::OnMenuLoadSymbolMap() { |
| @@ -255,7 +258,7 @@ void GMainWindow::OnMenuLoadSymbolMap() { | |||
| 255 | 258 | ||
| 256 | void GMainWindow::OnStartGame() | 259 | void GMainWindow::OnStartGame() |
| 257 | { | 260 | { |
| 258 | emu_thread->SetCpuRunning(true); | 261 | emu_thread->SetRunning(true); |
| 259 | 262 | ||
| 260 | ui.action_Start->setEnabled(false); | 263 | ui.action_Start->setEnabled(false); |
| 261 | ui.action_Pause->setEnabled(true); | 264 | ui.action_Pause->setEnabled(true); |
| @@ -264,7 +267,7 @@ void GMainWindow::OnStartGame() | |||
| 264 | 267 | ||
| 265 | void GMainWindow::OnPauseGame() | 268 | void GMainWindow::OnPauseGame() |
| 266 | { | 269 | { |
| 267 | emu_thread->SetCpuRunning(false); | 270 | emu_thread->SetRunning(false); |
| 268 | 271 | ||
| 269 | ui.action_Start->setEnabled(true); | 272 | ui.action_Start->setEnabled(true); |
| 270 | ui.action_Pause->setEnabled(false); | 273 | ui.action_Pause->setEnabled(false); |