2014-04-08 23:19:26 +00:00
|
|
|
// Copyright 2014 Citra Emulator Project
|
2014-12-17 05:38:14 +00:00
|
|
|
// Licensed under GPLv2 or any later version
|
2014-04-08 23:19:26 +00:00
|
|
|
// Refer to the license.txt file included.
|
2013-09-05 22:33:46 +00:00
|
|
|
|
2018-08-31 16:21:34 +00:00
|
|
|
#include <array>
|
2015-12-29 23:03:08 +00:00
|
|
|
#include <memory>
|
2017-03-09 01:21:31 +00:00
|
|
|
#include <utility>
|
2018-08-31 16:21:34 +00:00
|
|
|
|
2018-11-28 19:00:44 +00:00
|
|
|
#include "common/file_util.h"
|
2015-05-06 07:06:12 +00:00
|
|
|
#include "common/logging/log.h"
|
2018-08-10 01:33:13 +00:00
|
|
|
#include "common/string_util.h"
|
2018-08-31 16:21:34 +00:00
|
|
|
#include "core/arm/exclusive_monitor.h"
|
2016-09-21 06:52:38 +00:00
|
|
|
#include "core/core.h"
|
2018-08-31 16:21:34 +00:00
|
|
|
#include "core/core_cpu.h"
|
2016-09-02 03:18:01 +00:00
|
|
|
#include "core/core_timing.h"
|
2018-11-22 06:27:23 +00:00
|
|
|
#include "core/cpu_core_manager.h"
|
2018-09-02 14:53:06 +00:00
|
|
|
#include "core/file_sys/mode.h"
|
2018-12-28 05:03:01 +00:00
|
|
|
#include "core/file_sys/registered_cache.h"
|
2018-09-02 14:53:06 +00:00
|
|
|
#include "core/file_sys/vfs_concat.h"
|
|
|
|
#include "core/file_sys/vfs_real.h"
|
2016-09-02 03:18:01 +00:00
|
|
|
#include "core/gdbstub/gdbstub.h"
|
2018-04-20 23:29:04 +00:00
|
|
|
#include "core/hle/kernel/client_port.h"
|
2016-12-16 00:01:48 +00:00
|
|
|
#include "core/hle/kernel/kernel.h"
|
2017-09-26 23:17:47 +00:00
|
|
|
#include "core/hle/kernel/process.h"
|
2018-08-31 16:21:34 +00:00
|
|
|
#include "core/hle/kernel/scheduler.h"
|
2014-05-23 02:54:07 +00:00
|
|
|
#include "core/hle/kernel/thread.h"
|
2019-03-11 23:33:49 +00:00
|
|
|
#include "core/hle/service/am/applets/applets.h"
|
2019-06-29 02:46:31 +00:00
|
|
|
#include "core/hle/service/apm/controller.h"
|
2019-06-24 23:27:35 +00:00
|
|
|
#include "core/hle/service/glue/manager.h"
|
2016-12-16 05:37:38 +00:00
|
|
|
#include "core/hle/service/service.h"
|
2018-04-20 23:29:04 +00:00
|
|
|
#include "core/hle/service/sm/sm.h"
|
2016-12-16 00:01:48 +00:00
|
|
|
#include "core/loader/loader.h"
|
2018-08-31 16:21:34 +00:00
|
|
|
#include "core/perf_stats.h"
|
2019-05-18 01:45:56 +00:00
|
|
|
#include "core/reporter.h"
|
2018-12-28 23:36:37 +00:00
|
|
|
#include "core/settings.h"
|
2018-08-31 16:21:34 +00:00
|
|
|
#include "core/telemetry_session.h"
|
2019-06-07 15:11:11 +00:00
|
|
|
#include "core/tools/freezer.h"
|
2018-12-23 02:32:05 +00:00
|
|
|
#include "file_sys/cheat_engine.h"
|
2019-06-24 23:27:35 +00:00
|
|
|
#include "file_sys/patch_manager.h"
|
2018-08-31 16:21:34 +00:00
|
|
|
#include "video_core/debug_utils/debug_utils.h"
|
2018-08-03 16:55:58 +00:00
|
|
|
#include "video_core/renderer_base.h"
|
2016-12-16 00:01:48 +00:00
|
|
|
#include "video_core/video_core.h"
|
2015-09-02 12:56:38 +00:00
|
|
|
|
2013-09-05 22:33:46 +00:00
|
|
|
namespace Core {
|
|
|
|
|
2019-06-24 23:27:35 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
FileSys::StorageId GetStorageIdForFrontendSlot(
|
|
|
|
std::optional<FileSys::ContentProviderUnionSlot> slot) {
|
|
|
|
if (!slot.has_value()) {
|
|
|
|
return FileSys::StorageId::None;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (*slot) {
|
|
|
|
case FileSys::ContentProviderUnionSlot::UserNAND:
|
|
|
|
return FileSys::StorageId::NandUser;
|
|
|
|
case FileSys::ContentProviderUnionSlot::SysNAND:
|
|
|
|
return FileSys::StorageId::NandSystem;
|
|
|
|
case FileSys::ContentProviderUnionSlot::SDMC:
|
|
|
|
return FileSys::StorageId::SdCard;
|
|
|
|
case FileSys::ContentProviderUnionSlot::FrontendManual:
|
|
|
|
return FileSys::StorageId::Host;
|
|
|
|
default:
|
|
|
|
return FileSys::StorageId::None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // Anonymous namespace
|
|
|
|
|
2016-12-16 00:01:48 +00:00
|
|
|
/*static*/ System System::s_instance;
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2018-07-18 22:15:16 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
if (concat.empty())
|
|
|
|
return nullptr;
|
|
|
|
|
2018-09-25 21:38:16 +00:00
|
|
|
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
|
2018-08-30 14:50:54 +00:00
|
|
|
}
|
|
|
|
|
2018-11-28 19:00:44 +00:00
|
|
|
if (FileUtil::IsDirectory(path))
|
|
|
|
return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read);
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
return vfs->OpenFile(path, FileSys::Mode::Read);
|
|
|
|
}
|
|
|
|
struct System::Impl {
|
2019-05-26 15:40:41 +00:00
|
|
|
explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {}
|
2018-11-28 19:00:44 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
Cpu& CurrentCpuCore() {
|
2018-11-22 06:27:23 +00:00
|
|
|
return cpu_core_manager.GetCurrentCore();
|
2018-05-08 02:57:39 +00:00
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
ResultStatus RunLoop(bool tight_loop) {
|
|
|
|
status = ResultStatus::Success;
|
2018-05-08 02:57:39 +00:00
|
|
|
|
2018-11-22 06:27:23 +00:00
|
|
|
cpu_core_manager.RunLoop(tight_loop);
|
2018-08-30 14:50:54 +00:00
|
|
|
|
|
|
|
return status;
|
2015-09-02 12:56:38 +00:00
|
|
|
}
|
|
|
|
|
2018-11-22 06:27:23 +00:00
|
|
|
ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
2018-08-30 14:50:54 +00:00
|
|
|
LOG_DEBUG(HW_Memory, "initialized OK");
|
|
|
|
|
2019-02-14 17:42:58 +00:00
|
|
|
core_timing.Initialize();
|
core/cpu_core_manager: Create threads separately from initialization.
Our initialization process is a little wonky than one would expect when
it comes to code flow. We initialize the CPU last, as opposed to
hardware, where the CPU obviously needs to be first, otherwise nothing
else would work, and we have code that adds checks to get around this.
For example, in the page table setting code, we check to see if the
system is turned on before we even notify the CPU instances of a page
table switch. This results in dead code (at the moment), because the
only time a page table switch will occur is when the system is *not*
running, preventing the emulated CPU instances from being notified of a
page table switch in a convenient manner (technically the code path
could be taken, but we don't emulate the process creation svc handlers
yet).
This moves the threads creation into its own member function of the core
manager and restores a little order (and predictability) to our
initialization process.
Previously, in the multi-threaded cases, we'd kick off several threads
before even the main kernel process was created and ready to execute (gross!).
Now the initialization process is like so:
Initialization:
1. Timers
2. CPU
3. Kernel
4. Filesystem stuff (kind of gross, but can be amended trivially)
5. Applet stuff (ditto in terms of being kind of gross)
6. Main process (will be moved into the loading step in a following
change)
7. Telemetry (this should be initialized last in the future).
8. Services (4 and 5 should ideally be alongside this).
9. GDB (gross. Uses namespace scope state. Needs to be refactored into a
class or booted altogether).
10. Renderer
11. GPU (will also have its threads created in a separate step in a
following change).
Which... isn't *ideal* per-se, however getting rid of the wonky
intertwining of CPU state initialization out of this mix gets rid of
most of the footguns when it comes to our initialization process.
2019-04-09 17:25:54 +00:00
|
|
|
cpu_core_manager.Initialize();
|
2019-03-05 17:28:10 +00:00
|
|
|
kernel.Initialize();
|
2018-08-30 14:50:54 +00:00
|
|
|
|
2018-12-28 23:36:37 +00:00
|
|
|
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
2018-12-29 01:24:24 +00:00
|
|
|
std::chrono::system_clock::now().time_since_epoch());
|
2018-12-28 23:36:37 +00:00
|
|
|
Settings::values.custom_rtc_differential =
|
|
|
|
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
// Create a default fs if one doesn't already exist.
|
|
|
|
if (virtual_filesystem == nullptr)
|
|
|
|
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
2018-12-28 05:03:01 +00:00
|
|
|
if (content_provider == nullptr)
|
|
|
|
content_provider = std::make_unique<FileSys::ContentProviderUnion>();
|
2018-08-30 14:50:54 +00:00
|
|
|
|
2018-11-11 21:39:25 +00:00
|
|
|
/// Create default implementations of applets if one is not provided.
|
2019-03-11 23:33:49 +00:00
|
|
|
applet_manager.SetDefaultAppletsIfMissing();
|
2018-11-11 21:39:25 +00:00
|
|
|
|
2019-06-24 23:27:35 +00:00
|
|
|
/// Reset all glue registrations
|
|
|
|
arp_manager.ResetAll();
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
|
|
|
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
|
|
|
|
2019-06-29 01:02:34 +00:00
|
|
|
Service::Init(service_manager, system);
|
2018-08-30 14:50:54 +00:00
|
|
|
GDBStub::Init();
|
|
|
|
|
2019-01-15 19:28:42 +00:00
|
|
|
renderer = VideoCore::CreateRenderer(emu_window, system);
|
2018-08-30 14:50:54 +00:00
|
|
|
if (!renderer->Init()) {
|
|
|
|
return ResultStatus::ErrorVideoCore;
|
|
|
|
}
|
|
|
|
|
2019-04-09 18:02:00 +00:00
|
|
|
gpu_core = VideoCore::CreateGPU(system);
|
2019-01-08 02:42:32 +00:00
|
|
|
|
2019-04-09 18:02:00 +00:00
|
|
|
is_powered_on = true;
|
2018-08-30 14:50:54 +00:00
|
|
|
|
|
|
|
LOG_DEBUG(Core, "Initialized OK");
|
|
|
|
|
|
|
|
// Reset counters and set time origin to current frame
|
|
|
|
GetAndResetPerfStats();
|
|
|
|
perf_stats.BeginSystemFrame();
|
|
|
|
|
|
|
|
return ResultStatus::Success;
|
2018-08-07 02:01:24 +00:00
|
|
|
}
|
|
|
|
|
2018-11-22 06:27:23 +00:00
|
|
|
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
|
|
|
const std::string& filepath) {
|
2018-08-30 14:50:54 +00:00
|
|
|
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
|
|
|
if (!app_loader) {
|
|
|
|
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
|
|
|
return ResultStatus::ErrorGetLoader;
|
|
|
|
}
|
2018-08-10 00:48:41 +00:00
|
|
|
|
2018-11-22 06:27:23 +00:00
|
|
|
ResultStatus init_result{Init(system, emu_window)};
|
2018-08-30 14:50:54 +00:00
|
|
|
if (init_result != ResultStatus::Success) {
|
|
|
|
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
|
|
|
static_cast<int>(init_result));
|
|
|
|
Shutdown();
|
|
|
|
return init_result;
|
|
|
|
}
|
2018-08-10 00:48:41 +00:00
|
|
|
|
2019-05-29 01:12:23 +00:00
|
|
|
telemetry_session->AddInitialInfo(*app_loader);
|
2019-06-10 04:28:33 +00:00
|
|
|
auto main_process =
|
|
|
|
Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
|
2019-04-09 21:03:04 +00:00
|
|
|
const auto [load_result, load_parameters] = app_loader->Load(*main_process);
|
2018-08-30 14:50:54 +00:00
|
|
|
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));
|
|
|
|
}
|
2019-06-24 23:27:35 +00:00
|
|
|
AddGlueRegistrationForProcess(*app_loader, *main_process);
|
2019-04-09 19:27:44 +00:00
|
|
|
kernel.MakeCurrentProcess(main_process.get());
|
2019-01-14 01:05:53 +00:00
|
|
|
|
core/cpu_core_manager: Create threads separately from initialization.
Our initialization process is a little wonky than one would expect when
it comes to code flow. We initialize the CPU last, as opposed to
hardware, where the CPU obviously needs to be first, otherwise nothing
else would work, and we have code that adds checks to get around this.
For example, in the page table setting code, we check to see if the
system is turned on before we even notify the CPU instances of a page
table switch. This results in dead code (at the moment), because the
only time a page table switch will occur is when the system is *not*
running, preventing the emulated CPU instances from being notified of a
page table switch in a convenient manner (technically the code path
could be taken, but we don't emulate the process creation svc handlers
yet).
This moves the threads creation into its own member function of the core
manager and restores a little order (and predictability) to our
initialization process.
Previously, in the multi-threaded cases, we'd kick off several threads
before even the main kernel process was created and ready to execute (gross!).
Now the initialization process is like so:
Initialization:
1. Timers
2. CPU
3. Kernel
4. Filesystem stuff (kind of gross, but can be amended trivially)
5. Applet stuff (ditto in terms of being kind of gross)
6. Main process (will be moved into the loading step in a following
change)
7. Telemetry (this should be initialized last in the future).
8. Services (4 and 5 should ideally be alongside this).
9. GDB (gross. Uses namespace scope state. Needs to be refactored into a
class or booted altogether).
10. Renderer
11. GPU (will also have its threads created in a separate step in a
following change).
Which... isn't *ideal* per-se, however getting rid of the wonky
intertwining of CPU state initialization out of this mix gets rid of
most of the footguns when it comes to our initialization process.
2019-04-09 17:25:54 +00:00
|
|
|
// Main process has been loaded and been made current.
|
2019-04-09 18:02:00 +00:00
|
|
|
// Begin GPU and CPU execution.
|
|
|
|
gpu_core->Start();
|
core/cpu_core_manager: Create threads separately from initialization.
Our initialization process is a little wonky than one would expect when
it comes to code flow. We initialize the CPU last, as opposed to
hardware, where the CPU obviously needs to be first, otherwise nothing
else would work, and we have code that adds checks to get around this.
For example, in the page table setting code, we check to see if the
system is turned on before we even notify the CPU instances of a page
table switch. This results in dead code (at the moment), because the
only time a page table switch will occur is when the system is *not*
running, preventing the emulated CPU instances from being notified of a
page table switch in a convenient manner (technically the code path
could be taken, but we don't emulate the process creation svc handlers
yet).
This moves the threads creation into its own member function of the core
manager and restores a little order (and predictability) to our
initialization process.
Previously, in the multi-threaded cases, we'd kick off several threads
before even the main kernel process was created and ready to execute (gross!).
Now the initialization process is like so:
Initialization:
1. Timers
2. CPU
3. Kernel
4. Filesystem stuff (kind of gross, but can be amended trivially)
5. Applet stuff (ditto in terms of being kind of gross)
6. Main process (will be moved into the loading step in a following
change)
7. Telemetry (this should be initialized last in the future).
8. Services (4 and 5 should ideally be alongside this).
9. GDB (gross. Uses namespace scope state. Needs to be refactored into a
class or booted altogether).
10. Renderer
11. GPU (will also have its threads created in a separate step in a
following change).
Which... isn't *ideal* per-se, however getting rid of the wonky
intertwining of CPU state initialization out of this mix gets rid of
most of the footguns when it comes to our initialization process.
2019-04-09 17:25:54 +00:00
|
|
|
cpu_core_manager.StartThreads();
|
|
|
|
|
2019-04-09 21:03:04 +00:00
|
|
|
// All threads are started, begin main process execution, now that we're in the clear.
|
|
|
|
main_process->Run(load_parameters->main_thread_priority,
|
|
|
|
load_parameters->main_thread_stack_size);
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
status = ResultStatus::Success;
|
|
|
|
return status;
|
2018-08-10 00:48:41 +00:00
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
void Shutdown() {
|
|
|
|
// Log last frame performance stats
|
2019-03-02 20:17:40 +00:00
|
|
|
const auto perf_results = GetAndResetPerfStats();
|
|
|
|
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
|
|
|
|
perf_results.emulation_speed * 100.0);
|
|
|
|
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
|
|
|
|
perf_results.game_fps);
|
|
|
|
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
|
|
|
|
perf_results.frametime * 1000.0);
|
2018-08-30 14:50:54 +00:00
|
|
|
|
2018-11-22 06:27:23 +00:00
|
|
|
is_powered_on = false;
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
// Shutdown emulation session
|
|
|
|
renderer.reset();
|
|
|
|
GDBStub::Shutdown();
|
|
|
|
Service::Shutdown();
|
|
|
|
service_manager.reset();
|
2018-12-23 02:32:05 +00:00
|
|
|
cheat_engine.reset();
|
2018-08-30 14:50:54 +00:00
|
|
|
telemetry_session.reset();
|
|
|
|
gpu_core.reset();
|
|
|
|
|
|
|
|
// Close all CPU/threading state
|
2018-11-22 06:27:23 +00:00
|
|
|
cpu_core_manager.Shutdown();
|
2018-08-10 00:48:41 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
// Shutdown kernel and core timing
|
|
|
|
kernel.Shutdown();
|
2019-02-14 17:42:58 +00:00
|
|
|
core_timing.Shutdown();
|
2018-08-30 14:50:54 +00:00
|
|
|
|
|
|
|
// Close app loader
|
|
|
|
app_loader.reset();
|
2016-12-16 00:01:48 +00:00
|
|
|
|
2018-12-28 23:20:29 +00:00
|
|
|
// Clear all applets
|
2019-03-11 23:33:49 +00:00
|
|
|
applet_manager.ClearAll();
|
2018-12-28 23:20:29 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
LOG_DEBUG(Core, "Shutdown OK");
|
2016-12-16 00:01:48 +00:00
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
Loader::ResultStatus GetGameName(std::string& out) const {
|
|
|
|
if (app_loader == nullptr)
|
|
|
|
return Loader::ResultStatus::ErrorNotInitialized;
|
|
|
|
return app_loader->ReadTitle(out);
|
|
|
|
}
|
2017-03-08 21:28:30 +00:00
|
|
|
|
2019-06-24 23:27:35 +00:00
|
|
|
void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::Process& process) {
|
|
|
|
std::vector<u8> nacp_data;
|
|
|
|
FileSys::NACP nacp;
|
|
|
|
if (loader.ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
|
|
|
nacp_data = nacp.GetRawBytes();
|
|
|
|
} else {
|
|
|
|
nacp_data.resize(sizeof(FileSys::RawNACP));
|
|
|
|
}
|
|
|
|
|
|
|
|
Service::Glue::ApplicationLaunchProperty launch{};
|
|
|
|
launch.title_id = process.GetTitleID();
|
|
|
|
|
|
|
|
FileSys::PatchManager pm{launch.title_id};
|
|
|
|
launch.version = pm.GetGameVersion().value_or(0);
|
|
|
|
|
|
|
|
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
|
|
|
|
// current_process_game_card use correct StorageId
|
|
|
|
launch.base_game_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
|
|
|
|
launch.title_id, FileSys::ContentRecordType::Program));
|
|
|
|
launch.update_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
|
|
|
|
FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
|
|
|
|
|
|
|
|
arp_manager.Register(launch.title_id, launch, std::move(nacp_data));
|
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
|
|
|
|
status = new_status;
|
|
|
|
if (details) {
|
|
|
|
status_details = details;
|
|
|
|
}
|
2016-12-16 00:01:48 +00:00
|
|
|
}
|
|
|
|
|
2018-08-31 16:21:34 +00:00
|
|
|
PerfStatsResults GetAndResetPerfStats() {
|
2019-02-14 17:42:58 +00:00
|
|
|
return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs());
|
2016-12-16 00:01:48 +00:00
|
|
|
}
|
|
|
|
|
2019-02-14 17:42:58 +00:00
|
|
|
Timing::CoreTiming core_timing;
|
2018-08-30 14:50:54 +00:00
|
|
|
Kernel::KernelCore kernel;
|
|
|
|
/// RealVfsFilesystem instance
|
|
|
|
FileSys::VirtualFilesystem virtual_filesystem;
|
2018-12-28 05:03:01 +00:00
|
|
|
/// ContentProviderUnion instance
|
|
|
|
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
|
2018-08-30 14:50:54 +00:00
|
|
|
/// 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;
|
2018-11-22 06:27:23 +00:00
|
|
|
CpuCoreManager cpu_core_manager;
|
|
|
|
bool is_powered_on = false;
|
2018-08-30 14:50:54 +00:00
|
|
|
|
2018-12-23 02:32:05 +00:00
|
|
|
std::unique_ptr<FileSys::CheatEngine> cheat_engine;
|
2019-06-07 15:11:11 +00:00
|
|
|
std::unique_ptr<Tools::Freezer> memory_freezer;
|
2018-12-23 02:32:05 +00:00
|
|
|
|
2018-11-11 21:39:25 +00:00
|
|
|
/// Frontend applets
|
2019-03-11 23:33:49 +00:00
|
|
|
Service::AM::Applets::AppletManager applet_manager;
|
2018-11-11 21:39:25 +00:00
|
|
|
|
2019-06-29 02:46:31 +00:00
|
|
|
/// APM (Performance) services
|
|
|
|
Service::APM::Controller apm_controller{core_timing};
|
|
|
|
|
2019-06-24 23:27:35 +00:00
|
|
|
/// Glue services
|
|
|
|
Service::Glue::ARPManager arp_manager;
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
/// Service manager
|
|
|
|
std::shared_ptr<Service::SM::ServiceManager> service_manager;
|
|
|
|
|
|
|
|
/// Telemetry session for this emulation session
|
|
|
|
std::unique_ptr<Core::TelemetrySession> telemetry_session;
|
|
|
|
|
2019-05-18 01:45:56 +00:00
|
|
|
Reporter reporter;
|
2019-05-18 01:41:33 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
ResultStatus status = ResultStatus::Success;
|
|
|
|
std::string status_details = "";
|
|
|
|
|
|
|
|
Core::PerfStats perf_stats;
|
|
|
|
Core::FrameLimiter frame_limiter;
|
|
|
|
};
|
|
|
|
|
2019-03-05 17:28:10 +00:00
|
|
|
System::System() : impl{std::make_unique<Impl>(*this)} {}
|
2018-08-30 14:50:54 +00:00
|
|
|
System::~System() = default;
|
|
|
|
|
|
|
|
Cpu& System::CurrentCpuCore() {
|
|
|
|
return impl->CurrentCpuCore();
|
|
|
|
}
|
|
|
|
|
2018-10-28 21:37:31 +00:00
|
|
|
const Cpu& System::CurrentCpuCore() const {
|
|
|
|
return impl->CurrentCpuCore();
|
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
System::ResultStatus System::RunLoop(bool tight_loop) {
|
|
|
|
return impl->RunLoop(tight_loop);
|
|
|
|
}
|
|
|
|
|
|
|
|
System::ResultStatus System::SingleStep() {
|
|
|
|
return RunLoop(false);
|
|
|
|
}
|
2016-12-16 00:01:48 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
void System::InvalidateCpuInstructionCaches() {
|
2018-11-22 06:27:23 +00:00
|
|
|
impl->cpu_core_manager.InvalidateAllInstructionCaches();
|
2018-08-30 14:50:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
2018-11-22 06:27:23 +00:00
|
|
|
return impl->Load(*this, emu_window, filepath);
|
2018-08-30 14:50:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool System::IsPoweredOn() const {
|
2018-11-22 06:27:23 +00:00
|
|
|
return impl->is_powered_on;
|
2013-09-05 22:33:46 +00:00
|
|
|
}
|
|
|
|
|
2016-12-16 05:37:38 +00:00
|
|
|
void System::PrepareReschedule() {
|
2018-05-03 01:26:14 +00:00
|
|
|
CurrentCpuCore().PrepareReschedule();
|
2016-12-16 05:37:38 +00:00
|
|
|
}
|
|
|
|
|
2018-08-31 16:21:34 +00:00
|
|
|
PerfStatsResults System::GetAndResetPerfStats() {
|
2018-08-30 14:50:54 +00:00
|
|
|
return impl->GetAndResetPerfStats();
|
2017-02-19 22:34:47 +00:00
|
|
|
}
|
|
|
|
|
2018-10-28 21:37:31 +00:00
|
|
|
TelemetrySession& System::TelemetrySession() {
|
|
|
|
return *impl->telemetry_session;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TelemetrySession& System::TelemetrySession() const {
|
2018-08-30 14:50:54 +00:00
|
|
|
return *impl->telemetry_session;
|
2018-05-03 04:34:54 +00:00
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
ARM_Interface& System::CurrentArmInterface() {
|
|
|
|
return CurrentCpuCore().ArmInterface();
|
2018-08-28 16:30:33 +00:00
|
|
|
}
|
|
|
|
|
2018-10-28 21:37:31 +00:00
|
|
|
const ARM_Interface& System::CurrentArmInterface() const {
|
|
|
|
return CurrentCpuCore().ArmInterface();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::size_t System::CurrentCoreIndex() const {
|
2018-08-30 14:50:54 +00:00
|
|
|
return CurrentCpuCore().CoreIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
Kernel::Scheduler& System::CurrentScheduler() {
|
2018-10-15 13:25:11 +00:00
|
|
|
return CurrentCpuCore().Scheduler();
|
2018-08-30 14:50:54 +00:00
|
|
|
}
|
|
|
|
|
2018-10-28 21:37:31 +00:00
|
|
|
const Kernel::Scheduler& System::CurrentScheduler() const {
|
|
|
|
return CurrentCpuCore().Scheduler();
|
|
|
|
}
|
|
|
|
|
2018-10-15 13:25:11 +00:00
|
|
|
Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
|
|
|
|
return CpuCore(core_index).Scheduler();
|
|
|
|
}
|
|
|
|
|
|
|
|
const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
|
|
|
|
return CpuCore(core_index).Scheduler();
|
2018-08-30 14:50:54 +00:00
|
|
|
}
|
|
|
|
|
2018-10-10 04:42:10 +00:00
|
|
|
Kernel::Process* System::CurrentProcess() {
|
2018-09-07 00:34:51 +00:00
|
|
|
return impl->kernel.CurrentProcess();
|
|
|
|
}
|
|
|
|
|
2018-10-10 04:42:10 +00:00
|
|
|
const Kernel::Process* System::CurrentProcess() const {
|
2018-09-07 00:34:51 +00:00
|
|
|
return impl->kernel.CurrentProcess();
|
2018-08-28 16:30:33 +00:00
|
|
|
}
|
|
|
|
|
2018-09-15 13:21:06 +00:00
|
|
|
ARM_Interface& System::ArmInterface(std::size_t core_index) {
|
2018-10-18 16:07:21 +00:00
|
|
|
return CpuCore(core_index).ArmInterface();
|
2018-05-03 04:34:54 +00:00
|
|
|
}
|
|
|
|
|
2018-10-28 21:37:31 +00:00
|
|
|
const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
|
|
|
|
return CpuCore(core_index).ArmInterface();
|
|
|
|
}
|
|
|
|
|
2018-09-15 13:21:06 +00:00
|
|
|
Cpu& System::CpuCore(std::size_t core_index) {
|
2018-11-22 06:27:23 +00:00
|
|
|
return impl->cpu_core_manager.GetCore(core_index);
|
2018-05-06 03:54:43 +00:00
|
|
|
}
|
|
|
|
|
2018-10-15 13:25:11 +00:00
|
|
|
const Cpu& System::CpuCore(std::size_t core_index) const {
|
|
|
|
ASSERT(core_index < NUM_CPU_CORES);
|
2018-11-22 06:27:23 +00:00
|
|
|
return impl->cpu_core_manager.GetCore(core_index);
|
2018-10-15 13:25:11 +00:00
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
ExclusiveMonitor& System::Monitor() {
|
2018-11-22 06:27:23 +00:00
|
|
|
return impl->cpu_core_manager.GetExclusiveMonitor();
|
2018-10-28 21:37:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const ExclusiveMonitor& System::Monitor() const {
|
2018-11-22 06:27:23 +00:00
|
|
|
return impl->cpu_core_manager.GetExclusiveMonitor();
|
2018-08-30 14:50:54 +00:00
|
|
|
}
|
2016-12-16 00:01:48 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
Tegra::GPU& System::GPU() {
|
|
|
|
return *impl->gpu_core;
|
|
|
|
}
|
2018-02-21 16:37:48 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
const Tegra::GPU& System::GPU() const {
|
|
|
|
return *impl->gpu_core;
|
|
|
|
}
|
2018-08-03 15:51:48 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
VideoCore::RendererBase& System::Renderer() {
|
|
|
|
return *impl->renderer;
|
|
|
|
}
|
2018-03-13 21:49:59 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
const VideoCore::RendererBase& System::Renderer() const {
|
|
|
|
return *impl->renderer;
|
|
|
|
}
|
2018-01-12 16:06:30 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
Kernel::KernelCore& System::Kernel() {
|
|
|
|
return impl->kernel;
|
|
|
|
}
|
2018-04-20 23:29:04 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
const Kernel::KernelCore& System::Kernel() const {
|
|
|
|
return impl->kernel;
|
|
|
|
}
|
2016-12-16 00:01:48 +00:00
|
|
|
|
2019-02-14 17:42:58 +00:00
|
|
|
Timing::CoreTiming& System::CoreTiming() {
|
|
|
|
return impl->core_timing;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Timing::CoreTiming& System::CoreTiming() const {
|
|
|
|
return impl->core_timing;
|
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
Core::PerfStats& System::GetPerfStats() {
|
|
|
|
return impl->perf_stats;
|
|
|
|
}
|
2014-10-25 19:54:44 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
const Core::PerfStats& System::GetPerfStats() const {
|
|
|
|
return impl->perf_stats;
|
|
|
|
}
|
2018-08-03 16:55:58 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
Core::FrameLimiter& System::FrameLimiter() {
|
|
|
|
return impl->frame_limiter;
|
|
|
|
}
|
2018-05-03 01:26:14 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
const Core::FrameLimiter& System::FrameLimiter() const {
|
|
|
|
return impl->frame_limiter;
|
|
|
|
}
|
2016-12-16 00:01:48 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
|
|
|
return impl->GetGameName(out);
|
|
|
|
}
|
2017-02-19 22:34:47 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
void System::SetStatus(ResultStatus new_status, const char* details) {
|
|
|
|
impl->SetStatus(new_status, details);
|
2014-04-01 02:26:50 +00:00
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
const std::string& System::GetStatusDetails() const {
|
|
|
|
return impl->status_details;
|
|
|
|
}
|
2018-05-02 02:21:38 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
Loader::AppLoader& System::GetAppLoader() const {
|
|
|
|
return *impl->app_loader;
|
|
|
|
}
|
2018-02-21 16:37:48 +00:00
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
|
|
|
|
impl->debug_context = std::move(context);
|
|
|
|
}
|
2014-04-05 19:26:03 +00:00
|
|
|
|
2018-09-04 12:06:52 +00:00
|
|
|
Tegra::DebugContext* System::GetGPUDebugContext() const {
|
|
|
|
return impl->debug_context.get();
|
2018-08-30 14:50:54 +00:00
|
|
|
}
|
|
|
|
|
2018-12-23 02:32:05 +00:00
|
|
|
void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
|
2019-03-05 15:09:27 +00:00
|
|
|
const std::string& build_id, VAddr code_region_start,
|
|
|
|
VAddr code_region_end) {
|
2019-03-22 10:08:11 +00:00
|
|
|
impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
|
|
|
|
code_region_start, code_region_end);
|
2018-12-23 02:32:05 +00:00
|
|
|
}
|
|
|
|
|
2018-08-31 16:21:34 +00:00
|
|
|
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
|
2018-08-30 14:50:54 +00:00
|
|
|
impl->virtual_filesystem = std::move(vfs);
|
|
|
|
}
|
|
|
|
|
2018-08-31 16:21:34 +00:00
|
|
|
std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
|
2018-08-30 14:50:54 +00:00
|
|
|
return impl->virtual_filesystem;
|
|
|
|
}
|
|
|
|
|
2019-03-11 23:33:49 +00:00
|
|
|
void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
|
|
|
|
impl->applet_manager.SetAppletFrontendSet(std::move(set));
|
2018-11-23 02:00:04 +00:00
|
|
|
}
|
|
|
|
|
2019-03-11 23:33:49 +00:00
|
|
|
void System::SetDefaultAppletFrontendSet() {
|
|
|
|
impl->applet_manager.SetDefaultAppletFrontendSet();
|
2018-11-23 02:00:04 +00:00
|
|
|
}
|
|
|
|
|
2019-03-11 23:33:49 +00:00
|
|
|
Service::AM::Applets::AppletManager& System::GetAppletManager() {
|
|
|
|
return impl->applet_manager;
|
2018-11-11 21:39:25 +00:00
|
|
|
}
|
|
|
|
|
2019-03-11 23:33:49 +00:00
|
|
|
const Service::AM::Applets::AppletManager& System::GetAppletManager() const {
|
|
|
|
return impl->applet_manager;
|
2018-11-11 21:39:25 +00:00
|
|
|
}
|
|
|
|
|
2018-12-28 05:03:01 +00:00
|
|
|
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
|
|
|
|
impl->content_provider = std::move(provider);
|
|
|
|
}
|
|
|
|
|
|
|
|
FileSys::ContentProvider& System::GetContentProvider() {
|
|
|
|
return *impl->content_provider;
|
|
|
|
}
|
|
|
|
|
|
|
|
const FileSys::ContentProvider& System::GetContentProvider() const {
|
|
|
|
return *impl->content_provider;
|
|
|
|
}
|
|
|
|
|
|
|
|
void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
|
|
|
|
FileSys::ContentProvider* provider) {
|
|
|
|
impl->content_provider->SetSlot(slot, provider);
|
|
|
|
}
|
|
|
|
|
|
|
|
void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
|
|
|
|
impl->content_provider->ClearSlot(slot);
|
|
|
|
}
|
|
|
|
|
2019-05-18 01:45:56 +00:00
|
|
|
const Reporter& System::GetReporter() const {
|
|
|
|
return impl->reporter;
|
|
|
|
}
|
|
|
|
|
2019-06-24 23:27:35 +00:00
|
|
|
Service::Glue::ARPManager& System::GetARPManager() {
|
|
|
|
return impl->arp_manager;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Service::Glue::ARPManager& System::GetARPManager() const {
|
|
|
|
return impl->arp_manager;
|
|
|
|
}
|
|
|
|
|
2019-06-29 02:46:31 +00:00
|
|
|
Service::APM::Controller& System::GetAPMController() {
|
|
|
|
return impl->apm_controller;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Service::APM::Controller& System::GetAPMController() const {
|
|
|
|
return impl->apm_controller;
|
|
|
|
}
|
|
|
|
|
2018-08-30 14:50:54 +00:00
|
|
|
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
2018-11-22 06:27:23 +00:00
|
|
|
return impl->Init(*this, emu_window);
|
2018-08-30 14:50:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void System::Shutdown() {
|
|
|
|
impl->Shutdown();
|
2013-09-05 22:33:46 +00:00
|
|
|
}
|
|
|
|
|
2018-04-20 23:29:04 +00:00
|
|
|
Service::SM::ServiceManager& System::ServiceManager() {
|
2018-08-30 14:50:54 +00:00
|
|
|
return *impl->service_manager;
|
2018-04-20 23:29:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const Service::SM::ServiceManager& System::ServiceManager() const {
|
2018-08-30 14:50:54 +00:00
|
|
|
return *impl->service_manager;
|
2018-04-20 23:29:04 +00:00
|
|
|
}
|
|
|
|
|
2017-09-26 23:17:47 +00:00
|
|
|
} // namespace Core
|