824 lines
20 KiB
C++
824 lines
20 KiB
C++
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <cstdlib>
|
|
#include <optional>
|
|
|
|
#include "yuzu_audio_core/audio_core.h"
|
|
#include "yuzu_common/hardware_properties.h"
|
|
#include "core/core.h"
|
|
#include "os_settings.h"
|
|
#include "core/core_timing.h"
|
|
#include "core/cpu_manager.h"
|
|
#include "core/debugger/debugger.h"
|
|
#include "core/gpu_dirty_memory_manager.h"
|
|
#include "core/hle/kernel/k_memory_manager.h"
|
|
#include "core/hle/kernel/k_process.h"
|
|
#include "core/hle/kernel/k_resource_limit.h"
|
|
#include "core/hle/kernel/k_scheduler.h"
|
|
#include "core/hle/kernel/kernel.h"
|
|
#include "core/hle/kernel/physical_core.h"
|
|
#include "core/hle/service/am/applet_manager.h"
|
|
#include "core/hle/service/am/frontend/applets.h"
|
|
#include "core/hle/service/apm/apm_controller.h"
|
|
#include "core/hle/service/filesystem/filesystem.h"
|
|
#include "core/hle/service/glue/glue_manager.h"
|
|
#include "core/hle/service/kernel_helpers.h"
|
|
#include "core/hle/service/os/event.h"
|
|
#include "core/hle/service/services.h"
|
|
#include "core/internal_network/network.h"
|
|
#include "core/perf_stats.h"
|
|
#include "core/reporter.h"
|
|
#include "yuzu_hid_core/hid_core.h"
|
|
#include "yuzu_input_common/main.h"
|
|
#include "network/network.h"
|
|
#include <nxemu-module-spec/system_loader.h>
|
|
#include <nxemu-cpu/cpu_settings_identifiers.h>
|
|
#include <nxemu-video/video_settings_identifiers.h>
|
|
|
|
#ifdef _WIN32
|
|
#include "yuzu_common/windows/timer_resolution.h"
|
|
#endif
|
|
|
|
extern IModuleSettings * g_settings;
|
|
|
|
namespace Core {
|
|
|
|
struct System::Impl {
|
|
explicit Impl(System & system_, ISystemModules & modules_) :
|
|
kernel{system_},
|
|
hid_core{},
|
|
cpu_manager{system_},
|
|
room_network{},
|
|
applet_manager{system_},
|
|
frontend_applets{system_},
|
|
modules(modules_),
|
|
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
|
|
reporter{system_}
|
|
{
|
|
device_memory = std::make_unique<Core::DeviceMemory>();
|
|
}
|
|
|
|
~Impl()
|
|
{
|
|
hid_core.UnloadInputDevices();
|
|
input_subsystem->Shutdown();
|
|
}
|
|
|
|
void Initialize(System& system)
|
|
{
|
|
ConfigureRuntime(system);
|
|
|
|
// These objects outlive individual games and runtime setting changes.
|
|
frontend_applets.SetDefaultAppletsIfMissing();
|
|
input_subsystem->Initialize();
|
|
}
|
|
|
|
void ConfigureRuntime(System& system)
|
|
{
|
|
is_multicore = osSettings.use_multi_core;
|
|
cpu_hle_requested = osSettings.cpu_hle_synchronization;
|
|
cpu_hle_initialized_supported = CpuHleSupported();
|
|
cpu_hle_initialized_native_clock = CpuHleUsesNativeClock();
|
|
|
|
#ifdef _WIN32
|
|
const std::chrono::nanoseconds timer_resolution = Common::Windows::SetCurrentTimerResolutionToMaximum();
|
|
core_timing.SetTimerResolutionNs(timer_resolution);
|
|
LOG_INFO(Core, "Host Timer Resolution: {:.4f} ms", std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(timer_resolution).count());
|
|
#endif
|
|
|
|
core_timing.SetMulticore(is_multicore);
|
|
core_timing.Initialize([&system]() { system.RegisterHostThread(); },
|
|
cpu_hle_initialized_supported, cpu_hle_initialized_native_clock);
|
|
|
|
is_async_gpu = g_settings->GetBool(NXVideoSetting::UseAsynchronousGPUEmulation);
|
|
|
|
kernel.SetMulticore(is_multicore);
|
|
cpu_manager.SetMulticore(is_multicore);
|
|
cpu_manager.SetAsyncGpu(is_async_gpu);
|
|
}
|
|
|
|
void ReconfigureIfNecessary(System & system)
|
|
{
|
|
const bool must_reconfigure = is_multicore != osSettings.use_multi_core ||
|
|
cpu_hle_requested != osSettings.cpu_hle_synchronization ||
|
|
(cpu_hle_requested && cpu_hle_initialized_supported != CpuHleSupported()) ||
|
|
(cpu_hle_requested && cpu_hle_initialized_native_clock != CpuHleUsesNativeClock()) ||
|
|
core_timing.CpuHleSynchronizationFailed();
|
|
|
|
if (!must_reconfigure)
|
|
{
|
|
return;
|
|
}
|
|
|
|
LOG_DEBUG(Kernel, "Reconfiguring runtime timing and CPU settings");
|
|
|
|
ConfigureRuntime(system);
|
|
}
|
|
|
|
bool CpuHleSupported() const
|
|
{
|
|
return cpu_hle_guest64;
|
|
}
|
|
|
|
bool CpuHleUsesNativeClock() const
|
|
{
|
|
#if (defined(_M_ARM64) || defined(ARCHITECTURE_arm64) || defined(__aarch64__)) && (defined(__linux__) || defined(__ANDROID__))
|
|
return cpu_hle_guest64 && g_settings->GetBool(NXCpuSetting::NceEnabled);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void Run()
|
|
{
|
|
std::unique_lock<std::mutex> lk(suspend_guard);
|
|
|
|
core_timing.SyncPause(false);
|
|
kernel.SuspendEmulation(false);
|
|
is_paused.store(false, std::memory_order_relaxed);
|
|
}
|
|
|
|
void Pause()
|
|
{
|
|
std::unique_lock<std::mutex> lk(suspend_guard);
|
|
|
|
kernel.SuspendEmulation(true);
|
|
core_timing.SyncPause(true);
|
|
is_paused.store(true, std::memory_order_relaxed);
|
|
}
|
|
|
|
bool IsPaused() const
|
|
{
|
|
return is_paused.load(std::memory_order_relaxed);
|
|
}
|
|
|
|
std::unique_lock<std::mutex> StallApplication()
|
|
{
|
|
std::unique_lock<std::mutex> lk(suspend_guard);
|
|
kernel.SuspendEmulation(true);
|
|
core_timing.SyncPause(true);
|
|
return lk;
|
|
}
|
|
|
|
void UnstallApplication()
|
|
{
|
|
if (!IsPaused())
|
|
{
|
|
core_timing.SyncPause(false);
|
|
kernel.SuspendEmulation(false);
|
|
}
|
|
}
|
|
|
|
void SetNVDECActive(bool is_nvdec_active)
|
|
{
|
|
nvdec_active.store(is_nvdec_active, std::memory_order_relaxed);
|
|
}
|
|
|
|
bool GetNVDECActive()
|
|
{
|
|
return nvdec_active.load(std::memory_order_relaxed);
|
|
}
|
|
|
|
void InitializeKernel(System & system, uint64_t titleID, bool is_64_bit)
|
|
{
|
|
LOG_DEBUG(Core, "initialized OK");
|
|
|
|
cpu_hle_guest64 = is_64_bit;
|
|
// Reconfigure timing before creating guest threads. Input factories and
|
|
// frontend applets remain the instances registered during OS startup.
|
|
ReconfigureIfNecessary(system);
|
|
|
|
kernel.Initialize();
|
|
cpu_manager.Initialize();
|
|
arp_manager.ResetAll();
|
|
audio_core = std::make_unique<AudioCore::AudioCore>(system);
|
|
|
|
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
|
|
services =
|
|
std::make_unique<Service::Services>(service_manager, system, stop_event.get_token());
|
|
|
|
is_powered_on = true;
|
|
exit_locked = false;
|
|
exit_requested = false;
|
|
|
|
perf_stats = std::make_unique<PerfStats>(titleID);
|
|
|
|
// Reset counters and set time origin to current frame
|
|
GetAndResetPerfStats();
|
|
perf_stats->BeginSystemFrame();
|
|
|
|
LOG_DEBUG(Core, "Initialized OK");
|
|
}
|
|
|
|
void ShutdownMainProcess()
|
|
{
|
|
IVideo & video = modules.Video();
|
|
|
|
SetShuttingDown(true);
|
|
|
|
is_powered_on = false;
|
|
exit_locked = false;
|
|
exit_requested = false;
|
|
|
|
stop_event.request_stop();
|
|
core_timing.SyncPause(false);
|
|
Network::CancelPendingSocketOperations();
|
|
kernel.SuspendEmulation(true);
|
|
kernel.CloseServices();
|
|
applet_manager.StopEventObserver();
|
|
kernel.ShutdownCores();
|
|
applet_manager.Reset();
|
|
{
|
|
std::scoped_lock lk{general_channel_mutex};
|
|
general_channel.clear();
|
|
general_channel_event.reset();
|
|
general_channel_context.reset();
|
|
}
|
|
services.reset();
|
|
service_manager.reset();
|
|
core_timing.ClearPendingEvents();
|
|
audio_core.reset();
|
|
perf_stats.reset();
|
|
cpu_manager.Shutdown();
|
|
kernel.Shutdown();
|
|
stop_event = {};
|
|
Network::RestartSocketOperations();
|
|
|
|
if (auto room_member = room_network.GetRoomMember().lock())
|
|
{
|
|
Network::GameInfo game_info{};
|
|
room_member->SendGameInfo(game_info);
|
|
}
|
|
|
|
LOG_DEBUG(Core, "Shutdown OK");
|
|
}
|
|
|
|
bool IsShuttingDown() const {
|
|
return is_shutting_down;
|
|
}
|
|
|
|
void SetShuttingDown(bool shutting_down) {
|
|
is_shutting_down = shutting_down;
|
|
}
|
|
|
|
PerfStatsResults GetAndResetPerfStats() {
|
|
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
|
|
}
|
|
|
|
mutable std::mutex suspend_guard;
|
|
std::atomic_bool is_paused{};
|
|
std::atomic<bool> is_shutting_down{};
|
|
|
|
Timing::CoreTiming core_timing;
|
|
Kernel::KernelCore kernel;
|
|
std::unique_ptr<Core::DeviceMemory> device_memory;
|
|
std::unique_ptr<AudioCore::AudioCore> audio_core;
|
|
Core::HID::HIDCore hid_core;
|
|
Network::RoomNetwork room_network;
|
|
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
|
|
|
|
CpuManager cpu_manager;
|
|
std::atomic_bool is_powered_on{};
|
|
bool exit_locked = false;
|
|
bool exit_requested = false;
|
|
|
|
std::atomic<bool> nvdec_active{};
|
|
|
|
Reporter reporter;
|
|
std::array<u8, 0x20> build_id{};
|
|
|
|
ISystemModules & modules;
|
|
|
|
/// Applets
|
|
Service::AM::AppletManager applet_manager;
|
|
Service::AM::Frontend::FrontendAppletHolder frontend_applets;
|
|
|
|
/// APM (Performance) services
|
|
Service::APM::Controller apm_controller{core_timing};
|
|
|
|
/// Service State
|
|
Service::Glue::ARPManager arp_manager;
|
|
|
|
/// Service manager
|
|
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
|
|
|
/// Services
|
|
std::unique_ptr<Service::Services> services;
|
|
|
|
/// Network instance
|
|
Network::NetworkInstance network_instance;
|
|
|
|
std::unique_ptr<Core::PerfStats> perf_stats;
|
|
Core::SpeedLimiter speed_limiter;
|
|
|
|
bool is_multicore{};
|
|
bool cpu_hle_requested{};
|
|
bool cpu_hle_guest64{true}, cpu_hle_initialized_supported{true};
|
|
bool cpu_hle_initialized_native_clock{};
|
|
bool is_async_gpu{};
|
|
|
|
::ExecuteProgramCallback execute_program_callback{};
|
|
void * execute_program_user_data{};
|
|
::ExitCallback exit_callback{};
|
|
void * exit_user_data{};
|
|
std::stop_source stop_event;
|
|
|
|
std::array<Core::GPUDirtyMemoryManager, Hardware::NUM_CPU_CORES>
|
|
gpu_dirty_memory_managers;
|
|
|
|
std::deque<std::vector<u8>> user_channel;
|
|
std::vector<std::vector<u8>> general_channel;
|
|
std::mutex general_channel_mutex;
|
|
std::optional<Service::KernelHelpers::ServiceContext> general_channel_context;
|
|
std::optional<Service::Event> general_channel_event;
|
|
|
|
void EnsureGeneralChannelInitialized(System& system) {
|
|
if (!general_channel_event) {
|
|
general_channel_context.emplace(system, "GeneralChannel");
|
|
general_channel_event.emplace(*general_channel_context);
|
|
}
|
|
}
|
|
|
|
Kernel::KProcess * current_process{nullptr};
|
|
};
|
|
|
|
System::System(ISystemModules & modules) :
|
|
impl{std::make_unique<Impl>(*this, modules)}
|
|
{
|
|
}
|
|
|
|
System::~System() = default;
|
|
|
|
CpuManager & System::GetCpuManager()
|
|
{
|
|
return impl->cpu_manager;
|
|
}
|
|
|
|
const CpuManager & System::GetCpuManager() const
|
|
{
|
|
return impl->cpu_manager;
|
|
}
|
|
|
|
void System::InitializeKernel(uint64_t titleID, bool is_64_bit)
|
|
{
|
|
impl->InitializeKernel(*this, titleID, is_64_bit);
|
|
}
|
|
|
|
void System::Initialize() {
|
|
impl->Initialize(*this);
|
|
}
|
|
|
|
void System::Run()
|
|
{
|
|
impl->Run();
|
|
}
|
|
|
|
void System::Pause()
|
|
{
|
|
impl->Pause();
|
|
}
|
|
|
|
bool System::IsPaused() const
|
|
{
|
|
return impl->IsPaused();
|
|
}
|
|
|
|
bool System::IsShuttingDown() const
|
|
{
|
|
return impl->IsShuttingDown();
|
|
}
|
|
|
|
void System::SetShuttingDown(bool shutting_down)
|
|
{
|
|
impl->SetShuttingDown(shutting_down);
|
|
}
|
|
|
|
void System::ShutdownMainProcess()
|
|
{
|
|
impl->ShutdownMainProcess();
|
|
}
|
|
|
|
std::unique_lock<std::mutex> System::StallApplication()
|
|
{
|
|
return impl->StallApplication();
|
|
}
|
|
|
|
void System::UnstallApplication()
|
|
{
|
|
impl->UnstallApplication();
|
|
}
|
|
|
|
void System::SetNVDECActive(bool is_nvdec_active)
|
|
{
|
|
impl->SetNVDECActive(is_nvdec_active);
|
|
}
|
|
|
|
bool System::GetNVDECActive()
|
|
{
|
|
return impl->GetNVDECActive();
|
|
}
|
|
|
|
bool System::IsPoweredOn() const
|
|
{
|
|
return impl->is_powered_on.load(std::memory_order::relaxed);
|
|
}
|
|
|
|
size_t System::GetCurrentHostThreadID() const
|
|
{
|
|
return impl->kernel.GetCurrentHostThreadID();
|
|
}
|
|
|
|
std::span<GPUDirtyMemoryManager> System::GetGPUDirtyMemoryManager()
|
|
{
|
|
return impl->gpu_dirty_memory_managers;
|
|
}
|
|
|
|
void System::GatherGPUDirtyMemory(ICacheInvalidator * invalidator)
|
|
{
|
|
for (auto & manager : impl->gpu_dirty_memory_managers)
|
|
{
|
|
manager.Gather(invalidator);
|
|
}
|
|
}
|
|
|
|
PerfStatsResults System::GetAndResetPerfStats()
|
|
{
|
|
return impl->GetAndResetPerfStats();
|
|
}
|
|
|
|
ISystemModules & System::GetSystemModules()
|
|
{
|
|
return impl->modules;
|
|
}
|
|
|
|
IVideo & System::GetVideo()
|
|
{
|
|
return impl->modules.Video();
|
|
}
|
|
|
|
ISystemloader & System::GetSystemloader()
|
|
{
|
|
return impl->modules.Systemloader();
|
|
}
|
|
|
|
Kernel::PhysicalCore & System::CurrentPhysicalCore()
|
|
{
|
|
return impl->kernel.CurrentPhysicalCore();
|
|
}
|
|
|
|
const Kernel::PhysicalCore & System::CurrentPhysicalCore() const
|
|
{
|
|
return impl->kernel.CurrentPhysicalCore();
|
|
}
|
|
|
|
/// Gets the global scheduler
|
|
Kernel::GlobalSchedulerContext & System::GlobalSchedulerContext()
|
|
{
|
|
return impl->kernel.GlobalSchedulerContext();
|
|
}
|
|
|
|
/// Gets the global scheduler
|
|
const Kernel::GlobalSchedulerContext & System::GlobalSchedulerContext() const
|
|
{
|
|
return impl->kernel.GlobalSchedulerContext();
|
|
}
|
|
|
|
Kernel::KProcess * System::ApplicationProcess()
|
|
{
|
|
return impl->kernel.ApplicationProcess();
|
|
}
|
|
|
|
void System::SetCurrentProcess(Kernel::KProcess * process)
|
|
{
|
|
impl->current_process = process;
|
|
}
|
|
|
|
Kernel::KProcess * System::CurrentProcess() const
|
|
{
|
|
return impl->current_process;
|
|
}
|
|
|
|
Core::DeviceMemory & System::DeviceMemory()
|
|
{
|
|
return *impl->device_memory;
|
|
}
|
|
|
|
const Core::DeviceMemory & System::DeviceMemory() const
|
|
{
|
|
return *impl->device_memory;
|
|
}
|
|
|
|
Memory::Memory & System::ApplicationMemory()
|
|
{
|
|
return impl->kernel.ApplicationProcess()->GetCoreMemory();
|
|
}
|
|
|
|
const Core::Memory::Memory & System::ApplicationMemory() const
|
|
{
|
|
return impl->kernel.ApplicationProcess()->GetCoreMemory();
|
|
}
|
|
|
|
Kernel::KernelCore & System::Kernel()
|
|
{
|
|
return impl->kernel;
|
|
}
|
|
|
|
const Kernel::KernelCore & System::Kernel() const
|
|
{
|
|
return impl->kernel;
|
|
}
|
|
|
|
HID::HIDCore & System::HIDCore()
|
|
{
|
|
return impl->hid_core;
|
|
}
|
|
|
|
const HID::HIDCore & System::HIDCore() const
|
|
{
|
|
return impl->hid_core;
|
|
}
|
|
|
|
AudioCore::AudioCore & System::AudioCore()
|
|
{
|
|
return *impl->audio_core;
|
|
}
|
|
|
|
const AudioCore::AudioCore & System::AudioCore() const
|
|
{
|
|
return *impl->audio_core;
|
|
}
|
|
|
|
ICoreTiming & System::Timing()
|
|
{
|
|
return impl->core_timing;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
const Core::PerfStats & System::GetPerfStats() const
|
|
{
|
|
return *impl->perf_stats;
|
|
}
|
|
|
|
Core::SpeedLimiter & System::SpeedLimiter()
|
|
{
|
|
return impl->speed_limiter;
|
|
}
|
|
|
|
const Core::SpeedLimiter & System::SpeedLimiter() const
|
|
{
|
|
return impl->speed_limiter;
|
|
}
|
|
|
|
std::shared_ptr<InputCommon::InputSubsystem> & System::InputSubsystem() const
|
|
{
|
|
return impl->input_subsystem;
|
|
}
|
|
|
|
u64 System::GetApplicationProcessProgramID() const {
|
|
return impl->kernel.ApplicationProcess()->GetProgramId();
|
|
}
|
|
|
|
void System::AddGlueRegistrationForProcess(Kernel::KProcess & process, uint32_t version, StorageId baseGameStorageId, StorageId updateStorageId, uint8_t * nacpData, uint32_t nacpDataLen)
|
|
{
|
|
std::vector<u8> nacp_data;
|
|
if (nacpDataLen > 0)
|
|
{
|
|
nacp_data.resize(nacpDataLen);
|
|
memcpy(nacp_data.data(), nacpData, nacpDataLen);
|
|
}
|
|
|
|
Service::Glue::ApplicationLaunchProperty launch{};
|
|
launch.title_id = process.GetProgramId();
|
|
launch.version = version;
|
|
launch.base_game_storage_id = baseGameStorageId;
|
|
launch.update_storage_id = updateStorageId;
|
|
|
|
impl->arp_manager.Register(launch.title_id, launch, std::move(nacp_data));
|
|
}
|
|
|
|
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet && set)
|
|
{
|
|
impl->frontend_applets.SetFrontendAppletSet(std::move(set));
|
|
impl->frontend_applets.SetDefaultAppletsIfMissing();
|
|
}
|
|
|
|
Service::AM::Frontend::FrontendAppletHolder & System::GetFrontendAppletHolder()
|
|
{
|
|
return impl->frontend_applets;
|
|
}
|
|
|
|
const Service::AM::Frontend::FrontendAppletHolder & System::GetFrontendAppletHolder() const
|
|
{
|
|
return impl->frontend_applets;
|
|
}
|
|
|
|
Service::AM::AppletManager & System::GetAppletManager()
|
|
{
|
|
return impl->applet_manager;
|
|
}
|
|
|
|
const Reporter & System::GetReporter() const
|
|
{
|
|
return impl->reporter;
|
|
}
|
|
|
|
Service::Glue::ARPManager & System::GetARPManager()
|
|
{
|
|
return impl->arp_manager;
|
|
}
|
|
|
|
const Service::Glue::ARPManager & System::GetARPManager() const
|
|
{
|
|
return impl->arp_manager;
|
|
}
|
|
|
|
Service::APM::Controller & System::GetAPMController()
|
|
{
|
|
return impl->apm_controller;
|
|
}
|
|
|
|
const Service::APM::Controller & System::GetAPMController() const
|
|
{
|
|
return impl->apm_controller;
|
|
}
|
|
|
|
void System::SetExitLocked(bool locked)
|
|
{
|
|
impl->exit_locked = locked;
|
|
}
|
|
|
|
bool System::GetExitLocked() const
|
|
{
|
|
return impl->exit_locked;
|
|
}
|
|
|
|
void System::SetExitRequested(bool requested)
|
|
{
|
|
impl->exit_requested = requested;
|
|
}
|
|
|
|
bool System::GetExitRequested() const
|
|
{
|
|
return impl->exit_requested;
|
|
}
|
|
|
|
void System::SetApplicationProcessBuildID(const CurrentBuildProcessID & id)
|
|
{
|
|
impl->build_id = id;
|
|
}
|
|
|
|
const System::CurrentBuildProcessID & System::GetApplicationProcessBuildID() const
|
|
{
|
|
return impl->build_id;
|
|
}
|
|
|
|
Service::SM::ServiceManager & System::ServiceManager()
|
|
{
|
|
return *impl->service_manager;
|
|
}
|
|
|
|
const Service::SM::ServiceManager & System::ServiceManager() const
|
|
{
|
|
return *impl->service_manager;
|
|
}
|
|
|
|
void System::RegisterCoreThread(std::size_t id)
|
|
{
|
|
impl->kernel.RegisterCoreThread(id);
|
|
}
|
|
|
|
void System::RegisterHostThread()
|
|
{
|
|
impl->kernel.RegisterHostThread();
|
|
}
|
|
|
|
bool System::IsMulticore() const
|
|
{
|
|
return impl->is_multicore;
|
|
}
|
|
|
|
bool System::DebuggerEnabled() const
|
|
{
|
|
//return Settings::values.use_gdbstub.GetValue();
|
|
return false;
|
|
}
|
|
|
|
Core::Debugger & System::GetDebugger()
|
|
{
|
|
UNIMPLEMENTED();
|
|
static Core::Debugger * ptr = nullptr;
|
|
return *ptr;
|
|
}
|
|
|
|
const Core::Debugger & System::GetDebugger() const
|
|
{
|
|
UNIMPLEMENTED();
|
|
static Core::Debugger * ptr = nullptr;
|
|
return *ptr;
|
|
}
|
|
|
|
Network::RoomNetwork & System::GetRoomNetwork()
|
|
{
|
|
return impl->room_network;
|
|
}
|
|
|
|
const Network::RoomNetwork & System::GetRoomNetwork() const
|
|
{
|
|
return impl->room_network;
|
|
}
|
|
|
|
void System::RunServer(std::unique_ptr<Service::ServerManager> && server_manager)
|
|
{
|
|
return impl->kernel.RunServer(std::move(server_manager));
|
|
}
|
|
|
|
void System::RegisterExecuteProgramCallback(::ExecuteProgramCallback callback, void * userData)
|
|
{
|
|
impl->execute_program_callback = callback;
|
|
impl->execute_program_user_data = userData;
|
|
}
|
|
|
|
void System::ExecuteProgram(std::size_t program_index)
|
|
{
|
|
if (impl->execute_program_callback)
|
|
{
|
|
impl->execute_program_callback((uint32_t)program_index, impl->execute_program_user_data);
|
|
}
|
|
else
|
|
{
|
|
LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend");
|
|
}
|
|
}
|
|
|
|
std::deque<std::vector<u8>> & System::GetUserChannel()
|
|
{
|
|
return impl->user_channel;
|
|
}
|
|
|
|
void System::PushGeneralChannelData(std::vector<u8>&& data) {
|
|
std::scoped_lock lk{impl->general_channel_mutex};
|
|
impl->EnsureGeneralChannelInitialized(*this);
|
|
const bool was_empty = impl->general_channel.empty();
|
|
impl->general_channel.push_back(std::move(data));
|
|
if (was_empty) {
|
|
impl->general_channel_event->Signal();
|
|
}
|
|
}
|
|
|
|
bool System::TryPopGeneralChannel(std::vector<u8>& out_data) {
|
|
std::scoped_lock lk{impl->general_channel_mutex};
|
|
if (!impl->general_channel_event || impl->general_channel.empty()) {
|
|
return false;
|
|
}
|
|
|
|
out_data = std::move(impl->general_channel.back());
|
|
impl->general_channel.pop_back();
|
|
if (impl->general_channel.empty()) {
|
|
impl->general_channel_event->Clear();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Service::Event& System::GetGeneralChannelEvent() {
|
|
std::scoped_lock lk{impl->general_channel_mutex};
|
|
impl->EnsureGeneralChannelInitialized(*this);
|
|
return *impl->general_channel_event;
|
|
}
|
|
|
|
void System::RegisterExitCallback(::ExitCallback callback, void * userData)
|
|
{
|
|
impl->exit_callback = callback;
|
|
impl->exit_user_data = userData;
|
|
}
|
|
|
|
void System::Exit()
|
|
{
|
|
if (impl->exit_callback)
|
|
{
|
|
impl->exit_callback(impl->exit_user_data);
|
|
}
|
|
else
|
|
{
|
|
LOG_CRITICAL(Core, "exit_callback must be initialized by the frontend");
|
|
}
|
|
}
|
|
|
|
} // namespace Core
|