summaryrefslogtreecommitdiffstats
path: root/src/core/core_cpu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/core_cpu.cpp')
-rw-r--r--src/core/core_cpu.cpp119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
new file mode 100644
index 000000000..099f2bb1a
--- /dev/null
+++ b/src/core/core_cpu.cpp
@@ -0,0 +1,119 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <condition_variable>
+#include <mutex>
+
+#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 {
+
+void CpuBarrier::NotifyEnd() {
+ std::unique_lock<std::mutex> lock(mutex);
+ end = true;
+ condition.notify_all();
+}
+
+bool CpuBarrier::Rendezvous() {
+ if (!Settings::values.use_multi_core) {
+ // Meaningless when running in single-core mode
+ return true;
+ }
+
+ if (!end) {
+ std::unique_lock<std::mutex> lock(mutex);
+
+ --cores_waiting;
+ if (!cores_waiting) {
+ cores_waiting = NUM_CPU_CORES;
+ condition.notify_all();
+ return true;
+ }
+
+ condition.wait(lock);
+ return true;
+ }
+
+ return false;
+}
+
+Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index)
+ : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
+
+ 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_shared<Kernel::Scheduler>(arm_interface.get());
+}
+
+void Cpu::RunLoop(bool tight_loop) {
+ // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
+ if (!cpu_barrier->Rendezvous()) {
+ // If rendezvous failed, session has been killed
+ return;
+ }
+
+ // 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, "Core-{} idling", core_index);
+
+ if (IsMainCore()) {
+ CoreTiming::Idle();
+ CoreTiming::Advance();
+ }
+
+ PrepareReschedule();
+ } else {
+ if (IsMainCore()) {
+ 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