// Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include #include #include #include #include #include "common/common_types.h" #include "core/arm/exclusive_monitor.h" #include "core/core_cpu.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/scheduler.h" #include "core/loader/loader.h" #include "core/memory.h" #include "core/perf_stats.h" #include "core/telemetry_session.h" #include "file_sys/vfs_real.h" #include "hle/service/filesystem/filesystem.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/gpu.h" class ARM_Interface; namespace Core::Frontend { class EmuWindow; } namespace Service::SM { class ServiceManager; } namespace VideoCore { class RendererBase; } namespace Core { class System { public: System(const System&) = delete; System& operator=(const System&) = delete; System(System&&) = delete; System& operator=(System&&) = delete; ~System(); /** * Gets the instance of the System singleton class. * @returns Reference to the instance of the System singleton class. */ static System& GetInstance() { return s_instance; } /// Enumeration representing the return values of the System Initialize and Load process. enum class ResultStatus : u32 { Success, ///< Succeeded ErrorNotInitialized, ///< Error trying to use core prior to initialization ErrorGetLoader, ///< Error finding the correct application loader ErrorSystemMode, ///< Error determining the system mode ErrorSystemFiles, ///< Error in finding system files ErrorSharedFont, ///< Error in finding shared font ErrorVideoCore, ///< Error in the video core ErrorUnknown, ///< Any other error ErrorLoader, ///< The base for loader errors (too many to repeat) }; /** * Run the core CPU loop * This function runs the core for the specified number of CPU instructions before trying to * update hardware. This is much faster than SingleStep (and should be equivalent), as the CPU * is not required to do a full dispatch with each instruction. NOTE: the number of instructions * requested is not guaranteed to run, as this will be interrupted preemptively if a hardware * update is requested (e.g. on a thread switch). * @param tight_loop If false, the CPU single-steps. * @return Result status, indicating whether or not the operation succeeded. */ ResultStatus RunLoop(bool tight_loop = true); /** * Step the CPU one instruction * @return Result status, indicating whether or not the operation succeeded. */ ResultStatus SingleStep(); /** * Invalidate the CPU instruction caches * This function should only be used by GDB Stub to support breakpoints, memory updates and * step/continue commands. */ void InvalidateCpuInstructionCaches() { for (auto& cpu : cpu_cores) { cpu->ArmInterface().ClearInstructionCache(); } } /// Shutdown the emulated system. void Shutdown(); /** * Load an executable application. * @param emu_window Reference to the host-system window used for video output and keyboard * input. * @param filepath String path to the executable application to load on the host file system. * @returns ResultStatus code, indicating if the operation succeeded. */ ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath); /** * Indicates if the emulated system is powered on (all subsystems initialized and able to run an * application). * @returns True if the emulated system is powered on, otherwise false. */ bool IsPoweredOn() const { return cpu_barrier && cpu_barrier->IsAlive(); } /** * Returns a reference to the telemetry session for this emulation session. * @returns Reference to the telemetry session. */ Core::TelemetrySession& TelemetrySession() const { return *telemetry_session; } /// Prepare the core emulation for a reschedule void PrepareReschedule(); /// Gets and resets core performance statistics PerfStats::Results GetAndResetPerfStats(); /// Gets an ARM interface to the CPU core that is currently running ARM_Interface& CurrentArmInterface() { return CurrentCpuCore().ArmInterface(); } /// Gets the index of the currently running CPU core size_t CurrentCoreIndex() { return CurrentCpuCore().CoreIndex(); } /// Gets an ARM interface to the CPU core with the specified index ARM_Interface& ArmInterface(size_t core_index); /// Gets a CPU interface to the CPU core with the specified index Cpu& CpuCore(size_t core_index); /// Gets a mutable reference to the GPU interface Tegra::GPU& GPU() { return *gpu_core; } /// Gets an immutable reference to the GPU interface. const Tegra::GPU& GPU() const { return *gpu_core; } /// Gets a mutable reference to the renderer. VideoCore::RendererBase& Renderer() { return *renderer; } /// Gets an immutable reference to the renderer. const VideoCore::RendererBase& Renderer() const { return *renderer; } /// Gets the scheduler for the CPU core that is currently running Kernel::Scheduler& CurrentScheduler() { return *CurrentCpuCore().Scheduler(); } /// Gets the exclusive monitor ExclusiveMonitor& Monitor() { return *cpu_exclusive_monitor; } /// Gets the scheduler for the CPU core with the specified index const std::shared_ptr& Scheduler(size_t core_index); /// Gets the current process Kernel::SharedPtr& CurrentProcess() { return current_process; } /// Gets the name of the current game Loader::ResultStatus GetGameName(std::string& out) const { if (app_loader == nullptr) return Loader::ResultStatus::ErrorNotInitialized; return app_loader->ReadTitle(out); } PerfStats perf_stats; FrameLimiter frame_limiter; void SetStatus(ResultStatus new_status, const char* details = nullptr) { status = new_status; if (details) { status_details = details; } } const std::string& GetStatusDetails() const { return status_details; } Loader::AppLoader& GetAppLoader() const { return *app_loader; } Service::SM::ServiceManager& ServiceManager(); const Service::SM::ServiceManager& ServiceManager() const; void SetGPUDebugContext(std::shared_ptr context) { debug_context = std::move(context); } std::shared_ptr GetGPUDebugContext() const { return debug_context; } void SetFilesystem(FileSys::VirtualFilesystem vfs) { virtual_filesystem = std::move(vfs); } FileSys::VirtualFilesystem GetFilesystem() const { return virtual_filesystem; } private: System(); /// Returns the currently running CPU core Cpu& CurrentCpuCore(); /** * Initialize the emulated system. * @param emu_window Reference to the host-system window used for video output and keyboard * input. * @return ResultStatus code, indicating if the operation succeeded. */ ResultStatus Init(Frontend::EmuWindow& emu_window); /// RealVfsFilesystem instance FileSys::VirtualFilesystem virtual_filesystem; /// AppLoader used to load the current executing application std::unique_ptr app_loader; std::unique_ptr renderer; std::unique_ptr gpu_core; std::shared_ptr debug_context; Kernel::SharedPtr current_process; std::shared_ptr cpu_exclusive_monitor; std::shared_ptr cpu_barrier; std::array, NUM_CPU_CORES> cpu_cores; std::array, NUM_CPU_CORES - 1> cpu_core_threads; size_t active_core{}; ///< Active core, only used in single thread mode /// Service manager std::shared_ptr service_manager; /// Telemetry session for this emulation session std::unique_ptr telemetry_session; static System s_instance; ResultStatus status = ResultStatus::Success; std::string status_details = ""; /// Map of guest threads to CPU cores std::map> thread_to_cpu; }; inline ARM_Interface& CurrentArmInterface() { return System::GetInstance().CurrentArmInterface(); } inline TelemetrySession& Telemetry() { return System::GetInstance().TelemetrySession(); } inline Kernel::SharedPtr& CurrentProcess() { return System::GetInstance().CurrentProcess(); } } // namespace Core