@ -27,19 +27,48 @@ 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
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 ( ) ) ;
@ -51,7 +80,7 @@ Cpu& System::CurrentCpuCore() {
return * cpu_cores [ active_core ] ;
}
System : : ResultStatus System : : RunLoop( bool tight_loop ) {
ResultStatus RunLoop( bool tight_loop ) {
status = ResultStatus : : Success ;
// Update thread_to_cpu in case Core 0 is run from a different host thread
@ -86,110 +115,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
return status ;
}
System : : ResultStatus System : : SingleStep ( ) {
return RunLoop ( false ) ;
}
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 ( concat . empty ( ) )
return nullptr ;
return FileSys : : ConcatenateFiles ( concat , dir - > GetName ( ) ) ;
}
return vfs - > OpenFile ( path , FileSys : : Mode : : Read ) ;
}
System : : ResultStatus System : : Load ( Frontend : : EmuWindow & emu_window , const std : : string & filepath ) {
app_loader = Loader : : GetLoader ( GetGameFileFromPath ( virtual_filesystem , filepath ) ) ;
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 ( ) ;
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 ;
}
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 ;
}
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 ( ) ;
return static_cast < ResultStatus > ( static_cast < u32 > ( ResultStatus : : ErrorLoader ) +
static_cast < u32 > ( load_result ) ) ;
}
status = ResultStatus : : Success ;
return status ;
}
void System : : PrepareReschedule ( ) {
CurrentCpuCore ( ) . PrepareReschedule ( ) ;
}
PerfStats : : Results System : : GetAndResetPerfStats ( ) {
return perf_stats . GetAndResetStats ( CoreTiming : : GetGlobalTimeUs ( ) ) ;
}
const std : : shared_ptr < Kernel : : Scheduler > & System : : Scheduler ( size_t core_index ) {
ASSERT ( core_index < NUM_CPU_CORES ) ;
return cpu_cores [ core_index ] - > Scheduler ( ) ;
}
Kernel : : KernelCore & System : : Kernel ( ) {
return kernel ;
}
const Kernel : : KernelCore & System : : Kernel ( ) const {
return kernel ;
}
ARM_Interface & System : : ArmInterface ( size_t core_index ) {
ASSERT ( core_index < NUM_CPU_CORES ) ;
return cpu_cores [ core_index ] - > ArmInterface ( ) ;
}
Cpu & System : : CpuCore ( size_t core_index ) {
ASSERT ( core_index < NUM_CPU_CORES ) ;
return * cpu_cores [ core_index ] ;
}
System : : ResultStatus System : : Init ( Frontend : : EmuWindow & emu_window ) {
ResultStatus Init ( Frontend : : EmuWindow & emu_window ) {
LOG_DEBUG ( HW_Memory , " initialized OK " ) ;
CoreTiming : : Init ( ) ;
@ -240,7 +166,44 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return ResultStatus : : Success ;
}
void System : : Shutdown ( ) {
ResultStatus Load ( Frontend : : EmuWindow & emu_window , const std : : string & filepath ) {
app_loader = Loader : : GetLoader ( GetGameFileFromPath ( virtual_filesystem , filepath ) ) ;
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 ( ) ;
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 ;
}
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 ;
}
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 ;
}
void Shutdown ( ) {
// Log last frame performance stats
auto perf_results = GetAndResetPerfStats ( ) ;
Telemetry ( ) . AddField ( Telemetry : : FieldType : : Performance , " Shutdown_EmulationSpeed " ,
@ -282,12 +245,216 @@ void System::Shutdown() {
LOG_DEBUG ( Core , " Shutdown OK " ) ;
}
Loader : : ResultStatus GetGameName ( std : : string & out ) const {
if ( app_loader = = nullptr )
return Loader : : ResultStatus : : ErrorNotInitialized ;
return app_loader - > ReadTitle ( out ) ;
}
void SetStatus ( ResultStatus new_status , const char * details = nullptr ) {
status = new_status ;
if ( details ) {
status_details = details ;
}
}
PerfStats : : Results GetAndResetPerfStats ( ) {
return perf_stats . GetAndResetStats ( CoreTiming : : GetGlobalTimeUs ( ) ) ;
}
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 ) ;
}
void System : : InvalidateCpuInstructionCaches ( ) {
for ( auto & cpu : impl - > cpu_cores ) {
cpu - > ArmInterface ( ) . ClearInstructionCache ( ) ;
}
}
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 ( ) {
CurrentCpuCore ( ) . PrepareReschedule ( ) ;
}
PerfStats : : Results System : : GetAndResetPerfStats ( ) {
return impl - > GetAndResetPerfStats ( ) ;
}
Core : : TelemetrySession & System : : TelemetrySession ( ) const {
return * impl - > telemetry_session ;
}
ARM_Interface & System : : CurrentArmInterface ( ) {
return CurrentCpuCore ( ) . ArmInterface ( ) ;
}
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 impl - > cpu_cores [ core_index ] - > ArmInterface ( ) ;
}
Cpu & System : : CpuCore ( size_t core_index ) {
ASSERT ( core_index < NUM_CPU_CORES ) ;
return * impl - > cpu_cores [ core_index ] ;
}
ExclusiveMonitor & System : : Monitor ( ) {
return * impl - > cpu_exclusive_monitor ;
}
Tegra : : GPU & System : : GPU ( ) {
return * impl - > gpu_core ;
}
const Tegra : : GPU & System : : GPU ( ) const {
return * impl - > gpu_core ;
}
VideoCore : : RendererBase & System : : Renderer ( ) {
return * impl - > renderer ;
}
const VideoCore : : RendererBase & System : : Renderer ( ) const {
return * impl - > renderer ;
}
Kernel : : KernelCore & System : : Kernel ( ) {
return impl - > kernel ;
}
const Kernel : : KernelCore & System : : Kernel ( ) const {
return impl - > kernel ;
}
Core : : PerfStats & System : : GetPerfStats ( ) {
return impl - > perf_stats ;
}
const Core : : PerfStats & System : : GetPerfStats ( ) const {
return impl - > perf_stats ;
}
Core : : FrameLimiter & System : : FrameLimiter ( ) {
return impl - > frame_limiter ;
}
const Core : : FrameLimiter & System : : FrameLimiter ( ) const {
return impl - > frame_limiter ;
}
Loader : : ResultStatus System : : GetGameName ( std : : string & out ) const {
return impl - > GetGameName ( out ) ;
}
void System : : SetStatus ( ResultStatus new_status , const char * details ) {
impl - > SetStatus ( new_status , details ) ;
}
const std : : string & System : : GetStatusDetails ( ) const {
return impl - > status_details ;
}
Loader : : AppLoader & System : : GetAppLoader ( ) const {
return * impl - > app_loader ;
}
void System : : SetGPUDebugContext ( std : : shared_ptr < Tegra : : DebugContext > context ) {
impl - > debug_context = std : : move ( context ) ;
}
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