summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/psc/time/clocks
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/service/psc/time/clocks')
-rw-r--r--src/core/hle/service/psc/time/clocks/context_writers.cpp83
-rw-r--r--src/core/hle/service/psc/time/clocks/context_writers.h79
-rw-r--r--src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h21
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp20
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h23
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp42
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h30
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp101
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h54
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp63
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h55
-rw-r--r--src/core/hle/service/psc/time/clocks/steady_clock_core.h81
-rw-r--r--src/core/hle/service/psc/time/clocks/system_clock_core.cpp75
-rw-r--r--src/core/hle/service/psc/time/clocks/system_clock_core.h55
-rw-r--r--src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp43
-rw-r--r--src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h41
16 files changed, 866 insertions, 0 deletions
diff --git a/src/core/hle/service/psc/time/clocks/context_writers.cpp b/src/core/hle/service/psc/time/clocks/context_writers.cpp
new file mode 100644
index 000000000..ac8700f76
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/context_writers.cpp
@@ -0,0 +1,83 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/service/psc/time/clocks/context_writers.h"
+
+namespace Service::PSC::Time {
+
+void ContextWriter::SignalAllNodes() {
+ std::scoped_lock l{m_mutex};
+ for (auto& operation : m_operation_events) {
+ operation.m_event->Signal();
+ }
+}
+
+void ContextWriter::Link(OperationEvent& operation_event) {
+ std::scoped_lock l{m_mutex};
+ m_operation_events.push_back(operation_event);
+}
+
+LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& system,
+ SharedMemory& shared_memory)
+ : m_system{system}, m_shared_memory{shared_memory} {}
+
+Result LocalSystemClockContextWriter::Write(SystemClockContext& context) {
+ if (m_in_use) {
+ R_SUCCEED_IF(context == m_context);
+ m_context = context;
+ } else {
+ m_context = context;
+ m_in_use = true;
+ }
+
+ m_shared_memory.SetLocalSystemContext(context);
+
+ SignalAllNodes();
+
+ R_SUCCEED();
+}
+
+NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& system,
+ SharedMemory& shared_memory,
+ SystemClockCore& system_clock)
+ : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {}
+
+Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) {
+ s64 time{};
+ [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time);
+
+ if (m_in_use) {
+ R_SUCCEED_IF(context == m_context);
+ m_context = context;
+ } else {
+ m_context = context;
+ m_in_use = true;
+ }
+
+ m_shared_memory.SetNetworkSystemContext(context);
+
+ SignalAllNodes();
+
+ R_SUCCEED();
+}
+
+EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWriter(
+ Core::System& system)
+ : m_system{system} {}
+
+Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) {
+ if (m_in_use) {
+ R_SUCCEED_IF(context == m_context);
+ m_context = context;
+ } else {
+ m_context = context;
+ m_in_use = true;
+ }
+
+ SignalAllNodes();
+
+ R_SUCCEED();
+}
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/context_writers.h b/src/core/hle/service/psc/time/clocks/context_writers.h
new file mode 100644
index 000000000..afd3725d4
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/context_writers.h
@@ -0,0 +1,79 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <list>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/service/psc/time/clocks/system_clock_core.h"
+#include "core/hle/service/psc/time/common.h"
+#include "core/hle/service/psc/time/shared_memory.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::PSC::Time {
+
+class ContextWriter {
+private:
+ using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType;
+
+public:
+ virtual ~ContextWriter() = default;
+
+ virtual Result Write(SystemClockContext& context) = 0;
+ void SignalAllNodes();
+ void Link(OperationEvent& operation_event);
+
+private:
+ OperationEventList m_operation_events;
+ std::mutex m_mutex;
+};
+
+class LocalSystemClockContextWriter : public ContextWriter {
+public:
+ explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory);
+
+ Result Write(SystemClockContext& context) override;
+
+private:
+ Core::System& m_system;
+
+ SharedMemory& m_shared_memory;
+ bool m_in_use{};
+ SystemClockContext m_context{};
+};
+
+class NetworkSystemClockContextWriter : public ContextWriter {
+public:
+ explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory,
+ SystemClockCore& system_clock);
+
+ Result Write(SystemClockContext& context) override;
+
+private:
+ Core::System& m_system;
+
+ SharedMemory& m_shared_memory;
+ bool m_in_use{};
+ SystemClockContext m_context{};
+ SystemClockCore& m_system_clock;
+};
+
+class EphemeralNetworkSystemClockContextWriter : public ContextWriter {
+public:
+ EphemeralNetworkSystemClockContextWriter(Core::System& system);
+
+ Result Write(SystemClockContext& context) override;
+
+private:
+ Core::System& m_system;
+
+ bool m_in_use{};
+ SystemClockContext m_context{};
+};
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
new file mode 100644
index 000000000..0a68867d9
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+#include "core/hle/service/psc/time/clocks/context_writers.h"
+#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
+#include "core/hle/service/psc/time/clocks/system_clock_core.h"
+#include "core/hle/service/psc/time/common.h"
+
+namespace Service::PSC::Time {
+
+class EphemeralNetworkSystemClockCore : public SystemClockCore {
+public:
+ explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock)
+ : SystemClockCore{steady_clock} {}
+ ~EphemeralNetworkSystemClockCore() override = default;
+};
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
new file mode 100644
index 000000000..36dca6689
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
+
+namespace Service::PSC::Time {
+
+void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) {
+ SteadyClockTimePoint time_point{};
+ if (GetCurrentTimePoint(time_point) == ResultSuccess &&
+ context.steady_time_point.IdMatches(time_point)) {
+ SetContextAndWrite(context);
+ } else if (SetCurrentTime(time) != ResultSuccess) {
+ LOG_ERROR(Service_Time, "Failed to SetCurrentTime");
+ }
+
+ SetInitialized();
+}
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h
new file mode 100644
index 000000000..176ba3e94
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h
@@ -0,0 +1,23 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+#include "core/hle/service/psc/time/clocks/context_writers.h"
+#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
+#include "core/hle/service/psc/time/clocks/system_clock_core.h"
+#include "core/hle/service/psc/time/common.h"
+
+namespace Service::PSC::Time {
+
+class StandardLocalSystemClockCore : public SystemClockCore {
+public:
+ explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock)
+ : SystemClockCore{steady_clock} {}
+ ~StandardLocalSystemClockCore() override = default;
+
+ void Initialize(SystemClockContext& context, s64 time);
+};
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
new file mode 100644
index 000000000..8d6cb7db1
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
@@ -0,0 +1,42 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
+
+namespace Service::PSC::Time {
+
+void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) {
+ if (SetContextAndWrite(context) != ResultSuccess) {
+ LOG_ERROR(Service_Time, "Failed to SetContext");
+ }
+ m_sufficient_accuracy = accuracy;
+ SetInitialized();
+}
+
+bool StandardNetworkSystemClockCore::IsAccuracySufficient() {
+ if (!IsInitialized()) {
+ return false;
+ }
+
+ SystemClockContext context{};
+ SteadyClockTimePoint current_time_point{};
+ if (GetCurrentTimePoint(current_time_point) != ResultSuccess ||
+ GetContext(context) != ResultSuccess) {
+ return false;
+ }
+
+ s64 seconds{};
+ if (GetSpanBetweenTimePoints(&seconds, context.steady_time_point, current_time_point) !=
+ ResultSuccess) {
+ return false;
+ }
+
+ if (std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(seconds))
+ .count() < m_sufficient_accuracy) {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h
new file mode 100644
index 000000000..933d2c8e3
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <chrono>
+
+#include "core/hle/result.h"
+#include "core/hle/service/psc/time/clocks/context_writers.h"
+#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
+#include "core/hle/service/psc/time/clocks/system_clock_core.h"
+#include "core/hle/service/psc/time/common.h"
+
+namespace Service::PSC::Time {
+
+class StandardNetworkSystemClockCore : public SystemClockCore {
+public:
+ explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock)
+ : SystemClockCore{steady_clock} {}
+ ~StandardNetworkSystemClockCore() override = default;
+
+ void Initialize(SystemClockContext& context, s64 accuracy);
+ bool IsAccuracySufficient();
+
+private:
+ s64 m_sufficient_accuracy{
+ std::chrono ::duration_cast<std::chrono::nanoseconds>(std::chrono::days(10)).count()};
+};
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp
new file mode 100644
index 000000000..7a72d7aa2
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp
@@ -0,0 +1,101 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <chrono>
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
+
+namespace Service::PSC::Time {
+
+void StandardSteadyClockCore::Initialize(ClockSourceId clock_source_id, s64 rtc_offset,
+ s64 internal_offset, s64 test_offset,
+ bool is_rtc_reset_detected) {
+ m_clock_source_id = clock_source_id;
+ m_rtc_offset = rtc_offset;
+ m_internal_offset = internal_offset;
+ m_test_offset = test_offset;
+ if (is_rtc_reset_detected) {
+ SetResetDetected();
+ }
+ SetInitialized();
+}
+
+void StandardSteadyClockCore::SetRtcOffset(s64 offset) {
+ m_rtc_offset = offset;
+}
+
+void StandardSteadyClockCore::SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time) {
+ auto ticks{m_system.CoreTiming().GetClockTicks()};
+
+ m_continuous_adjustment_time_point.rtc_offset = ConvertToTimeSpan(ticks).count();
+ m_continuous_adjustment_time_point.diff_scale = 0;
+ m_continuous_adjustment_time_point.shift_amount = 0;
+ m_continuous_adjustment_time_point.lower = time;
+ m_continuous_adjustment_time_point.upper = time;
+ m_continuous_adjustment_time_point.clock_source_id = clock_source_id;
+}
+
+void StandardSteadyClockCore::GetContinuousAdjustment(
+ ContinuousAdjustmentTimePoint& out_time_point) const {
+ out_time_point = m_continuous_adjustment_time_point;
+}
+
+void StandardSteadyClockCore::UpdateContinuousAdjustmentTime(s64 in_time) {
+ auto ticks{m_system.CoreTiming().GetClockTicks()};
+ auto uptime_ns{ConvertToTimeSpan(ticks).count()};
+ auto adjusted_time{((uptime_ns - m_continuous_adjustment_time_point.rtc_offset) *
+ m_continuous_adjustment_time_point.diff_scale) >>
+ m_continuous_adjustment_time_point.shift_amount};
+ auto expected_time{adjusted_time + m_continuous_adjustment_time_point.lower};
+
+ auto last_time_point{m_continuous_adjustment_time_point.upper};
+ m_continuous_adjustment_time_point.upper = in_time;
+ auto t1{std::min<s64>(expected_time, last_time_point)};
+ expected_time = std::max<s64>(expected_time, last_time_point);
+ expected_time = m_continuous_adjustment_time_point.diff_scale >= 0 ? t1 : expected_time;
+
+ auto new_diff{in_time < expected_time ? -55 : 55};
+
+ m_continuous_adjustment_time_point.rtc_offset = uptime_ns;
+ m_continuous_adjustment_time_point.shift_amount = expected_time == in_time ? 0 : 14;
+ m_continuous_adjustment_time_point.diff_scale = expected_time == in_time ? 0 : new_diff;
+ m_continuous_adjustment_time_point.lower = expected_time;
+}
+
+Result StandardSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
+ auto current_time_ns = GetCurrentRawTimePointImpl();
+ auto current_time_s =
+ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(current_time_ns));
+ out_time_point.time_point = current_time_s.count();
+ out_time_point.clock_source_id = m_clock_source_id;
+ R_SUCCEED();
+}
+
+s64 StandardSteadyClockCore::GetCurrentRawTimePointImpl() {
+ std::scoped_lock l{m_mutex};
+ auto ticks{static_cast<s64>(m_system.CoreTiming().GetClockTicks())};
+ auto current_time_ns = m_rtc_offset + ConvertToTimeSpan(ticks).count();
+ auto time_point = std::max<s64>(current_time_ns, m_cached_time_point);
+ m_cached_time_point = time_point;
+ return time_point;
+}
+
+s64 StandardSteadyClockCore::GetTestOffsetImpl() const {
+ return m_test_offset;
+}
+
+void StandardSteadyClockCore::SetTestOffsetImpl(s64 offset) {
+ m_test_offset = offset;
+}
+
+s64 StandardSteadyClockCore::GetInternalOffsetImpl() const {
+ return m_internal_offset;
+}
+
+void StandardSteadyClockCore::SetInternalOffsetImpl(s64 offset) {
+ m_internal_offset = offset;
+}
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h
new file mode 100644
index 000000000..bbf98fcd5
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h
@@ -0,0 +1,54 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <mutex>
+
+#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::PSC::Time {
+class StandardSteadyClockCore : public SteadyClockCore {
+public:
+ explicit StandardSteadyClockCore(Core::System& system) : m_system{system} {}
+ ~StandardSteadyClockCore() override = default;
+
+ void Initialize(ClockSourceId clock_source_id, s64 rtc_offset, s64 internal_offset,
+ s64 test_offset, bool is_rtc_reset_detected);
+
+ void SetRtcOffset(s64 offset);
+ void SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time);
+ void GetContinuousAdjustment(ContinuousAdjustmentTimePoint& out_time_point) const;
+ void UpdateContinuousAdjustmentTime(s64 time);
+
+ Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
+ s64 GetCurrentRawTimePointImpl() override;
+ s64 GetTestOffsetImpl() const override;
+ void SetTestOffsetImpl(s64 offset) override;
+ s64 GetInternalOffsetImpl() const override;
+ void SetInternalOffsetImpl(s64 offset) override;
+
+ Result GetRtcValueImpl(s64& out_value) override {
+ R_RETURN(ResultNotImplemented);
+ }
+
+ Result GetSetupResultValueImpl() override {
+ R_SUCCEED();
+ }
+
+private:
+ Core::System& m_system;
+
+ std::mutex m_mutex;
+ s64 m_test_offset{};
+ s64 m_internal_offset{};
+ ClockSourceId m_clock_source_id{};
+ s64 m_rtc_offset{};
+ s64 m_cached_time_point{};
+ ContinuousAdjustmentTimePoint m_continuous_adjustment_time_point{};
+};
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
new file mode 100644
index 000000000..9e9be05d6
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
@@ -0,0 +1,63 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core.h"
+#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
+
+namespace Service::PSC::Time {
+
+StandardUserSystemClockCore::StandardUserSystemClockCore(
+ Core::System& system, StandardLocalSystemClockCore& local_clock,
+ StandardNetworkSystemClockCore& network_clock)
+ : SystemClockCore{local_clock.GetSteadyClock()}, m_system{system},
+ m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock},
+ m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent(
+ "Psc:StandardUserSystemClockCore:Event")} {}
+
+StandardUserSystemClockCore::~StandardUserSystemClockCore() {
+ m_ctx.CloseEvent(m_event);
+}
+
+Result StandardUserSystemClockCore::SetAutomaticCorrection(bool automatic_correction) {
+ R_SUCCEED_IF(m_automatic_correction == automatic_correction);
+ R_SUCCEED_IF(!m_network_system_clock.CheckClockSourceMatches());
+
+ SystemClockContext context{};
+ R_TRY(m_network_system_clock.GetContext(context));
+ R_TRY(m_local_system_clock.SetContextAndWrite(context));
+
+ m_automatic_correction = automatic_correction;
+ R_SUCCEED();
+}
+
+Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) const {
+ if (!m_automatic_correction) {
+ R_RETURN(m_local_system_clock.GetContext(out_context));
+ }
+
+ if (!m_network_system_clock.CheckClockSourceMatches()) {
+ R_RETURN(m_local_system_clock.GetContext(out_context));
+ }
+
+ SystemClockContext context{};
+ R_TRY(m_network_system_clock.GetContext(context));
+ R_TRY(m_local_system_clock.SetContextAndWrite(context));
+
+ R_RETURN(m_local_system_clock.GetContext(out_context));
+}
+
+Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) {
+ R_RETURN(ResultNotImplemented);
+}
+
+Result StandardUserSystemClockCore::GetTimePoint(SteadyClockTimePoint& out_time_point) {
+ out_time_point = m_time_point;
+ R_SUCCEED();
+}
+
+void StandardUserSystemClockCore::SetTimePointAndSignal(SteadyClockTimePoint& time_point) {
+ m_time_point = time_point;
+ m_event->Signal();
+}
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h
new file mode 100644
index 000000000..a7fe7648d
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h
@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/result.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/psc/time/clocks/context_writers.h"
+#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
+#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
+#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
+#include "core/hle/service/psc/time/clocks/system_clock_core.h"
+#include "core/hle/service/psc/time/common.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::PSC::Time {
+
+class StandardUserSystemClockCore : public SystemClockCore {
+public:
+ explicit StandardUserSystemClockCore(Core::System& system,
+ StandardLocalSystemClockCore& local_clock,
+ StandardNetworkSystemClockCore& network_clock);
+ ~StandardUserSystemClockCore() override;
+
+ Kernel::KEvent& GetEvent() {
+ return *m_event;
+ }
+
+ bool GetAutomaticCorrection() const {
+ return m_automatic_correction;
+ }
+ Result SetAutomaticCorrection(bool automatic_correction);
+
+ Result GetContext(SystemClockContext& out_context) const override;
+ Result SetContext(SystemClockContext& context) override;
+
+ Result GetTimePoint(SteadyClockTimePoint& out_time_point);
+ void SetTimePointAndSignal(SteadyClockTimePoint& time_point);
+
+private:
+ Core::System& m_system;
+ KernelHelpers::ServiceContext m_ctx;
+
+ bool m_automatic_correction{};
+ StandardLocalSystemClockCore& m_local_system_clock;
+ StandardNetworkSystemClockCore& m_network_system_clock;
+ SteadyClockTimePoint m_time_point{};
+ Kernel::KEvent* m_event{};
+};
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/steady_clock_core.h b/src/core/hle/service/psc/time/clocks/steady_clock_core.h
new file mode 100644
index 000000000..f933cc15f
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/steady_clock_core.h
@@ -0,0 +1,81 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <chrono>
+
+#include "core/hle/result.h"
+#include "core/hle/service/psc/time/common.h"
+
+namespace Service::PSC::Time {
+
+class SteadyClockCore {
+public:
+ SteadyClockCore() = default;
+ virtual ~SteadyClockCore() = default;
+
+ void SetInitialized() {
+ m_initialized = true;
+ }
+
+ bool IsInitialized() const {
+ return m_initialized;
+ }
+
+ void SetResetDetected() {
+ m_reset_detected = true;
+ }
+
+ bool IsResetDetected() const {
+ return m_reset_detected;
+ }
+
+ Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
+ R_TRY(GetCurrentTimePointImpl(out_time_point));
+
+ auto one_second_ns{
+ std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
+ out_time_point.time_point += GetTestOffsetImpl() / one_second_ns;
+ out_time_point.time_point += GetInternalOffsetImpl() / one_second_ns;
+ R_SUCCEED();
+ }
+
+ s64 GetTestOffset() const {
+ return GetTestOffsetImpl();
+ }
+
+ void SetTestOffset(s64 offset) {
+ SetTestOffsetImpl(offset);
+ }
+
+ s64 GetInternalOffset() const {
+ return GetInternalOffsetImpl();
+ }
+
+ s64 GetRawTime() {
+ return GetCurrentRawTimePointImpl() + GetTestOffsetImpl() + GetInternalOffsetImpl();
+ }
+
+ Result GetRtcValue(s64& out_value) {
+ R_RETURN(GetRtcValueImpl(out_value));
+ }
+
+ Result GetSetupResultValue() {
+ R_RETURN(GetSetupResultValueImpl());
+ }
+
+private:
+ virtual Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) = 0;
+ virtual s64 GetCurrentRawTimePointImpl() = 0;
+ virtual s64 GetTestOffsetImpl() const = 0;
+ virtual void SetTestOffsetImpl(s64 offset) = 0;
+ virtual s64 GetInternalOffsetImpl() const = 0;
+ virtual void SetInternalOffsetImpl(s64 offset) = 0;
+ virtual Result GetRtcValueImpl(s64& out_value) = 0;
+ virtual Result GetSetupResultValueImpl() = 0;
+
+ bool m_initialized{};
+ bool m_reset_detected{};
+};
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp
new file mode 100644
index 000000000..c507ef517
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp
@@ -0,0 +1,75 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/psc/time/clocks/context_writers.h"
+#include "core/hle/service/psc/time/clocks/system_clock_core.h"
+
+namespace Service::PSC::Time {
+
+bool SystemClockCore::CheckClockSourceMatches() {
+ SystemClockContext context{};
+ if (GetContext(context) != ResultSuccess) {
+ return false;
+ }
+
+ SteadyClockTimePoint time_point{};
+ if (m_steady_clock.GetCurrentTimePoint(time_point) != ResultSuccess) {
+ return false;
+ }
+
+ return context.steady_time_point.IdMatches(time_point);
+}
+
+Result SystemClockCore::GetCurrentTime(s64* out_time) const {
+ R_UNLESS(out_time != nullptr, ResultInvalidArgument);
+
+ SystemClockContext context{};
+ SteadyClockTimePoint time_point{};
+
+ R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));
+ R_TRY(GetContext(context));
+
+ R_UNLESS(context.steady_time_point.IdMatches(time_point), ResultClockMismatch);
+
+ *out_time = context.offset + time_point.time_point;
+ R_SUCCEED();
+}
+
+Result SystemClockCore::SetCurrentTime(s64 time) {
+ SteadyClockTimePoint time_point{};
+ R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));
+
+ SystemClockContext context{
+ .offset = time - time_point.time_point,
+ .steady_time_point = time_point,
+ };
+ R_RETURN(SetContextAndWrite(context));
+}
+
+Result SystemClockCore::GetContext(SystemClockContext& out_context) const {
+ out_context = m_context;
+ R_SUCCEED();
+}
+
+Result SystemClockCore::SetContext(SystemClockContext& context) {
+ m_context = context;
+ R_SUCCEED();
+}
+
+Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) {
+ R_TRY(SetContext(context));
+
+ if (m_context_writer) {
+ R_RETURN(m_context_writer->Write(context));
+ }
+
+ R_SUCCEED();
+}
+
+void SystemClockCore::LinkOperationEvent(OperationEvent& operation_event) {
+ if (m_context_writer) {
+ m_context_writer->Link(operation_event);
+ }
+}
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.h b/src/core/hle/service/psc/time/clocks/system_clock_core.h
new file mode 100644
index 000000000..73811712e
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/system_clock_core.h
@@ -0,0 +1,55 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
+#include "core/hle/service/psc/time/common.h"
+
+namespace Service::PSC::Time {
+class ContextWriter;
+
+class SystemClockCore {
+public:
+ explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {}
+ virtual ~SystemClockCore() = default;
+
+ SteadyClockCore& GetSteadyClock() {
+ return m_steady_clock;
+ }
+
+ bool IsInitialized() const {
+ return m_initialized;
+ }
+
+ void SetInitialized() {
+ m_initialized = true;
+ }
+
+ void SetContextWriter(ContextWriter& context_writer) {
+ m_context_writer = &context_writer;
+ }
+
+ bool CheckClockSourceMatches();
+
+ Result GetCurrentTime(s64* out_time) const;
+ Result SetCurrentTime(s64 time);
+
+ Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
+ R_RETURN(m_steady_clock.GetCurrentTimePoint(out_time_point));
+ }
+
+ virtual Result GetContext(SystemClockContext& out_context) const;
+ virtual Result SetContext(SystemClockContext& context);
+ Result SetContextAndWrite(SystemClockContext& context);
+
+ void LinkOperationEvent(OperationEvent& operation_event);
+
+private:
+ bool m_initialized{};
+ ContextWriter* m_context_writer{};
+ SteadyClockCore& m_steady_clock;
+ SystemClockContext m_context{};
+};
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
new file mode 100644
index 000000000..22da1fbcc
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <chrono>
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h"
+
+namespace Service::PSC::Time {
+
+Result TickBasedSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
+ auto ticks{m_system.CoreTiming().GetClockTicks()};
+ auto current_time_s =
+ std::chrono::duration_cast<std::chrono::seconds>(ConvertToTimeSpan(ticks)).count();
+ out_time_point.time_point = current_time_s;
+ out_time_point.clock_source_id = m_clock_source_id;
+ R_SUCCEED();
+}
+
+s64 TickBasedSteadyClockCore::GetCurrentRawTimePointImpl() {
+ SteadyClockTimePoint time_point{};
+ if (GetCurrentTimePointImpl(time_point) != ResultSuccess) {
+ LOG_ERROR(Service_Time, "Failed to GetCurrentTimePoint!");
+ }
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(
+ std::chrono::seconds(time_point.time_point))
+ .count();
+}
+
+s64 TickBasedSteadyClockCore::GetTestOffsetImpl() const {
+ return 0;
+}
+
+void TickBasedSteadyClockCore::SetTestOffsetImpl(s64 offset) {}
+
+s64 TickBasedSteadyClockCore::GetInternalOffsetImpl() const {
+ return 0;
+}
+
+void TickBasedSteadyClockCore::SetInternalOffsetImpl(s64 offset) {}
+
+} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h
new file mode 100644
index 000000000..a7bea86a2
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <mutex>
+
+#include "common/uuid.h"
+#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::PSC::Time {
+class TickBasedSteadyClockCore : public SteadyClockCore {
+public:
+ explicit TickBasedSteadyClockCore(Core::System& system) : m_system{system} {}
+ ~TickBasedSteadyClockCore() override = default;
+
+ Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
+ s64 GetCurrentRawTimePointImpl() override;
+ s64 GetTestOffsetImpl() const override;
+ void SetTestOffsetImpl(s64 offset) override;
+ s64 GetInternalOffsetImpl() const override;
+ void SetInternalOffsetImpl(s64 offset) override;
+
+ Result GetRtcValueImpl(s64& out_value) override {
+ R_RETURN(ResultNotImplemented);
+ }
+
+ Result GetSetupResultValueImpl() override {
+ R_SUCCEED();
+ }
+
+private:
+ Core::System& m_system;
+
+ ClockSourceId m_clock_source_id{Common::UUID::MakeRandom()};
+};
+} // namespace Service::PSC::Time