From bd983414f643b734a1f8bebe3183723733344f72 Mon Sep 17 00:00:00 2001
From: Lioncash <mathew1800@gmail.com>
Date: Thu, 14 Feb 2019 12:42:58 -0500
Subject: [PATCH] core_timing: Convert core timing into a class

Gets rid of the largest set of mutable global state within the core.
This also paves a way for eliminating usages of GetInstance() on the
System class as a follow-up.

Note that no behavioral changes have been made, and this simply extracts
the functionality into a class. This also has the benefit of making
dependencies on the core timing functionality explicit within the
relevant interfaces.
---
 src/audio_core/audio_out.cpp                  |   5 +-
 src/audio_core/audio_out.h                    |   8 +-
 src/audio_core/audio_renderer.cpp             |   7 +-
 src/audio_core/audio_renderer.h               |   6 +-
 src/audio_core/stream.cpp                     |  11 +-
 src/audio_core/stream.h                       |   8 +-
 src/core/arm/dynarmic/arm_dynarmic.cpp        |  12 +-
 src/core/arm/dynarmic/arm_dynarmic.h          |   8 +-
 src/core/arm/unicorn/arm_unicorn.cpp          |   6 +-
 src/core/arm/unicorn/arm_unicorn.h            |   8 +-
 src/core/core.cpp                             |  19 +-
 src/core/core.h                               |  10 +
 src/core/core_cpu.cpp                         |  15 +-
 src/core/core_cpu.h                           |   8 +-
 src/core/core_timing.cpp                      | 203 ++++++++---------
 src/core/core_timing.h                        | 177 ++++++++++-----
 src/core/cpu_core_manager.cpp                 |   3 +-
 src/core/hle/kernel/kernel.cpp                |  12 +-
 src/core/hle/kernel/kernel.h                  |   9 +-
 src/core/hle/kernel/scheduler.cpp             |   2 +-
 src/core/hle/kernel/svc.cpp                   |  10 +-
 src/core/hle/kernel/thread.cpp                |  19 +-
 src/core/hle/service/audio/audout_u.cpp       |  10 +-
 src/core/hle/service/audio/audren_u.cpp       |   9 +-
 .../service/hid/controllers/controller_base.h |   7 +-
 .../hle/service/hid/controllers/debug_pad.cpp |   5 +-
 .../hle/service/hid/controllers/debug_pad.h   |   2 +-
 .../hle/service/hid/controllers/gesture.cpp   |   5 +-
 .../hle/service/hid/controllers/gesture.h     |   2 +-
 .../hle/service/hid/controllers/keyboard.cpp  |   5 +-
 .../hle/service/hid/controllers/keyboard.h    |   2 +-
 .../hle/service/hid/controllers/mouse.cpp     |   5 +-
 src/core/hle/service/hid/controllers/mouse.h  |   2 +-
 src/core/hle/service/hid/controllers/npad.cpp |   5 +-
 src/core/hle/service/hid/controllers/npad.h   |   2 +-
 .../hle/service/hid/controllers/stubbed.cpp   |   5 +-
 .../hle/service/hid/controllers/stubbed.h     |   2 +-
 .../service/hid/controllers/touchscreen.cpp   |   7 +-
 .../hle/service/hid/controllers/touchscreen.h |   2 +-
 src/core/hle/service/hid/controllers/xpad.cpp |   5 +-
 src/core/hle/service/hid/controllers/xpad.h   |   2 +-
 src/core/hle/service/hid/hid.cpp              |  18 +-
 src/core/hle/service/hid/irs.cpp              |   2 +-
 .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp |   3 +-
 src/core/hle/service/nvflinger/nvflinger.cpp  |  10 +-
 src/core/hle/service/nvflinger/nvflinger.h    |   8 +-
 src/core/hle/service/service.cpp              |   5 +-
 src/core/hle/service/service.h                |  15 +-
 src/core/hle/service/time/time.cpp            |   7 +-
 src/tests/core/core_timing.cpp                | 211 +++++++++---------
 src/video_core/engines/maxwell_3d.cpp         |   2 +-
 src/video_core/gpu.cpp                        |   3 +-
 .../renderer_opengl/renderer_opengl.cpp       |   2 +-
 53 files changed, 536 insertions(+), 400 deletions(-)

diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp
index 50d2a1ed3..8619a3f03 100644
--- a/src/audio_core/audio_out.cpp
+++ b/src/audio_core/audio_out.cpp
@@ -26,14 +26,15 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
     return {};
 }
 
-StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
+StreamPtr AudioOut::OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate,
+                               u32 num_channels, std::string&& name,
                                Stream::ReleaseCallback&& release_callback) {
     if (!sink) {
         sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id);
     }
 
     return std::make_shared<Stream>(
-        sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
+        core_timing, sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
         sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
 }
 
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h
index df9607ac7..b07588287 100644
--- a/src/audio_core/audio_out.h
+++ b/src/audio_core/audio_out.h
@@ -13,6 +13,10 @@
 #include "audio_core/stream.h"
 #include "common/common_types.h"
 
+namespace Core::Timing {
+class CoreTiming;
+}
+
 namespace AudioCore {
 
 /**
@@ -21,8 +25,8 @@ namespace AudioCore {
 class AudioOut {
 public:
     /// Opens a new audio stream
-    StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
-                         Stream::ReleaseCallback&& release_callback);
+    StreamPtr OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, u32 num_channels,
+                         std::string&& name, Stream::ReleaseCallback&& release_callback);
 
     /// Returns a vector of recently released buffers specified by tag for the specified stream
     std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 00c026511..9a0939883 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -8,6 +8,7 @@
 #include "audio_core/codec.h"
 #include "common/assert.h"
 #include "common/logging/log.h"
+#include "core/core.h"
 #include "core/hle/kernel/writable_event.h"
 #include "core/memory.h"
 
@@ -71,14 +72,14 @@ private:
     EffectOutStatus out_status{};
     EffectInStatus info{};
 };
-AudioRenderer::AudioRenderer(AudioRendererParameter params,
+AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
                              Kernel::SharedPtr<Kernel::WritableEvent> buffer_event)
     : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
       effects(params.effect_count) {
 
     audio_out = std::make_unique<AudioCore::AudioOut>();
-    stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
-                                   [=]() { buffer_event->Signal(); });
+    stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
+                                   "AudioRenderer", [=]() { buffer_event->Signal(); });
     audio_out->StartStream(stream);
 
     QueueMixedBuffer(0);
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 7826881bf..201ec7a3c 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -14,6 +14,10 @@
 #include "common/swap.h"
 #include "core/hle/kernel/object.h"
 
+namespace Core::Timing {
+class CoreTiming;
+}
+
 namespace Kernel {
 class WritableEvent;
 }
@@ -208,7 +212,7 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
 
 class AudioRenderer {
 public:
-    AudioRenderer(AudioRendererParameter params,
+    AudioRenderer(Core::Timing::CoreTiming& core_timing, AudioRendererParameter params,
                   Kernel::SharedPtr<Kernel::WritableEvent> buffer_event);
     ~AudioRenderer();
 
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 8ab5649df..d89ff30b7 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -32,12 +32,12 @@ u32 Stream::GetNumChannels() const {
     return {};
 }
 
-Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
-               SinkStream& sink_stream, std::string&& name_)
+Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
+               ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_)
     : sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
-      sink_stream{sink_stream}, name{std::move(name_)} {
+      sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
 
-    release_event = Core::Timing::RegisterEvent(
+    release_event = core_timing.RegisterEvent(
         name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
 }
 
@@ -99,8 +99,7 @@ void Stream::PlayNextBuffer() {
 
     sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
 
-    Core::Timing::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event,
-                                          {});
+    core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
 }
 
 void Stream::ReleaseActiveBuffer() {
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h
index caa775544..05071243b 100644
--- a/src/audio_core/stream.h
+++ b/src/audio_core/stream.h
@@ -14,8 +14,9 @@
 #include "common/common_types.h"
 
 namespace Core::Timing {
+class CoreTiming;
 struct EventType;
-}
+} // namespace Core::Timing
 
 namespace AudioCore {
 
@@ -42,8 +43,8 @@ public:
     /// Callback function type, used to change guest state on a buffer being released
     using ReleaseCallback = std::function<void()>;
 
-    Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
-           SinkStream& sink_stream, std::string&& name_);
+    Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
+           ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_);
 
     /// Plays the audio stream
     void Play();
@@ -100,6 +101,7 @@ private:
     std::queue<BufferPtr> queued_buffers;     ///< Buffers queued to be played in the stream
     std::queue<BufferPtr> released_buffers;   ///< Buffers recently released from the stream
     SinkStream& sink_stream;                  ///< Output sink for the stream
+    Core::Timing::CoreTiming& core_timing;    ///< Core timing instance.
     std::string name;                         ///< Name of the stream, must be unique
 };
 
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index f28951f8a..9b7ca4030 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -112,14 +112,14 @@ public:
         // Always execute at least one tick.
         amortized_ticks = std::max<u64>(amortized_ticks, 1);
 
-        Timing::AddTicks(amortized_ticks);
+        parent.core_timing.AddTicks(amortized_ticks);
         num_interpreted_instructions = 0;
     }
     u64 GetTicksRemaining() override {
-        return std::max(Timing::GetDowncount(), 0);
+        return std::max(parent.core_timing.GetDowncount(), 0);
     }
     u64 GetCNTPCT() override {
-        return Timing::GetTicks();
+        return parent.core_timing.GetTicks();
     }
 
     ARM_Dynarmic& parent;
@@ -172,8 +172,10 @@ void ARM_Dynarmic::Step() {
     cb->InterpreterFallback(jit->GetPC(), 1);
 }
 
-ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index)
-    : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
+ARM_Dynarmic::ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
+                           std::size_t core_index)
+    : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), inner_unicorn{core_timing},
+      core_index{core_index}, core_timing{core_timing},
       exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
     ThreadContext ctx{};
     inner_unicorn.SaveContext(ctx);
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index 512bf8ce9..6cc458296 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -16,6 +16,10 @@ namespace Memory {
 struct PageTable;
 }
 
+namespace Core::Timing {
+class CoreTiming;
+}
+
 namespace Core {
 
 class ARM_Dynarmic_Callbacks;
@@ -23,7 +27,8 @@ class DynarmicExclusiveMonitor;
 
 class ARM_Dynarmic final : public ARM_Interface {
 public:
-    ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
+    ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
+                 std::size_t core_index);
     ~ARM_Dynarmic();
 
     void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -62,6 +67,7 @@ private:
     ARM_Unicorn inner_unicorn;
 
     std::size_t core_index;
+    Timing::CoreTiming& core_timing;
     DynarmicExclusiveMonitor& exclusive_monitor;
 
     Memory::PageTable* current_page_table = nullptr;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index c36c15c02..a542a098b 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -72,7 +72,7 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
     return {};
 }
 
-ARM_Unicorn::ARM_Unicorn() {
+ARM_Unicorn::ARM_Unicorn(Timing::CoreTiming& core_timing) : core_timing{core_timing} {
     CHECKED(uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc));
 
     auto fpv = 3 << 20;
@@ -177,7 +177,7 @@ void ARM_Unicorn::Run() {
     if (GDBStub::IsServerEnabled()) {
         ExecuteInstructions(std::max(4000000, 0));
     } else {
-        ExecuteInstructions(std::max(Timing::GetDowncount(), 0));
+        ExecuteInstructions(std::max(core_timing.GetDowncount(), 0));
     }
 }
 
@@ -190,7 +190,7 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
 void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
     MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
     CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
-    Timing::AddTicks(num_instructions);
+    core_timing.AddTicks(num_instructions);
     if (GDBStub::IsServerEnabled()) {
         if (last_bkpt_hit) {
             uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index 75761950b..dbd6955ea 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -9,12 +9,17 @@
 #include "core/arm/arm_interface.h"
 #include "core/gdbstub/gdbstub.h"
 
+namespace Core::Timing {
+class CoreTiming;
+}
+
 namespace Core {
 
 class ARM_Unicorn final : public ARM_Interface {
 public:
-    ARM_Unicorn();
+    explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
     ~ARM_Unicorn();
+
     void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
                           Kernel::VMAPermission perms) override;
     void UnmapMemory(VAddr address, std::size_t size) override;
@@ -43,6 +48,7 @@ public:
 
 private:
     uc_engine* uc{};
+    Timing::CoreTiming& core_timing;
     GDBStub::BreakpointAddress last_bkpt{};
     bool last_bkpt_hit;
 };
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 4d9d21ee4..8aa0932c5 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -94,8 +94,8 @@ struct System::Impl {
     ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
         LOG_DEBUG(HW_Memory, "initialized OK");
 
-        Timing::Init();
-        kernel.Initialize();
+        core_timing.Initialize();
+        kernel.Initialize(core_timing);
 
         const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
             std::chrono::system_clock::now().time_since_epoch());
@@ -120,7 +120,7 @@ struct System::Impl {
         telemetry_session = std::make_unique<Core::TelemetrySession>();
         service_manager = std::make_shared<Service::SM::ServiceManager>();
 
-        Service::Init(service_manager, *virtual_filesystem);
+        Service::Init(service_manager, system, *virtual_filesystem);
         GDBStub::Init();
 
         renderer = VideoCore::CreateRenderer(emu_window, system);
@@ -205,7 +205,7 @@ struct System::Impl {
 
         // Shutdown kernel and core timing
         kernel.Shutdown();
-        Timing::Shutdown();
+        core_timing.Shutdown();
 
         // Close app loader
         app_loader.reset();
@@ -232,9 +232,10 @@ struct System::Impl {
     }
 
     PerfStatsResults GetAndResetPerfStats() {
-        return perf_stats.GetAndResetStats(Timing::GetGlobalTimeUs());
+        return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs());
     }
 
+    Timing::CoreTiming core_timing;
     Kernel::KernelCore kernel;
     /// RealVfsFilesystem instance
     FileSys::VirtualFilesystem virtual_filesystem;
@@ -396,6 +397,14 @@ const Kernel::KernelCore& System::Kernel() const {
     return impl->kernel;
 }
 
+Timing::CoreTiming& System::CoreTiming() {
+    return impl->core_timing;
+}
+
+const Timing::CoreTiming& System::CoreTiming() const {
+    return impl->core_timing;
+}
+
 Core::PerfStats& System::GetPerfStats() {
     return impl->perf_stats;
 }
diff --git a/src/core/core.h b/src/core/core.h
index 511a5ad3a..d720013f7 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -47,6 +47,10 @@ namespace VideoCore {
 class RendererBase;
 } // namespace VideoCore
 
+namespace Core::Timing {
+class CoreTiming;
+}
+
 namespace Core {
 
 class ARM_Interface;
@@ -205,6 +209,12 @@ public:
     /// Provides a constant pointer to the current process.
     const Kernel::Process* CurrentProcess() const;
 
+    /// Provides a reference to the core timing instance.
+    Timing::CoreTiming& CoreTiming();
+
+    /// Provides a constant reference to the core timing instance.
+    const Timing::CoreTiming& CoreTiming() const;
+
     /// Provides a reference to the kernel instance.
     Kernel::KernelCore& Kernel();
 
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 452366250..54aa21a3a 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -49,17 +49,18 @@ bool CpuBarrier::Rendezvous() {
     return false;
 }
 
-Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index)
-    : cpu_barrier{cpu_barrier}, core_index{core_index} {
+Cpu::Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
+         CpuBarrier& cpu_barrier, std::size_t core_index)
+    : cpu_barrier{cpu_barrier}, core_timing{core_timing}, core_index{core_index} {
     if (Settings::values.use_cpu_jit) {
 #ifdef ARCHITECTURE_x86_64
-        arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
+        arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
 #else
         arm_interface = std::make_unique<ARM_Unicorn>();
         LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
 #endif
     } else {
-        arm_interface = std::make_unique<ARM_Unicorn>();
+        arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
     }
 
     scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
@@ -93,14 +94,14 @@ void Cpu::RunLoop(bool tight_loop) {
 
         if (IsMainCore()) {
             // TODO(Subv): Only let CoreTiming idle if all 4 cores are idling.
-            Timing::Idle();
-            Timing::Advance();
+            core_timing.Idle();
+            core_timing.Advance();
         }
 
         PrepareReschedule();
     } else {
         if (IsMainCore()) {
-            Timing::Advance();
+            core_timing.Advance();
         }
 
         if (tight_loop) {
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index 1d2bdc6cd..e2204c6b0 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -15,6 +15,10 @@ namespace Kernel {
 class Scheduler;
 }
 
+namespace Core::Timing {
+class CoreTiming;
+}
+
 namespace Core {
 
 class ARM_Interface;
@@ -41,7 +45,8 @@ private:
 
 class Cpu {
 public:
-    Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index);
+    Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
+        CpuBarrier& cpu_barrier, std::size_t core_index);
     ~Cpu();
 
     void RunLoop(bool tight_loop = true);
@@ -82,6 +87,7 @@ private:
     std::unique_ptr<ARM_Interface> arm_interface;
     CpuBarrier& cpu_barrier;
     std::unique_ptr<Kernel::Scheduler> scheduler;
+    Timing::CoreTiming& core_timing;
 
     std::atomic<bool> reschedule_pending = false;
     std::size_t core_index;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 4ea00c277..a0dd5db24 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -8,69 +8,60 @@
 #include <mutex>
 #include <string>
 #include <tuple>
-#include <unordered_map>
-#include <vector>
+
 #include "common/assert.h"
 #include "common/thread.h"
-#include "common/threadsafe_queue.h"
 #include "core/core_timing_util.h"
 
 namespace Core::Timing {
 
-static s64 global_timer;
-static int slice_length;
-static int downcount;
+constexpr int MAX_SLICE_LENGTH = 20000;
 
-struct EventType {
-    TimedCallback callback;
-    const std::string* name;
-};
-
-struct Event {
+struct CoreTiming::Event {
     s64 time;
     u64 fifo_order;
     u64 userdata;
     const EventType* type;
+
+    // Sort by time, unless the times are the same, in which case sort by
+    // the order added to the queue
+    friend bool operator>(const Event& left, const Event& right) {
+        return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
+    }
+
+    friend bool operator<(const Event& left, const Event& right) {
+        return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
+    }
 };
 
-// Sort by time, unless the times are the same, in which case sort by the order added to the queue
-static bool operator>(const Event& left, const Event& right) {
-    return std::tie(left.time, left.fifo_order) > std::tie(right.time, right.fifo_order);
+CoreTiming::CoreTiming() = default;
+CoreTiming::~CoreTiming() = default;
+
+void CoreTiming::Initialize() {
+    downcount = MAX_SLICE_LENGTH;
+    slice_length = MAX_SLICE_LENGTH;
+    global_timer = 0;
+    idled_cycles = 0;
+
+    // The time between CoreTiming being initialized and the first call to Advance() is considered
+    // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
+    // executing the first cycle of each slice to prepare the slice length and downcount for
+    // that slice.
+    is_global_timer_sane = true;
+
+    event_fifo_id = 0;
+
+    const auto empty_timed_callback = [](u64, s64) {};
+    ev_lost = RegisterEvent("_lost_event", empty_timed_callback);
 }
 
-static bool operator<(const Event& left, const Event& right) {
-    return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order);
+void CoreTiming::Shutdown() {
+    MoveEvents();
+    ClearPendingEvents();
+    UnregisterAllEvents();
 }
 
-// unordered_map stores each element separately as a linked list node so pointers to elements
-// remain stable regardless of rehashes/resizing.
-static std::unordered_map<std::string, EventType> event_types;
-
-// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
-// We don't use std::priority_queue because we need to be able to serialize, unserialize and
-// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated
-// by the standard adaptor class.
-static std::vector<Event> event_queue;
-static u64 event_fifo_id;
-// the queue for storing the events from other threads threadsafe until they will be added
-// to the event_queue by the emu thread
-static Common::MPSCQueue<Event> ts_queue;
-
-// the queue for unscheduling the events from other threads threadsafe
-static Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
-
-constexpr int MAX_SLICE_LENGTH = 20000;
-
-static s64 idled_cycles;
-
-// Are we in a function that has been called from Advance()
-// If events are sheduled from a function that gets called from Advance(),
-// don't change slice_length and downcount.
-static bool is_global_timer_sane;
-
-static EventType* ev_lost = nullptr;
-
-EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
+EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
     // check for existing type with same name.
     // we want event type names to remain unique so that we can use them for serialization.
     ASSERT_MSG(event_types.find(name) == event_types.end(),
@@ -84,73 +75,31 @@ EventType* RegisterEvent(const std::string& name, TimedCallback callback) {
     return event_type;
 }
 
-void UnregisterAllEvents() {
+void CoreTiming::UnregisterAllEvents() {
     ASSERT_MSG(event_queue.empty(), "Cannot unregister events with events pending");
     event_types.clear();
 }
 
-void Init() {
-    downcount = MAX_SLICE_LENGTH;
-    slice_length = MAX_SLICE_LENGTH;
-    global_timer = 0;
-    idled_cycles = 0;
-
-    // The time between CoreTiming being intialized and the first call to Advance() is considered
-    // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before
-    // executing the first cycle of each slice to prepare the slice length and downcount for
-    // that slice.
-    is_global_timer_sane = true;
-
-    event_fifo_id = 0;
-
-    const auto empty_timed_callback = [](u64, s64) {};
-    ev_lost = RegisterEvent("_lost_event", empty_timed_callback);
-}
-
-void Shutdown() {
-    MoveEvents();
-    ClearPendingEvents();
-    UnregisterAllEvents();
-}
-
-// This should only be called from the CPU thread. If you are calling
-// it from any other thread, you are doing something evil
-u64 GetTicks() {
-    u64 ticks = static_cast<u64>(global_timer);
-    if (!is_global_timer_sane) {
-        ticks += slice_length - downcount;
-    }
-    return ticks;
-}
-
-void AddTicks(u64 ticks) {
-    downcount -= static_cast<int>(ticks);
-}
-
-u64 GetIdleTicks() {
-    return static_cast<u64>(idled_cycles);
-}
-
-void ClearPendingEvents() {
-    event_queue.clear();
-}
-
-void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
+void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
     ASSERT(event_type != nullptr);
-    s64 timeout = GetTicks() + cycles_into_future;
+    const s64 timeout = GetTicks() + cycles_into_future;
+
     // If this event needs to be scheduled before the next advance(), force one early
-    if (!is_global_timer_sane)
+    if (!is_global_timer_sane) {
         ForceExceptionCheck(cycles_into_future);
+    }
+
     event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
     std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
 }
 
-void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
+void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
+                                         u64 userdata) {
     ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
 }
 
-void UnscheduleEvent(const EventType* event_type, u64 userdata) {
-    auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
+void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
+    const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
         return e.type == event_type && e.userdata == userdata;
     });
 
@@ -161,13 +110,33 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) {
     }
 }
 
-void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
+void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
     unschedule_queue.Push(std::make_pair(event_type, userdata));
 }
 
-void RemoveEvent(const EventType* event_type) {
-    auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
-                              [&](const Event& e) { return e.type == event_type; });
+u64 CoreTiming::GetTicks() const {
+    u64 ticks = static_cast<u64>(global_timer);
+    if (!is_global_timer_sane) {
+        ticks += slice_length - downcount;
+    }
+    return ticks;
+}
+
+u64 CoreTiming::GetIdleTicks() const {
+    return static_cast<u64>(idled_cycles);
+}
+
+void CoreTiming::AddTicks(u64 ticks) {
+    downcount -= static_cast<int>(ticks);
+}
+
+void CoreTiming::ClearPendingEvents() {
+    event_queue.clear();
+}
+
+void CoreTiming::RemoveEvent(const EventType* event_type) {
+    const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
+                                    [&](const Event& e) { return e.type == event_type; });
 
     // Removing random items breaks the invariant so we have to re-establish it.
     if (itr != event_queue.end()) {
@@ -176,22 +145,24 @@ void RemoveEvent(const EventType* event_type) {
     }
 }
 
-void RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
+void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
     MoveEvents();
     RemoveEvent(event_type);
 }
 
-void ForceExceptionCheck(s64 cycles) {
+void CoreTiming::ForceExceptionCheck(s64 cycles) {
     cycles = std::max<s64>(0, cycles);
-    if (downcount > cycles) {
-        // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int
-        // here. Account for cycles already executed by adjusting the g.slice_length
-        slice_length -= downcount - static_cast<int>(cycles);
-        downcount = static_cast<int>(cycles);
+    if (downcount <= cycles) {
+        return;
     }
+
+    // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int
+    // here. Account for cycles already executed by adjusting the g.slice_length
+    slice_length -= downcount - static_cast<int>(cycles);
+    downcount = static_cast<int>(cycles);
 }
 
-void MoveEvents() {
+void CoreTiming::MoveEvents() {
     for (Event ev; ts_queue.Pop(ev);) {
         ev.fifo_order = event_fifo_id++;
         event_queue.emplace_back(std::move(ev));
@@ -199,13 +170,13 @@ void MoveEvents() {
     }
 }
 
-void Advance() {
+void CoreTiming::Advance() {
     MoveEvents();
     for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
         UnscheduleEvent(ev.first, ev.second);
     }
 
-    int cycles_executed = slice_length - downcount;
+    const int cycles_executed = slice_length - downcount;
     global_timer += cycles_executed;
     slice_length = MAX_SLICE_LENGTH;
 
@@ -229,16 +200,16 @@ void Advance() {
     downcount = slice_length;
 }
 
-void Idle() {
+void CoreTiming::Idle() {
     idled_cycles += downcount;
     downcount = 0;
 }
 
-std::chrono::microseconds GetGlobalTimeUs() {
+std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
     return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE};
 }
 
-int GetDowncount() {
+int CoreTiming::GetDowncount() const {
     return downcount;
 }
 
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 093989d4c..59163bae1 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -4,6 +4,27 @@
 
 #pragma once
 
+#include <chrono>
+#include <functional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include "common/common_types.h"
+#include "common/threadsafe_queue.h"
+
+namespace Core::Timing {
+
+/// A callback that may be scheduled for a particular core timing event.
+using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
+
+/// Contains the characteristics of a particular event.
+struct EventType {
+    /// The event's callback function.
+    TimedCallback callback;
+    /// A pointer to the name of the event.
+    const std::string* name;
+};
+
 /**
  * This is a system to schedule events into the emulated machine's future. Time is measured
  * in main CPU clock cycles.
@@ -16,80 +37,120 @@
  * inside callback:
  *   ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
  */
+class CoreTiming {
+public:
+    CoreTiming();
+    ~CoreTiming();
 
-#include <chrono>
-#include <functional>
-#include <string>
-#include "common/common_types.h"
+    CoreTiming(const CoreTiming&) = delete;
+    CoreTiming(CoreTiming&&) = delete;
 
-namespace Core::Timing {
+    CoreTiming& operator=(const CoreTiming&) = delete;
+    CoreTiming& operator=(CoreTiming&&) = delete;
 
-struct EventType;
+    /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
+    /// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
+    void Initialize();
 
-using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
+    /// Tears down all timing related functionality.
+    void Shutdown();
 
-/**
- * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
- * required to end slice -1 and start slice 0 before the first cycle of code is executed.
- */
-void Init();
-void Shutdown();
+    /// Registers a core timing event with the given name and callback.
+    ///
+    /// @param name     The name of the core timing event to register.
+    /// @param callback The callback to execute for the event.
+    ///
+    /// @returns An EventType instance representing the registered event.
+    ///
+    /// @pre The name of the event being registered must be unique among all
+    ///      registered events.
+    ///
+    EventType* RegisterEvent(const std::string& name, TimedCallback callback);
 
-/**
- * This should only be called from the emu thread, if you are calling it any other thread, you are
- * doing something evil
- */
-u64 GetTicks();
-u64 GetIdleTicks();
-void AddTicks(u64 ticks);
+    /// Unregisters all registered events thus far.
+    void UnregisterAllEvents();
 
-/**
- * Returns the event_type identifier. if name is not unique, it will assert.
- */
-EventType* RegisterEvent(const std::string& name, TimedCallback callback);
-void UnregisterAllEvents();
+    /// After the first Advance, the slice lengths and the downcount will be reduced whenever an
+    /// event is scheduled earlier than the current values.
+    ///
+    /// Scheduling from a callback will not update the downcount until the Advance() completes.
+    void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
 
-/**
- * After the first Advance, the slice lengths and the downcount will be reduced whenever an event
- * is scheduled earlier than the current values.
- * Scheduling from a callback will not update the downcount until the Advance() completes.
- */
-void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
+    /// This is to be called when outside of hle threads, such as the graphics thread, wants to
+    /// schedule things to be executed on the main thread.
+    ///
+    /// @note This doesn't change slice_length and thus events scheduled by this might be
+    /// called with a delay of up to MAX_SLICE_LENGTH
+    void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
+                                 u64 userdata = 0);
 
-/**
- * This is to be called when outside of hle threads, such as the graphics thread, wants to
- * schedule things to be executed on the main thread.
- * Not that this doesn't change slice_length and thus events scheduled by this might be called
- * with a delay of up to MAX_SLICE_LENGTH
- */
-void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata);
+    void UnscheduleEvent(const EventType* event_type, u64 userdata);
+    void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
 
-void UnscheduleEvent(const EventType* event_type, u64 userdata);
-void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
+    /// We only permit one event of each type in the queue at a time.
+    void RemoveEvent(const EventType* event_type);
+    void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
 
-/// We only permit one event of each type in the queue at a time.
-void RemoveEvent(const EventType* event_type);
-void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
+    void ForceExceptionCheck(s64 cycles);
 
-/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
- * the previous timing slice and begins the next one, you must Advance from the previous
- * slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
- * Advance() is required to initialize the slice length before the first cycle of emulated
- * instructions is executed.
- */
-void Advance();
-void MoveEvents();
+    /// This should only be called from the emu thread, if you are calling it any other thread,
+    /// you are doing something evil
+    u64 GetTicks() const;
 
-/// Pretend that the main CPU has executed enough cycles to reach the next event.
-void Idle();
+    u64 GetIdleTicks() const;
 
-/// Clear all pending events. This should ONLY be done on exit.
-void ClearPendingEvents();
+    void AddTicks(u64 ticks);
 
-void ForceExceptionCheck(s64 cycles);
+    /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
+    /// the previous timing slice and begins the next one, you must Advance from the previous
+    /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
+    /// Advance() is required to initialize the slice length before the first cycle of emulated
+    /// instructions is executed.
+    void Advance();
 
-std::chrono::microseconds GetGlobalTimeUs();
+    /// Pretend that the main CPU has executed enough cycles to reach the next event.
+    void Idle();
 
-int GetDowncount();
+    std::chrono::microseconds GetGlobalTimeUs() const;
+
+    int GetDowncount() const;
+
+private:
+    struct Event;
+
+    /// Clear all pending events. This should ONLY be done on exit.
+    void ClearPendingEvents();
+    void MoveEvents();
+
+    s64 global_timer = 0;
+    s64 idled_cycles = 0;
+    int slice_length = 0;
+    int downcount = 0;
+
+    // Are we in a function that has been called from Advance()
+    // If events are scheduled from a function that gets called from Advance(),
+    // don't change slice_length and downcount.
+    bool is_global_timer_sane = false;
+
+    // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
+    // We don't use std::priority_queue because we need to be able to serialize, unserialize and
+    // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
+    // accomodated by the standard adaptor class.
+    std::vector<Event> event_queue;
+    u64 event_fifo_id = 0;
+
+    // Stores each element separately as a linked list node so pointers to elements
+    // remain stable regardless of rehashes/resizing.
+    std::unordered_map<std::string, EventType> event_types;
+
+    // The queue for storing the events from other threads threadsafe until they will be added
+    // to the event_queue by the emu thread
+    Common::MPSCQueue<Event> ts_queue;
+
+    // The queue for unscheduling the events from other threads threadsafe
+    Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
+
+    EventType* ev_lost = nullptr;
+};
 
 } // namespace Core::Timing
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
index 769a6fefa..2ddb3610d 100644
--- a/src/core/cpu_core_manager.cpp
+++ b/src/core/cpu_core_manager.cpp
@@ -27,7 +27,8 @@ void CpuCoreManager::Initialize(System& system) {
     exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
 
     for (std::size_t index = 0; index < cores.size(); ++index) {
-        cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
+        cores[index] =
+            std::make_unique<Cpu>(system.CoreTiming(), *exclusive_monitor, *barrier, index);
     }
 
     // Create threads for CPU cores 1-3, and build thread_to_cpu map
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 3721ae8fe..dd749eed4 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -86,11 +86,11 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
 }
 
 struct KernelCore::Impl {
-    void Initialize(KernelCore& kernel) {
+    void Initialize(KernelCore& kernel, Core::Timing::CoreTiming& core_timing) {
         Shutdown();
 
         InitializeSystemResourceLimit(kernel);
-        InitializeThreads();
+        InitializeThreads(core_timing);
     }
 
     void Shutdown() {
@@ -122,9 +122,9 @@ struct KernelCore::Impl {
         ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
     }
 
-    void InitializeThreads() {
+    void InitializeThreads(Core::Timing::CoreTiming& core_timing) {
         thread_wakeup_event_type =
-            Core::Timing::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
+            core_timing.RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
     }
 
     std::atomic<u32> next_object_id{0};
@@ -152,8 +152,8 @@ KernelCore::~KernelCore() {
     Shutdown();
 }
 
-void KernelCore::Initialize() {
-    impl->Initialize(*this);
+void KernelCore::Initialize(Core::Timing::CoreTiming& core_timing) {
+    impl->Initialize(*this, core_timing);
 }
 
 void KernelCore::Shutdown() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7406f107e..154bced42 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -12,8 +12,9 @@ template <typename T>
 class ResultVal;
 
 namespace Core::Timing {
+class CoreTiming;
 struct EventType;
-}
+} // namespace Core::Timing
 
 namespace Kernel {
 
@@ -39,7 +40,11 @@ public:
     KernelCore& operator=(KernelCore&&) = delete;
 
     /// Resets the kernel to a clean slate for use.
-    void Initialize();
+    ///
+    /// @param core_timing CoreTiming instance used to create any necessary
+    ///                    kernel-specific callback events.
+    ///
+    void Initialize(Core::Timing::CoreTiming& core_timing);
 
     /// Clears all resources in use by the kernel instance.
     void Shutdown();
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 9e2517e1b..44f30d070 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -111,7 +111,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
 
 void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
     const u64 prev_switch_ticks = last_context_switch_time;
-    const u64 most_recent_switch_ticks = Core::Timing::GetTicks();
+    const u64 most_recent_switch_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
     const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
 
     if (thread != nullptr) {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 5f040f79f..c5d399bab 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -918,6 +918,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
         }
 
         const auto& system = Core::System::GetInstance();
+        const auto& core_timing = system.CoreTiming();
         const auto& scheduler = system.CurrentScheduler();
         const auto* const current_thread = scheduler.GetCurrentThread();
         const bool same_thread = current_thread == thread;
@@ -927,9 +928,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
         if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
             const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
 
-            out_ticks = thread_ticks + (Core::Timing::GetTicks() - prev_ctx_ticks);
+            out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks);
         } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
-            out_ticks = Core::Timing::GetTicks() - prev_ctx_ticks;
+            out_ticks = core_timing.GetTicks() - prev_ctx_ticks;
         }
 
         *result = out_ticks;
@@ -1546,10 +1547,11 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
 static u64 GetSystemTick() {
     LOG_TRACE(Kernel_SVC, "called");
 
-    const u64 result{Core::Timing::GetTicks()};
+    auto& core_timing = Core::System::GetInstance().CoreTiming();
+    const u64 result{core_timing.GetTicks()};
 
     // Advance time to defeat dumb games that busy-wait for the frame to end.
-    Core::Timing::AddTicks(400);
+    core_timing.AddTicks(400);
 
     return result;
 }
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 7881c2b90..6661e2130 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -43,7 +43,8 @@ Thread::~Thread() = default;
 
 void Thread::Stop() {
     // Cancel any outstanding wakeup events for this thread
-    Core::Timing::UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), callback_handle);
+    Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
+                                                             callback_handle);
     kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
     callback_handle = 0;
 
@@ -85,13 +86,14 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
 
     // This function might be called from any thread so we have to be cautious and use the
     // thread-safe version of ScheduleEvent.
-    Core::Timing::ScheduleEventThreadsafe(Core::Timing::nsToCycles(nanoseconds),
-                                          kernel.ThreadWakeupCallbackEventType(), callback_handle);
+    Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
+        Core::Timing::nsToCycles(nanoseconds), kernel.ThreadWakeupCallbackEventType(),
+        callback_handle);
 }
 
 void Thread::CancelWakeupTimer() {
-    Core::Timing::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(),
-                                            callback_handle);
+    Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe(
+        kernel.ThreadWakeupCallbackEventType(), callback_handle);
 }
 
 static std::optional<s32> GetNextProcessorId(u64 mask) {
@@ -190,6 +192,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
         return ResultCode(-1);
     }
 
+    auto& system = Core::System::GetInstance();
     SharedPtr<Thread> thread(new Thread(kernel));
 
     thread->thread_id = kernel.CreateNewThreadID();
@@ -198,7 +201,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
     thread->stack_top = stack_top;
     thread->tpidr_el0 = 0;
     thread->nominal_priority = thread->current_priority = priority;
-    thread->last_running_ticks = Core::Timing::GetTicks();
+    thread->last_running_ticks = system.CoreTiming().GetTicks();
     thread->processor_id = processor_id;
     thread->ideal_core = processor_id;
     thread->affinity_mask = 1ULL << processor_id;
@@ -209,7 +212,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
     thread->name = std::move(name);
     thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
     thread->owner_process = &owner_process;
-    thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
+    thread->scheduler = &system.Scheduler(processor_id);
     thread->scheduler->AddThread(thread, priority);
     thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
 
@@ -258,7 +261,7 @@ void Thread::SetStatus(ThreadStatus new_status) {
     }
 
     if (status == ThreadStatus::Running) {
-        last_running_ticks = Core::Timing::GetTicks();
+        last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
     }
 
     status = new_status;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index dc6a6b188..6831c0735 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -68,12 +68,12 @@ public:
         RegisterHandlers(functions);
 
         // This is the event handle used to check if the audio buffer was released
-        auto& kernel = Core::System::GetInstance().Kernel();
-        buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
-                                                              "IAudioOutBufferReleased");
+        auto& system = Core::System::GetInstance();
+        buffer_event = Kernel::WritableEvent::CreateEventPair(
+            system.Kernel(), Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
 
-        stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
-                                       std::move(unique_name),
+        stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
+                                       audio_params.channel_count, std::move(unique_name),
                                        [=]() { buffer_event.writable->Signal(); });
     }
 
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 76cc48254..7e0cc64a8 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -42,10 +42,11 @@ public:
         // clang-format on
         RegisterHandlers(functions);
 
-        auto& kernel = Core::System::GetInstance().Kernel();
-        system_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
-                                                              "IAudioRenderer:SystemEvent");
-        renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event.writable);
+        auto& system = Core::System::GetInstance();
+        system_event = Kernel::WritableEvent::CreateEventPair(
+            system.Kernel(), Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
+        renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params,
+                                                              system_event.writable);
     }
 
 private:
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index f0e092b1b..5e5097a03 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -7,6 +7,10 @@
 #include "common/common_types.h"
 #include "common/swap.h"
 
+namespace Core::Timing {
+class CoreTiming;
+}
+
 namespace Service::HID {
 class ControllerBase {
 public:
@@ -20,7 +24,8 @@ public:
     virtual void OnRelease() = 0;
 
     // When the controller is requesting an update for the shared memory
-    virtual void OnUpdate(u8* data, std::size_t size) = 0;
+    virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+                          std::size_t size) = 0;
 
     // Called when input devices should be loaded
     virtual void OnLoadInputDevices() = 0;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index b264c9503..c5c2e032a 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -21,8 +21,9 @@ void Controller_DebugPad::OnInit() {}
 
 void Controller_DebugPad::OnRelease() {}
 
-void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
-    shared_memory.header.timestamp = Core::Timing::GetTicks();
+void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+                                   std::size_t size) {
+    shared_memory.header.timestamp = core_timing.GetTicks();
     shared_memory.header.total_entry_count = 17;
 
     if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 68b734248..929035034 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -26,7 +26,7 @@ public:
     void OnRelease() override;
 
     // When the controller is requesting an update for the shared memory
-    void OnUpdate(u8* data, std::size_t size) override;
+    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
     // Called when input devices should be loaded
     void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 6d21f1a7d..a179252e3 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -17,8 +17,9 @@ void Controller_Gesture::OnInit() {}
 
 void Controller_Gesture::OnRelease() {}
 
-void Controller_Gesture::OnUpdate(u8* data, std::size_t size) {
-    shared_memory.header.timestamp = Core::Timing::GetTicks();
+void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+                                  std::size_t size) {
+    shared_memory.header.timestamp = core_timing.GetTicks();
     shared_memory.header.total_entry_count = 17;
 
     if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 1056ffbcd..f305fe90f 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -22,7 +22,7 @@ public:
     void OnRelease() override;
 
     // When the controller is requesting an update for the shared memory
-    void OnUpdate(u8* data, size_t size) override;
+    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
 
     // Called when input devices should be loaded
     void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 798f30436..92d7bfb52 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -19,8 +19,9 @@ void Controller_Keyboard::OnInit() {}
 
 void Controller_Keyboard::OnRelease() {}
 
-void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
-    shared_memory.header.timestamp = Core::Timing::GetTicks();
+void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+                                   std::size_t size) {
+    shared_memory.header.timestamp = core_timing.GetTicks();
     shared_memory.header.total_entry_count = 17;
 
     if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index f52775456..73cd2c7bb 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -25,7 +25,7 @@ public:
     void OnRelease() override;
 
     // When the controller is requesting an update for the shared memory
-    void OnUpdate(u8* data, std::size_t size) override;
+    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
     // Called when input devices should be loaded
     void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 4985037be..11ab096d9 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -17,8 +17,9 @@ Controller_Mouse::~Controller_Mouse() = default;
 void Controller_Mouse::OnInit() {}
 void Controller_Mouse::OnRelease() {}
 
-void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
-    shared_memory.header.timestamp = Core::Timing::GetTicks();
+void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+                                std::size_t size) {
+    shared_memory.header.timestamp = core_timing.GetTicks();
     shared_memory.header.total_entry_count = 17;
 
     if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 70b654d07..9d46eecbe 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -24,7 +24,7 @@ public:
     void OnRelease() override;
 
     // When the controller is requesting an update for the shared memory
-    void OnUpdate(u8* data, std::size_t size) override;
+    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
     // Called when input devices should be loaded
     void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ffdd1c593..e7fc7a619 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -288,7 +288,8 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
     rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
 }
 
-void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
+void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+                               std::size_t data_len) {
     if (!IsControllerActivated())
         return;
     for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
@@ -308,7 +309,7 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
             const auto& last_entry =
                 main_controller->npad[main_controller->common.last_entry_index];
 
-            main_controller->common.timestamp = Core::Timing::GetTicks();
+            main_controller->common.timestamp = core_timing.GetTicks();
             main_controller->common.last_entry_index =
                 (main_controller->common.last_entry_index + 1) % 17;
 
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 106cf58c8..18c7a94e6 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -30,7 +30,7 @@ public:
     void OnRelease() override;
 
     // When the controller is requesting an update for the shared memory
-    void OnUpdate(u8* data, std::size_t size) override;
+    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
     // Called when input devices should be loaded
     void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index cca4dca1d..946948f5e 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -16,13 +16,14 @@ void Controller_Stubbed::OnInit() {}
 
 void Controller_Stubbed::OnRelease() {}
 
-void Controller_Stubbed::OnUpdate(u8* data, std::size_t size) {
+void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+                                  std::size_t size) {
     if (!smart_update) {
         return;
     }
 
     CommonHeader header{};
-    header.timestamp = Core::Timing::GetTicks();
+    header.timestamp = core_timing.GetTicks();
     header.total_entry_count = 17;
     header.entry_count = 0;
     header.last_entry_index = 0;
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 4a21c643e..24469f03e 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -20,7 +20,7 @@ public:
     void OnRelease() override;
 
     // When the controller is requesting an update for the shared memory
-    void OnUpdate(u8* data, std::size_t size) override;
+    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
     // Called when input devices should be loaded
     void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index a7c8acc72..1a8445a43 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -20,8 +20,9 @@ void Controller_Touchscreen::OnInit() {}
 
 void Controller_Touchscreen::OnRelease() {}
 
-void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
-    shared_memory.header.timestamp = Core::Timing::GetTicks();
+void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+                                      std::size_t size) {
+    shared_memory.header.timestamp = core_timing.GetTicks();
     shared_memory.header.total_entry_count = 17;
 
     if (!IsControllerActivated()) {
@@ -48,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
         touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
         touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
         touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
-        const u64 tick = Core::Timing::GetTicks();
+        const u64 tick = core_timing.GetTicks();
         touch_entry.delta_time = tick - last_touch;
         last_touch = tick;
         touch_entry.finger = Settings::values.touchscreen.finger;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 94cd0eba9..012b6e0dd 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -24,7 +24,7 @@ public:
     void OnRelease() override;
 
     // When the controller is requesting an update for the shared memory
-    void OnUpdate(u8* data, std::size_t size) override;
+    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
     // Called when input devices should be loaded
     void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index eff03d14e..1a9da9576 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -17,9 +17,10 @@ void Controller_XPad::OnInit() {}
 
 void Controller_XPad::OnRelease() {}
 
-void Controller_XPad::OnUpdate(u8* data, std::size_t size) {
+void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+                               std::size_t size) {
     for (auto& xpad_entry : shared_memory.shared_memory_entries) {
-        xpad_entry.header.timestamp = Core::Timing::GetTicks();
+        xpad_entry.header.timestamp = core_timing.GetTicks();
         xpad_entry.header.total_entry_count = 17;
 
         if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index ff836989f..2864e6617 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -22,7 +22,7 @@ public:
     void OnRelease() override;
 
     // When the controller is requesting an update for the shared memory
-    void OnUpdate(u8* data, std::size_t size) override;
+    void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
 
     // Called when input devices should be loaded
     void OnLoadInputDevices() override;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 79c320d04..8a6de83a2 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -73,13 +73,15 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
     GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
 
     // Register update callbacks
-    pad_update_event = Core::Timing::RegisterEvent(
-        "HID::UpdatePadCallback",
-        [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
+    auto& core_timing = Core::System::GetInstance().CoreTiming();
+    pad_update_event =
+        core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
+            UpdateControllers(userdata, cycles_late);
+        });
 
     // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
 
-    Core::Timing::ScheduleEvent(pad_update_ticks, pad_update_event);
+    core_timing.ScheduleEvent(pad_update_ticks, pad_update_event);
 
     ReloadInputDevices();
 }
@@ -93,7 +95,7 @@ void IAppletResource::DeactivateController(HidController controller) {
 }
 
 IAppletResource ::~IAppletResource() {
-    Core::Timing::UnscheduleEvent(pad_update_event, 0);
+    Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0);
 }
 
 void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
@@ -105,15 +107,17 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
 }
 
 void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
+    auto& core_timing = Core::System::GetInstance().CoreTiming();
+
     const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
     for (const auto& controller : controllers) {
         if (should_reload) {
             controller->OnLoadInputDevices();
         }
-        controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
+        controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
     }
 
-    Core::Timing::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
+    core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
 }
 
 class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index b427d4068..2c4625c99 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 5};
     rb.Push(RESULT_SUCCESS);
-    rb.PushRaw<u64>(Core::Timing::GetTicks());
+    rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks());
     rb.PushRaw<u32>(0);
 }
 
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 88d80ba06..45812d238 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -5,6 +5,7 @@
 #include <cstring>
 #include "common/assert.h"
 #include "common/logging/log.h"
+#include "core/core.h"
 #include "core/core_timing.h"
 #include "core/core_timing_util.h"
 #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
@@ -184,7 +185,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
 
     IoctlGetGpuTime params{};
     std::memcpy(&params, input.data(), input.size());
-    params.gpu_time = Core::Timing::cyclesToNs(Core::Timing::GetTicks());
+    params.gpu_time = Core::Timing::cyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks());
     std::memcpy(output.data(), &params, output.size());
     return 0;
 }
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index ce1b59860..3babc3f7c 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -27,19 +27,19 @@ namespace Service::NVFlinger {
 constexpr std::size_t SCREEN_REFRESH_RATE = 60;
 constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
 
-NVFlinger::NVFlinger() {
+NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
     // Schedule the screen composition events
     composition_event =
-        Core::Timing::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
+        core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
             Compose();
-            Core::Timing::ScheduleEvent(frame_ticks - cycles_late, composition_event);
+            this->core_timing.ScheduleEvent(frame_ticks - cycles_late, composition_event);
         });
 
-    Core::Timing::ScheduleEvent(frame_ticks, composition_event);
+    core_timing.ScheduleEvent(frame_ticks, composition_event);
 }
 
 NVFlinger::~NVFlinger() {
-    Core::Timing::UnscheduleEvent(composition_event, 0);
+    core_timing.UnscheduleEvent(composition_event, 0);
 }
 
 void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 6d8bcbd30..437aa592d 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -15,8 +15,9 @@
 #include "core/hle/kernel/object.h"
 
 namespace Core::Timing {
+class CoreTiming;
 struct EventType;
-}
+} // namespace Core::Timing
 
 namespace Kernel {
 class ReadableEvent;
@@ -52,7 +53,7 @@ struct Display {
 
 class NVFlinger final {
 public:
-    NVFlinger();
+    explicit NVFlinger(Core::Timing::CoreTiming& core_timing);
     ~NVFlinger();
 
     /// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -117,6 +118,9 @@ private:
 
     /// Event that handles screen composition.
     Core::Timing::EventType* composition_event;
+
+    /// Core timing instance for registering/unregistering the composition event.
+    Core::Timing::CoreTiming& core_timing;
 };
 
 } // namespace Service::NVFlinger
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index d25b80ab0..117f87a45 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -194,10 +194,11 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
 // Module interface
 
 /// Initialize ServiceManager
-void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) {
+void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
+          FileSys::VfsFilesystem& vfs) {
     // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
     // here and pass it into the respective InstallInterfaces functions.
-    auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
+    auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming());
 
     SM::ServiceManager::InstallInterfaces(sm);
 
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 029533628..830790269 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -14,6 +14,14 @@
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 // Namespace Service
 
+namespace Core {
+class System;
+}
+
+namespace FileSys {
+class VfsFilesystem;
+}
+
 namespace Kernel {
 class ClientPort;
 class ServerPort;
@@ -21,10 +29,6 @@ class ServerSession;
 class HLERequestContext;
 } // namespace Kernel
 
-namespace FileSys {
-class VfsFilesystem;
-}
-
 namespace Service {
 
 namespace SM {
@@ -178,7 +182,8 @@ private:
 };
 
 /// Initialize ServiceManager
-void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs);
+void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
+          FileSys::VfsFilesystem& vfs);
 
 /// Shutdown ServiceManager
 void Shutdown();
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index efebd1b24..aa115935d 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -5,6 +5,7 @@
 #include <chrono>
 #include <ctime>
 #include "common/logging/log.h"
+#include "core/core.h"
 #include "core/core_timing.h"
 #include "core/core_timing_util.h"
 #include "core/hle/ipc_helpers.h"
@@ -106,8 +107,9 @@ private:
     void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
         LOG_DEBUG(Service_Time, "called");
 
+        const auto& core_timing = Core::System::GetInstance().CoreTiming();
         const SteadyClockTimePoint steady_clock_time_point{
-            Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000};
+            Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000};
         IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
         rb.Push(RESULT_SUCCESS);
         rb.PushRaw(steady_clock_time_point);
@@ -281,8 +283,9 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
         return;
     }
 
+    const auto& core_timing = Core::System::GetInstance().CoreTiming();
     const SteadyClockTimePoint steady_clock_time_point{
-        Core::Timing::cyclesToMs(Core::Timing::GetTicks()) / 1000, {}};
+        Core::Timing::cyclesToMs(core_timing.GetTicks()) / 1000, {}};
 
     CalendarTime calendar_time{};
     calendar_time.year = tm->tm_year + 1900;
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 77607a755..340d6a272 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -28,100 +28,103 @@ void CallbackTemplate(u64 userdata, s64 cycles_late) {
     REQUIRE(lateness == cycles_late);
 }
 
-class ScopeInit final {
-public:
+struct ScopeInit final {
     ScopeInit() {
-        Core::Timing::Init();
+        core_timing.Initialize();
     }
     ~ScopeInit() {
-        Core::Timing::Shutdown();
+        core_timing.Shutdown();
     }
+
+    Core::Timing::CoreTiming core_timing;
 };
 
-static void AdvanceAndCheck(u32 idx, int downcount, int expected_lateness = 0,
-                            int cpu_downcount = 0) {
+static void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, int downcount,
+                            int expected_lateness = 0, int cpu_downcount = 0) {
     callbacks_ran_flags = 0;
     expected_callback = CB_IDS[idx];
     lateness = expected_lateness;
 
     // Pretend we executed X cycles of instructions.
-    Core::Timing::AddTicks(Core::Timing::GetDowncount() - cpu_downcount);
-    Core::Timing::Advance();
+    core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount);
+    core_timing.Advance();
 
     REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
-    REQUIRE(downcount == Core::Timing::GetDowncount());
+    REQUIRE(downcount == core_timing.GetDowncount());
 }
 
 TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
     ScopeInit guard;
+    auto& core_timing = guard.core_timing;
 
-    Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
-    Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
-    Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
-    Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
-    Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
+    Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
+    Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
+    Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
+    Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
+    Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
 
     // Enter slice 0
-    Core::Timing::Advance();
+    core_timing.Advance();
 
     // D -> B -> C -> A -> E
-    Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
-    REQUIRE(1000 == Core::Timing::GetDowncount());
-    Core::Timing::ScheduleEvent(500, cb_b, CB_IDS[1]);
-    REQUIRE(500 == Core::Timing::GetDowncount());
-    Core::Timing::ScheduleEvent(800, cb_c, CB_IDS[2]);
-    REQUIRE(500 == Core::Timing::GetDowncount());
-    Core::Timing::ScheduleEvent(100, cb_d, CB_IDS[3]);
-    REQUIRE(100 == Core::Timing::GetDowncount());
-    Core::Timing::ScheduleEvent(1200, cb_e, CB_IDS[4]);
-    REQUIRE(100 == Core::Timing::GetDowncount());
+    core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
+    REQUIRE(1000 == core_timing.GetDowncount());
+    core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
+    REQUIRE(500 == core_timing.GetDowncount());
+    core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
+    REQUIRE(500 == core_timing.GetDowncount());
+    core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
+    REQUIRE(100 == core_timing.GetDowncount());
+    core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
+    REQUIRE(100 == core_timing.GetDowncount());
 
-    AdvanceAndCheck(3, 400);
-    AdvanceAndCheck(1, 300);
-    AdvanceAndCheck(2, 200);
-    AdvanceAndCheck(0, 200);
-    AdvanceAndCheck(4, MAX_SLICE_LENGTH);
+    AdvanceAndCheck(core_timing, 3, 400);
+    AdvanceAndCheck(core_timing, 1, 300);
+    AdvanceAndCheck(core_timing, 2, 200);
+    AdvanceAndCheck(core_timing, 0, 200);
+    AdvanceAndCheck(core_timing, 4, MAX_SLICE_LENGTH);
 }
 
 TEST_CASE("CoreTiming[Threadsave]", "[core]") {
     ScopeInit guard;
+    auto& core_timing = guard.core_timing;
 
-    Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
-    Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
-    Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
-    Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", CallbackTemplate<3>);
-    Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", CallbackTemplate<4>);
+    Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
+    Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
+    Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
+    Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", CallbackTemplate<3>);
+    Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", CallbackTemplate<4>);
 
     // Enter slice 0
-    Core::Timing::Advance();
+    core_timing.Advance();
 
     // D -> B -> C -> A -> E
-    Core::Timing::ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
+    core_timing.ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]);
     // Manually force since ScheduleEventThreadsafe doesn't call it
-    Core::Timing::ForceExceptionCheck(1000);
-    REQUIRE(1000 == Core::Timing::GetDowncount());
-    Core::Timing::ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
+    core_timing.ForceExceptionCheck(1000);
+    REQUIRE(1000 == core_timing.GetDowncount());
+    core_timing.ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]);
     // Manually force since ScheduleEventThreadsafe doesn't call it
-    Core::Timing::ForceExceptionCheck(500);
-    REQUIRE(500 == Core::Timing::GetDowncount());
-    Core::Timing::ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
+    core_timing.ForceExceptionCheck(500);
+    REQUIRE(500 == core_timing.GetDowncount());
+    core_timing.ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]);
     // Manually force since ScheduleEventThreadsafe doesn't call it
-    Core::Timing::ForceExceptionCheck(800);
-    REQUIRE(500 == Core::Timing::GetDowncount());
-    Core::Timing::ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
+    core_timing.ForceExceptionCheck(800);
+    REQUIRE(500 == core_timing.GetDowncount());
+    core_timing.ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]);
     // Manually force since ScheduleEventThreadsafe doesn't call it
-    Core::Timing::ForceExceptionCheck(100);
-    REQUIRE(100 == Core::Timing::GetDowncount());
-    Core::Timing::ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
+    core_timing.ForceExceptionCheck(100);
+    REQUIRE(100 == core_timing.GetDowncount());
+    core_timing.ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]);
     // Manually force since ScheduleEventThreadsafe doesn't call it
-    Core::Timing::ForceExceptionCheck(1200);
-    REQUIRE(100 == Core::Timing::GetDowncount());
+    core_timing.ForceExceptionCheck(1200);
+    REQUIRE(100 == core_timing.GetDowncount());
 
-    AdvanceAndCheck(3, 400);
-    AdvanceAndCheck(1, 300);
-    AdvanceAndCheck(2, 200);
-    AdvanceAndCheck(0, 200);
-    AdvanceAndCheck(4, MAX_SLICE_LENGTH);
+    AdvanceAndCheck(core_timing, 3, 400);
+    AdvanceAndCheck(core_timing, 1, 300);
+    AdvanceAndCheck(core_timing, 2, 200);
+    AdvanceAndCheck(core_timing, 0, 200);
+    AdvanceAndCheck(core_timing, 4, MAX_SLICE_LENGTH);
 }
 
 namespace SharedSlotTest {
@@ -142,59 +145,62 @@ TEST_CASE("CoreTiming[SharedSlot]", "[core]") {
     using namespace SharedSlotTest;
 
     ScopeInit guard;
+    auto& core_timing = guard.core_timing;
 
-    Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", FifoCallback<0>);
-    Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", FifoCallback<1>);
-    Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", FifoCallback<2>);
-    Core::Timing::EventType* cb_d = Core::Timing::RegisterEvent("callbackD", FifoCallback<3>);
-    Core::Timing::EventType* cb_e = Core::Timing::RegisterEvent("callbackE", FifoCallback<4>);
+    Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", FifoCallback<0>);
+    Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", FifoCallback<1>);
+    Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", FifoCallback<2>);
+    Core::Timing::EventType* cb_d = core_timing.RegisterEvent("callbackD", FifoCallback<3>);
+    Core::Timing::EventType* cb_e = core_timing.RegisterEvent("callbackE", FifoCallback<4>);
 
-    Core::Timing::ScheduleEvent(1000, cb_a, CB_IDS[0]);
-    Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
-    Core::Timing::ScheduleEvent(1000, cb_c, CB_IDS[2]);
-    Core::Timing::ScheduleEvent(1000, cb_d, CB_IDS[3]);
-    Core::Timing::ScheduleEvent(1000, cb_e, CB_IDS[4]);
+    core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
+    core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
+    core_timing.ScheduleEvent(1000, cb_c, CB_IDS[2]);
+    core_timing.ScheduleEvent(1000, cb_d, CB_IDS[3]);
+    core_timing.ScheduleEvent(1000, cb_e, CB_IDS[4]);
 
     // Enter slice 0
-    Core::Timing::Advance();
-    REQUIRE(1000 == Core::Timing::GetDowncount());
+    core_timing.Advance();
+    REQUIRE(1000 == core_timing.GetDowncount());
 
     callbacks_ran_flags = 0;
     counter = 0;
     lateness = 0;
-    Core::Timing::AddTicks(Core::Timing::GetDowncount());
-    Core::Timing::Advance();
-    REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
+    core_timing.AddTicks(core_timing.GetDowncount());
+    core_timing.Advance();
+    REQUIRE(MAX_SLICE_LENGTH == core_timing.GetDowncount());
     REQUIRE(0x1FULL == callbacks_ran_flags.to_ullong());
 }
 
 TEST_CASE("Core::Timing[PredictableLateness]", "[core]") {
     ScopeInit guard;
+    auto& core_timing = guard.core_timing;
 
-    Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
-    Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
+    Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
+    Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
 
     // Enter slice 0
-    Core::Timing::Advance();
+    core_timing.Advance();
 
-    Core::Timing::ScheduleEvent(100, cb_a, CB_IDS[0]);
-    Core::Timing::ScheduleEvent(200, cb_b, CB_IDS[1]);
+    core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]);
+    core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]);
 
-    AdvanceAndCheck(0, 90, 10, -10); // (100 - 10)
-    AdvanceAndCheck(1, MAX_SLICE_LENGTH, 50, -50);
+    AdvanceAndCheck(core_timing, 0, 90, 10, -10); // (100 - 10)
+    AdvanceAndCheck(core_timing, 1, MAX_SLICE_LENGTH, 50, -50);
 }
 
 namespace ChainSchedulingTest {
 static int reschedules = 0;
 
-static void RescheduleCallback(u64 userdata, s64 cycles_late) {
+static void RescheduleCallback(Core::Timing::CoreTiming& core_timing, u64 userdata,
+                               s64 cycles_late) {
     --reschedules;
     REQUIRE(reschedules >= 0);
     REQUIRE(lateness == cycles_late);
 
     if (reschedules > 0) {
-        Core::Timing::ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata),
-                                    userdata);
+        core_timing.ScheduleEvent(1000, reinterpret_cast<Core::Timing::EventType*>(userdata),
+                                  userdata);
     }
 }
 } // namespace ChainSchedulingTest
@@ -203,36 +209,39 @@ TEST_CASE("CoreTiming[ChainScheduling]", "[core]") {
     using namespace ChainSchedulingTest;
 
     ScopeInit guard;
+    auto& core_timing = guard.core_timing;
 
-    Core::Timing::EventType* cb_a = Core::Timing::RegisterEvent("callbackA", CallbackTemplate<0>);
-    Core::Timing::EventType* cb_b = Core::Timing::RegisterEvent("callbackB", CallbackTemplate<1>);
-    Core::Timing::EventType* cb_c = Core::Timing::RegisterEvent("callbackC", CallbackTemplate<2>);
-    Core::Timing::EventType* cb_rs =
-        Core::Timing::RegisterEvent("callbackReschedule", RescheduleCallback);
+    Core::Timing::EventType* cb_a = core_timing.RegisterEvent("callbackA", CallbackTemplate<0>);
+    Core::Timing::EventType* cb_b = core_timing.RegisterEvent("callbackB", CallbackTemplate<1>);
+    Core::Timing::EventType* cb_c = core_timing.RegisterEvent("callbackC", CallbackTemplate<2>);
+    Core::Timing::EventType* cb_rs = core_timing.RegisterEvent(
+        "callbackReschedule", [&core_timing](u64 userdata, s64 cycles_late) {
+            RescheduleCallback(core_timing, userdata, cycles_late);
+        });
 
     // Enter slice 0
-    Core::Timing::Advance();
+    core_timing.Advance();
 
-    Core::Timing::ScheduleEvent(800, cb_a, CB_IDS[0]);
-    Core::Timing::ScheduleEvent(1000, cb_b, CB_IDS[1]);
-    Core::Timing::ScheduleEvent(2200, cb_c, CB_IDS[2]);
-    Core::Timing::ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
-    REQUIRE(800 == Core::Timing::GetDowncount());
+    core_timing.ScheduleEvent(800, cb_a, CB_IDS[0]);
+    core_timing.ScheduleEvent(1000, cb_b, CB_IDS[1]);
+    core_timing.ScheduleEvent(2200, cb_c, CB_IDS[2]);
+    core_timing.ScheduleEvent(1000, cb_rs, reinterpret_cast<u64>(cb_rs));
+    REQUIRE(800 == core_timing.GetDowncount());
 
     reschedules = 3;
-    AdvanceAndCheck(0, 200);  // cb_a
-    AdvanceAndCheck(1, 1000); // cb_b, cb_rs
+    AdvanceAndCheck(core_timing, 0, 200);  // cb_a
+    AdvanceAndCheck(core_timing, 1, 1000); // cb_b, cb_rs
     REQUIRE(2 == reschedules);
 
-    Core::Timing::AddTicks(Core::Timing::GetDowncount());
-    Core::Timing::Advance(); // cb_rs
+    core_timing.AddTicks(core_timing.GetDowncount());
+    core_timing.Advance(); // cb_rs
     REQUIRE(1 == reschedules);
-    REQUIRE(200 == Core::Timing::GetDowncount());
+    REQUIRE(200 == core_timing.GetDowncount());
 
-    AdvanceAndCheck(2, 800); // cb_c
+    AdvanceAndCheck(core_timing, 2, 800); // cb_c
 
-    Core::Timing::AddTicks(Core::Timing::GetDowncount());
-    Core::Timing::Advance(); // cb_rs
+    core_timing.AddTicks(core_timing.GetDowncount());
+    core_timing.Advance(); // cb_rs
     REQUIRE(0 == reschedules);
-    REQUIRE(MAX_SLICE_LENGTH == Core::Timing::GetDowncount());
+    REQUIRE(MAX_SLICE_LENGTH == core_timing.GetDowncount());
 }
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 19b6b14b2..86ede5faa 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -317,7 +317,7 @@ void Maxwell3D::ProcessQueryGet() {
             LongQueryResult query_result{};
             query_result.value = result;
             // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
-            query_result.timestamp = Core::Timing::GetTicks();
+            query_result.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
             Memory::WriteBlock(*address, &query_result, sizeof(query_result));
         }
         dirty_flags.OnMemoryWrite();
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 3d00c308b..b86265dfe 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 
 #include "common/assert.h"
+#include "core/core.h"
 #include "core/core_timing.h"
 #include "core/memory.h"
 #include "video_core/engines/fermi_2d.h"
@@ -283,7 +284,7 @@ void GPU::ProcessSemaphoreTriggerMethod() {
         block.sequence = regs.semaphore_sequence;
         // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
         // CoreTiming
-        block.timestamp = Core::Timing::GetTicks();
+        block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
         Memory::WriteBlock(*address, &block, sizeof(block));
     } else {
         const auto address =
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index cca2ed708..d40666ac6 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -137,7 +137,7 @@ void RendererOpenGL::SwapBuffers(
 
     render_window.PollEvents();
 
-    system.FrameLimiter().DoFrameLimiting(Core::Timing::GetGlobalTimeUs());
+    system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs());
     system.GetPerfStats().BeginSystemFrame();
 
     // Restore the rasterizer state