From 559024593086d04e24a99a9f77490a3f97cf952d Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Tue, 1 May 2018 22:21:38 -0400
Subject: [PATCH] core: Move common CPU core things to its own class.

---
 src/core/CMakeLists.txt |  2 ++
 src/core/core.cpp       | 57 ++++++--------------------------
 src/core/core.h         | 16 ++++-----
 src/core/core_cpu.cpp   | 72 +++++++++++++++++++++++++++++++++++++++++
 src/core/core_cpu.h     | 46 ++++++++++++++++++++++++++
 5 files changed, 135 insertions(+), 58 deletions(-)
 create mode 100644 src/core/core_cpu.cpp
 create mode 100644 src/core/core_cpu.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a41e22f4a5..821d2f8833 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -4,6 +4,8 @@ add_library(core STATIC
     arm/unicorn/arm_unicorn.h
     core.cpp
     core.h
+    core_cpu.cpp
+    core_cpu.h
     core_timing.cpp
     core_timing.h
     file_sys/directory.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9e2229d02e..0af78c18c4 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -5,10 +5,6 @@
 #include <memory>
 #include <utility>
 #include "common/logging/log.h"
-#ifdef ARCHITECTURE_x86_64
-#include "core/arm/dynarmic/arm_dynarmic.h"
-#endif
-#include "core/arm/unicorn/arm_unicorn.h"
 #include "core/core.h"
 #include "core/core_timing.h"
 #include "core/gdbstub/gdbstub.h"
@@ -33,9 +29,6 @@ System::~System() = default;
 
 System::ResultStatus System::RunLoop(bool tight_loop) {
     status = ResultStatus::Success;
-    if (!cpu_core) {
-        return ResultStatus::ErrorNotInitialized;
-    }
 
     if (GDBStub::IsServerEnabled()) {
         GDBStub::HandlePacket();
@@ -52,24 +45,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
         }
     }
 
-    // If we don't have a currently active thread then don't execute instructions,
-    // instead advance to the next event and try to yield to the next thread
-    if (Kernel::GetCurrentThread() == nullptr) {
-        NGLOG_TRACE(Core_ARM, "Idling");
-        CoreTiming::Idle();
-        CoreTiming::Advance();
-        PrepareReschedule();
-    } else {
-        CoreTiming::Advance();
-        if (tight_loop) {
-            cpu_core->Run();
-        } else {
-            cpu_core->Step();
-        }
-    }
-
-    HW::Update();
-    Reschedule();
+    cpu_cores[0]->RunLoop(tight_loop);
 
     return status;
 }
@@ -133,23 +109,13 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
 }
 
 void System::PrepareReschedule() {
-    cpu_core->PrepareReschedule();
-    reschedule_pending = true;
+    cpu_cores[0]->PrepareReschedule();
 }
 
 PerfStats::Results System::GetAndResetPerfStats() {
     return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
 }
 
-void System::Reschedule() {
-    if (!reschedule_pending) {
-        return;
-    }
-
-    reschedule_pending = false;
-    Core::System::GetInstance().Scheduler().Reschedule();
-}
-
 System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
     NGLOG_DEBUG(HW_Memory, "initialized OK");
 
@@ -157,15 +123,8 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
 
     current_process = Kernel::Process::Create("main");
 
-    if (Settings::values.use_cpu_jit) {
-#ifdef ARCHITECTURE_x86_64
-        cpu_core = std::make_shared<ARM_Dynarmic>();
-#else
-        cpu_core = std::make_shared<ARM_Unicorn>();
-        NGLOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
-#endif
-    } else {
-        cpu_core = std::make_shared<ARM_Unicorn>();
+    for (auto& cpu_core : cpu_cores) {
+        cpu_core = std::make_unique<Cpu>();
     }
 
     gpu_core = std::make_unique<Tegra::GPU>();
@@ -176,7 +135,6 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
 
     HW::Init();
     Kernel::Init(system_mode);
-    scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get());
     Service::Init(service_manager);
     GDBStub::Init();
 
@@ -207,13 +165,16 @@ void System::Shutdown() {
     VideoCore::Shutdown();
     GDBStub::Shutdown();
     Service::Shutdown();
-    scheduler.reset();
     Kernel::Shutdown();
     HW::Shutdown();
     service_manager.reset();
     telemetry_session.reset();
     gpu_core.reset();
-    cpu_core.reset();
+
+    for (auto& cpu_core : cpu_cores) {
+        cpu_core.reset();
+    }
+
     CoreTiming::Shutdown();
 
     app_loader.reset();
diff --git a/src/core/core.h b/src/core/core.h
index f81cbfb3cc..6e6cc7579c 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -4,9 +4,11 @@
 
 #pragma once
 
+#include <array>
 #include <memory>
 #include <string>
 #include "common/common_types.h"
+#include "core/core_cpu.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/scheduler.h"
 #include "core/loader/loader.h"
@@ -89,7 +91,7 @@ public:
      * @returns True if the emulated system is powered on, otherwise false.
      */
     bool IsPoweredOn() const {
-        return cpu_core != nullptr;
+        return cpu_cores[0] != nullptr;
     }
 
     /**
@@ -110,7 +112,7 @@ public:
      * @returns A reference to the emulated CPU.
      */
     ARM_Interface& CPU() {
-        return *cpu_core;
+        return cpu_cores[0]->CPU();
     }
 
     Tegra::GPU& GPU() {
@@ -118,7 +120,7 @@ public:
     }
 
     Kernel::Scheduler& Scheduler() {
-        return *scheduler;
+        return cpu_cores[0]->Scheduler();
     }
 
     Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
@@ -163,18 +165,12 @@ private:
      */
     ResultStatus Init(EmuWindow* emu_window, u32 system_mode);
 
-    /// Reschedule the core emulation
-    void Reschedule();
-
     /// AppLoader used to load the current executing application
     std::unique_ptr<Loader::AppLoader> app_loader;
 
-    std::shared_ptr<ARM_Interface> cpu_core;
-    std::unique_ptr<Kernel::Scheduler> scheduler;
+    std::array<std::unique_ptr<Cpu>, 4> cpu_cores;
     std::unique_ptr<Tegra::GPU> gpu_core;
-
     std::shared_ptr<Tegra::DebugContext> debug_context;
-
     Kernel::SharedPtr<Kernel::Process> current_process;
 
     /// When true, signals that a reschedule should happen
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
new file mode 100644
index 0000000000..81c0e212d0
--- /dev/null
+++ b/src/core/core_cpu.cpp
@@ -0,0 +1,72 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#ifdef ARCHITECTURE_x86_64
+#include "core/arm/dynarmic/arm_dynarmic.h"
+#endif
+#include "core/arm/unicorn/arm_unicorn.h"
+#include "core/core_cpu.h"
+#include "core/core_timing.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/scheduler.h"
+#include "core/hle/kernel/thread.h"
+#include "core/settings.h"
+
+namespace Core {
+
+Cpu::Cpu() {
+    if (Settings::values.use_cpu_jit) {
+#ifdef ARCHITECTURE_x86_64
+        arm_interface = std::make_shared<ARM_Dynarmic>();
+#else
+        cpu_core = std::make_shared<ARM_Unicorn>();
+        NGLOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
+#endif
+    } else {
+        arm_interface = std::make_shared<ARM_Unicorn>();
+    }
+
+    scheduler = std::make_unique<Kernel::Scheduler>(arm_interface.get());
+}
+
+void Cpu::RunLoop(bool tight_loop) {
+    // If we don't have a currently active thread then don't execute instructions,
+    // instead advance to the next event and try to yield to the next thread
+    if (Kernel::GetCurrentThread() == nullptr) {
+        NGLOG_TRACE(Core, "Idling");
+        CoreTiming::Idle();
+        CoreTiming::Advance();
+        PrepareReschedule();
+    } else {
+        CoreTiming::Advance();
+        if (tight_loop) {
+            arm_interface->Run();
+        } else {
+            arm_interface->Step();
+        }
+    }
+
+    Reschedule();
+}
+
+void Cpu::SingleStep() {
+    return RunLoop(false);
+}
+
+void Cpu::PrepareReschedule() {
+    arm_interface->PrepareReschedule();
+    reschedule_pending = true;
+}
+
+void Cpu::Reschedule() {
+    if (!reschedule_pending) {
+        return;
+    }
+
+    reschedule_pending = false;
+    scheduler->Reschedule();
+}
+
+} // namespace Core
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
new file mode 100644
index 0000000000..312db16557
--- /dev/null
+++ b/src/core/core_cpu.h
@@ -0,0 +1,46 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include "common/common_types.h"
+
+class ARM_Interface;
+
+namespace Kernel {
+class Scheduler;
+}
+
+namespace Core {
+
+class Cpu {
+public:
+    Cpu();
+
+    void RunLoop(bool tight_loop = true);
+
+    void SingleStep();
+
+    void PrepareReschedule();
+
+    ARM_Interface& CPU() {
+        return *arm_interface;
+    }
+
+    Kernel::Scheduler& Scheduler() {
+        return *scheduler;
+    }
+
+private:
+    void Reschedule();
+
+    std::shared_ptr<ARM_Interface> arm_interface;
+    std::unique_ptr<Kernel::Scheduler> scheduler;
+
+    bool reschedule_pending{};
+};
+
+} // namespace Core