diff options
Diffstat (limited to 'src/core/core.cpp')
-rw-r--r-- | src/core/core.cpp | 515 |
1 files changed, 341 insertions, 174 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp index 2293669e5..75c259068 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -27,135 +27,299 @@ namespace Core { /*static*/ System System::s_instance; -System::System() = default; +namespace { +FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, + const std::string& path) { + // To account for split 00+01+etc files. + std::string dir_name; + std::string filename; + Common::SplitPath(path, &dir_name, &filename, nullptr); + if (filename == "00") { + const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read); + std::vector<FileSys::VirtualFile> concat; + for (u8 i = 0; i < 0x10; ++i) { + auto next = dir->GetFile(fmt::format("{:02X}", i)); + if (next != nullptr) + concat.push_back(std::move(next)); + else { + next = dir->GetFile(fmt::format("{:02x}", i)); + if (next != nullptr) + concat.push_back(std::move(next)); + else + break; + } + } -System::~System() = default; + if (concat.empty()) + return nullptr; + + return FileSys::ConcatenateFiles(concat, dir->GetName()); + } + + return vfs->OpenFile(path, FileSys::Mode::Read); +} /// Runs a CPU core while the system is powered on -static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { +void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { while (Core::System::GetInstance().IsPoweredOn()) { cpu_state->RunLoop(true); } } +} // Anonymous namespace -Cpu& System::CurrentCpuCore() { - // If multicore is enabled, use host thread to figure out the current CPU core - if (Settings::values.use_multi_core) { - const auto& search = thread_to_cpu.find(std::this_thread::get_id()); - ASSERT(search != thread_to_cpu.end()); - ASSERT(search->second); - return *search->second; +struct System::Impl { + Cpu& CurrentCpuCore() { + if (Settings::values.use_multi_core) { + const auto& search = thread_to_cpu.find(std::this_thread::get_id()); + ASSERT(search != thread_to_cpu.end()); + ASSERT(search->second); + return *search->second; + } + + // Otherwise, use single-threaded mode active_core variable + return *cpu_cores[active_core]; } - // Otherwise, use single-threaded mode active_core variable - return *cpu_cores[active_core]; -} + ResultStatus RunLoop(bool tight_loop) { + status = ResultStatus::Success; -System::ResultStatus System::RunLoop(bool tight_loop) { - status = ResultStatus::Success; + // Update thread_to_cpu in case Core 0 is run from a different host thread + thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; - // Update thread_to_cpu in case Core 0 is run from a different host thread - thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; + if (GDBStub::IsServerEnabled()) { + GDBStub::HandlePacket(); - if (GDBStub::IsServerEnabled()) { - GDBStub::HandlePacket(); + // If the loop is halted and we want to step, use a tiny (1) number of instructions to + // execute. Otherwise, get out of the loop function. + if (GDBStub::GetCpuHaltFlag()) { + if (GDBStub::GetCpuStepFlag()) { + tight_loop = false; + } else { + return ResultStatus::Success; + } + } + } - // If the loop is halted and we want to step, use a tiny (1) number of instructions to - // execute. Otherwise, get out of the loop function. - if (GDBStub::GetCpuHaltFlag()) { - if (GDBStub::GetCpuStepFlag()) { - tight_loop = false; - } else { - return ResultStatus::Success; + for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { + cpu_cores[active_core]->RunLoop(tight_loop); + if (Settings::values.use_multi_core) { + // Cores 1-3 are run on other threads in this mode + break; } } + + if (GDBStub::IsServerEnabled()) { + GDBStub::SetCpuStepFlag(false); + } + + return status; } - for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { - cpu_cores[active_core]->RunLoop(tight_loop); + ResultStatus Init(Frontend::EmuWindow& emu_window) { + LOG_DEBUG(HW_Memory, "initialized OK"); + + CoreTiming::Init(); + kernel.Initialize(); + + // Create a default fs if one doesn't already exist. + if (virtual_filesystem == nullptr) + virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); + + current_process = Kernel::Process::Create(kernel, "main"); + + cpu_barrier = std::make_shared<CpuBarrier>(); + cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); + for (size_t index = 0; index < cpu_cores.size(); ++index) { + cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); + } + + telemetry_session = std::make_unique<Core::TelemetrySession>(); + service_manager = std::make_shared<Service::SM::ServiceManager>(); + + Service::Init(service_manager, virtual_filesystem); + GDBStub::Init(); + + renderer = VideoCore::CreateRenderer(emu_window); + if (!renderer->Init()) { + return ResultStatus::ErrorVideoCore; + } + + gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); + + // Create threads for CPU cores 1-3, and build thread_to_cpu map + // CPU core 0 is run on the main thread + thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; if (Settings::values.use_multi_core) { - // Cores 1-3 are run on other threads in this mode - break; + for (size_t index = 0; index < cpu_core_threads.size(); ++index) { + cpu_core_threads[index] = + std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); + thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; + } } - } - if (GDBStub::IsServerEnabled()) { - GDBStub::SetCpuStepFlag(false); + LOG_DEBUG(Core, "Initialized OK"); + + // Reset counters and set time origin to current frame + GetAndResetPerfStats(); + perf_stats.BeginSystemFrame(); + + return ResultStatus::Success; } - return status; -} + ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { + app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); -System::ResultStatus System::SingleStep() { - return RunLoop(false); -} + if (!app_loader) { + LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); + return ResultStatus::ErrorGetLoader; + } + std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode = + app_loader->LoadKernelSystemMode(); -static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, - const std::string& path) { - // To account for split 00+01+etc files. - std::string dir_name; - std::string filename; - Common::SplitPath(path, &dir_name, &filename, nullptr); - if (filename == "00") { - const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read); - std::vector<FileSys::VirtualFile> concat; - for (u8 i = 0; i < 0x10; ++i) { - auto next = dir->GetFile(fmt::format("{:02X}", i)); - if (next != nullptr) - concat.push_back(std::move(next)); - else { - next = dir->GetFile(fmt::format("{:02x}", i)); - if (next != nullptr) - concat.push_back(std::move(next)); - else - break; - } + if (system_mode.second != Loader::ResultStatus::Success) { + LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", + static_cast<int>(system_mode.second)); + + return ResultStatus::ErrorSystemMode; } - if (concat.empty()) - return nullptr; + ResultStatus init_result{Init(emu_window)}; + if (init_result != ResultStatus::Success) { + LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", + static_cast<int>(init_result)); + Shutdown(); + return init_result; + } - return FileSys::ConcatenateFiles(concat, dir->GetName()); + const Loader::ResultStatus load_result{app_loader->Load(current_process)}; + if (load_result != Loader::ResultStatus::Success) { + LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); + Shutdown(); + + return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + + static_cast<u32>(load_result)); + } + status = ResultStatus::Success; + return status; } - return vfs->OpenFile(path, FileSys::Mode::Read); -} + void Shutdown() { + // Log last frame performance stats + auto perf_results = GetAndResetPerfStats(); + Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed", + perf_results.emulation_speed * 100.0); + Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", + perf_results.game_fps); + Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", + perf_results.frametime * 1000.0); + + // Shutdown emulation session + renderer.reset(); + GDBStub::Shutdown(); + Service::Shutdown(); + service_manager.reset(); + telemetry_session.reset(); + gpu_core.reset(); + + // Close all CPU/threading state + cpu_barrier->NotifyEnd(); + if (Settings::values.use_multi_core) { + for (auto& thread : cpu_core_threads) { + thread->join(); + thread.reset(); + } + } + thread_to_cpu.clear(); + for (auto& cpu_core : cpu_cores) { + cpu_core.reset(); + } + cpu_barrier.reset(); -System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { - app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); + // Shutdown kernel and core timing + kernel.Shutdown(); + CoreTiming::Shutdown(); + + // Close app loader + app_loader.reset(); - if (!app_loader) { - LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); - return ResultStatus::ErrorGetLoader; + LOG_DEBUG(Core, "Shutdown OK"); } - std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode = - app_loader->LoadKernelSystemMode(); - if (system_mode.second != Loader::ResultStatus::Success) { - LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", - static_cast<int>(system_mode.second)); + Loader::ResultStatus GetGameName(std::string& out) const { + if (app_loader == nullptr) + return Loader::ResultStatus::ErrorNotInitialized; + return app_loader->ReadTitle(out); + } - return ResultStatus::ErrorSystemMode; + void SetStatus(ResultStatus new_status, const char* details = nullptr) { + status = new_status; + if (details) { + status_details = details; + } } - ResultStatus init_result{Init(emu_window)}; - if (init_result != ResultStatus::Success) { - LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", - static_cast<int>(init_result)); - System::Shutdown(); - return init_result; + PerfStats::Results GetAndResetPerfStats() { + return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); } - const Loader::ResultStatus load_result{app_loader->Load(current_process)}; - if (load_result != Loader::ResultStatus::Success) { - LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); - System::Shutdown(); + Kernel::KernelCore kernel; + /// RealVfsFilesystem instance + FileSys::VirtualFilesystem virtual_filesystem; + /// AppLoader used to load the current executing application + std::unique_ptr<Loader::AppLoader> app_loader; + std::unique_ptr<VideoCore::RendererBase> renderer; + std::unique_ptr<Tegra::GPU> gpu_core; + std::shared_ptr<Tegra::DebugContext> debug_context; + Kernel::SharedPtr<Kernel::Process> current_process; + std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; + std::shared_ptr<CpuBarrier> cpu_barrier; + std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; + std::array<std::unique_ptr<std::thread>, 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::SM::ServiceManager> service_manager; + + /// Telemetry session for this emulation session + std::unique_ptr<Core::TelemetrySession> telemetry_session; + + ResultStatus status = ResultStatus::Success; + std::string status_details = ""; + + /// Map of guest threads to CPU cores + std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu; + + Core::PerfStats perf_stats; + Core::FrameLimiter frame_limiter; +}; + +System::System() : impl{std::make_unique<Impl>()} {} +System::~System() = default; + +Cpu& System::CurrentCpuCore() { + return impl->CurrentCpuCore(); +} + +System::ResultStatus System::RunLoop(bool tight_loop) { + return impl->RunLoop(tight_loop); +} + +System::ResultStatus System::SingleStep() { + return RunLoop(false); +} - return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + - static_cast<u32>(load_result)); +void System::InvalidateCpuInstructionCaches() { + for (auto& cpu : impl->cpu_cores) { + cpu->ArmInterface().ClearInstructionCache(); } - status = ResultStatus::Success; - return status; +} + +System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { + return impl->Load(emu_window, filepath); +} + +bool System::IsPoweredOn() const { + return impl->cpu_barrier && impl->cpu_barrier->IsAlive(); } void System::PrepareReschedule() { @@ -163,131 +327,134 @@ void System::PrepareReschedule() { } PerfStats::Results System::GetAndResetPerfStats() { - return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); + return impl->GetAndResetPerfStats(); } -const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { - ASSERT(core_index < NUM_CPU_CORES); - return cpu_cores[core_index]->Scheduler(); +Core::TelemetrySession& System::TelemetrySession() const { + return *impl->telemetry_session; } -Kernel::KernelCore& System::Kernel() { - return kernel; +ARM_Interface& System::CurrentArmInterface() { + return CurrentCpuCore().ArmInterface(); } -const Kernel::KernelCore& System::Kernel() const { - return kernel; +size_t System::CurrentCoreIndex() { + return CurrentCpuCore().CoreIndex(); +} + +Kernel::Scheduler& System::CurrentScheduler() { + return *CurrentCpuCore().Scheduler(); +} + +const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { + ASSERT(core_index < NUM_CPU_CORES); + return impl->cpu_cores[core_index]->Scheduler(); +} + +Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() { + return impl->current_process; } ARM_Interface& System::ArmInterface(size_t core_index) { ASSERT(core_index < NUM_CPU_CORES); - return cpu_cores[core_index]->ArmInterface(); + return impl->cpu_cores[core_index]->ArmInterface(); } Cpu& System::CpuCore(size_t core_index) { ASSERT(core_index < NUM_CPU_CORES); - return *cpu_cores[core_index]; + return *impl->cpu_cores[core_index]; } -System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { - LOG_DEBUG(HW_Memory, "initialized OK"); +ExclusiveMonitor& System::Monitor() { + return *impl->cpu_exclusive_monitor; +} - CoreTiming::Init(); - kernel.Initialize(); +Tegra::GPU& System::GPU() { + return *impl->gpu_core; +} - // Create a default fs if one doesn't already exist. - if (virtual_filesystem == nullptr) - virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); +const Tegra::GPU& System::GPU() const { + return *impl->gpu_core; +} - current_process = Kernel::Process::Create(kernel, "main"); +VideoCore::RendererBase& System::Renderer() { + return *impl->renderer; +} - cpu_barrier = std::make_shared<CpuBarrier>(); - cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); - for (size_t index = 0; index < cpu_cores.size(); ++index) { - cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); - } +const VideoCore::RendererBase& System::Renderer() const { + return *impl->renderer; +} - telemetry_session = std::make_unique<Core::TelemetrySession>(); - service_manager = std::make_shared<Service::SM::ServiceManager>(); +Kernel::KernelCore& System::Kernel() { + return impl->kernel; +} - Service::Init(service_manager, virtual_filesystem); - GDBStub::Init(); +const Kernel::KernelCore& System::Kernel() const { + return impl->kernel; +} - renderer = VideoCore::CreateRenderer(emu_window); - if (!renderer->Init()) { - return ResultStatus::ErrorVideoCore; - } +Core::PerfStats& System::GetPerfStats() { + return impl->perf_stats; +} - gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); +const Core::PerfStats& System::GetPerfStats() const { + return impl->perf_stats; +} - // Create threads for CPU cores 1-3, and build thread_to_cpu map - // CPU core 0 is run on the main thread - thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; - if (Settings::values.use_multi_core) { - for (size_t index = 0; index < cpu_core_threads.size(); ++index) { - cpu_core_threads[index] = - std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); - thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; - } - } +Core::FrameLimiter& System::FrameLimiter() { + return impl->frame_limiter; +} - LOG_DEBUG(Core, "Initialized OK"); +const Core::FrameLimiter& System::FrameLimiter() const { + return impl->frame_limiter; +} - // Reset counters and set time origin to current frame - GetAndResetPerfStats(); - perf_stats.BeginSystemFrame(); +Loader::ResultStatus System::GetGameName(std::string& out) const { + return impl->GetGameName(out); +} - return ResultStatus::Success; +void System::SetStatus(ResultStatus new_status, const char* details) { + impl->SetStatus(new_status, details); } -void System::Shutdown() { - // Log last frame performance stats - auto perf_results = GetAndResetPerfStats(); - Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed", - perf_results.emulation_speed * 100.0); - Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", - perf_results.game_fps); - Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", - perf_results.frametime * 1000.0); - - // Shutdown emulation session - renderer.reset(); - GDBStub::Shutdown(); - Service::Shutdown(); - service_manager.reset(); - telemetry_session.reset(); - gpu_core.reset(); - - // Close all CPU/threading state - cpu_barrier->NotifyEnd(); - if (Settings::values.use_multi_core) { - for (auto& thread : cpu_core_threads) { - thread->join(); - thread.reset(); - } - } - thread_to_cpu.clear(); - for (auto& cpu_core : cpu_cores) { - cpu_core.reset(); - } - cpu_barrier.reset(); +const std::string& System::GetStatusDetails() const { + return impl->status_details; +} - // Shutdown kernel and core timing - kernel.Shutdown(); - CoreTiming::Shutdown(); +Loader::AppLoader& System::GetAppLoader() const { + return *impl->app_loader; +} - // Close app loader - app_loader.reset(); +void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) { + impl->debug_context = std::move(context); +} - LOG_DEBUG(Core, "Shutdown OK"); +std::shared_ptr<Tegra::DebugContext> System::GetGPUDebugContext() const { + return impl->debug_context; +} + +void System::SetFilesystem(FileSys::VirtualFilesystem vfs) { + impl->virtual_filesystem = std::move(vfs); +} + +FileSys::VirtualFilesystem System::GetFilesystem() const { + return impl->virtual_filesystem; +} + +System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { + return impl->Init(emu_window); +} + +void System::Shutdown() { + impl->Shutdown(); } Service::SM::ServiceManager& System::ServiceManager() { - return *service_manager; + return *impl->service_manager; } const Service::SM::ServiceManager& System::ServiceManager() const { - return *service_manager; + return *impl->service_manager; } } // namespace Core |