emulator.cc savestates
Change UserModule::Launch to be non-blocking (returns the main thread)
This commit is contained in:
parent
52ec24ea6a
commit
aa7919bd89
|
@ -13,10 +13,12 @@
|
||||||
|
|
||||||
#include "xenia/apu/audio_system.h"
|
#include "xenia/apu/audio_system.h"
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
|
#include "xenia/base/byte_stream.h"
|
||||||
#include "xenia/base/clock.h"
|
#include "xenia/base/clock.h"
|
||||||
#include "xenia/base/debugging.h"
|
#include "xenia/base/debugging.h"
|
||||||
#include "xenia/base/exception_handler.h"
|
#include "xenia/base/exception_handler.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
#include "xenia/base/mapped_memory.h"
|
||||||
#include "xenia/base/profiling.h"
|
#include "xenia/base/profiling.h"
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
#include "xenia/cpu/backend/code_cache.h"
|
#include "xenia/cpu/backend/code_cache.h"
|
||||||
|
@ -24,6 +26,7 @@
|
||||||
#include "xenia/hid/input_driver.h"
|
#include "xenia/hid/input_driver.h"
|
||||||
#include "xenia/hid/input_system.h"
|
#include "xenia/hid/input_system.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
|
#include "xenia/kernel/user_module.h"
|
||||||
#include "xenia/kernel/xam/xam_module.h"
|
#include "xenia/kernel/xam/xam_module.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_module.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
@ -288,6 +291,112 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path) {
|
||||||
return CompleteLaunch(path, "game:\\default.xex");
|
return CompleteLaunch(path, "game:\\default.xex");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Emulator::Pause() {
|
||||||
|
auto lock = global_critical_region::AcquireDirect();
|
||||||
|
if (paused_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
paused_ = true;
|
||||||
|
|
||||||
|
auto threads =
|
||||||
|
kernel_state()->object_table()->GetObjectsByType<kernel::XThread>(
|
||||||
|
kernel::XObject::kTypeThread);
|
||||||
|
for (auto thread : threads) {
|
||||||
|
if (!thread->can_debugger_suspend()) {
|
||||||
|
// Don't pause host threads.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread->thread()->Suspend(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
XELOGD("! EMULATOR PAUSED !");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Emulator::Resume() {
|
||||||
|
if (!paused_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
paused_ = false;
|
||||||
|
XELOGD("! EMULATOR RESUMED !");
|
||||||
|
|
||||||
|
auto threads =
|
||||||
|
kernel_state()->object_table()->GetObjectsByType<kernel::XThread>(
|
||||||
|
kernel::XObject::kTypeThread);
|
||||||
|
for (auto thread : threads) {
|
||||||
|
if (!thread->can_debugger_suspend()) {
|
||||||
|
// Don't pause host threads.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread->thread()->Resume(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Emulator::SaveToFile(const std::wstring& path) {
|
||||||
|
Pause();
|
||||||
|
|
||||||
|
filesystem::CreateFile(path);
|
||||||
|
auto map = MappedMemory::Open(path, MappedMemory::Mode::kReadWrite, 0,
|
||||||
|
1024ull * 1024ull * 1024ull * 4ull);
|
||||||
|
if (!map) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the emulator state to a file
|
||||||
|
ByteStream stream(map->data(), map->size());
|
||||||
|
stream.Write('XSAV');
|
||||||
|
|
||||||
|
kernel_state_->Save(&stream);
|
||||||
|
memory_->Save(&stream);
|
||||||
|
map->Close(stream.offset());
|
||||||
|
|
||||||
|
Resume();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Emulator::RestoreFromFile(const std::wstring& path) {
|
||||||
|
// Restore the emulator state from a file
|
||||||
|
auto map = MappedMemory::Open(path, MappedMemory::Mode::kReadWrite);
|
||||||
|
if (!map) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
restoring_ = true;
|
||||||
|
|
||||||
|
// Terminate any loaded titles.
|
||||||
|
Pause();
|
||||||
|
kernel_state_->TerminateTitle();
|
||||||
|
|
||||||
|
ByteStream stream(map->data(), map->size());
|
||||||
|
if (stream.Read<uint32_t>() != 'XSAV') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!kernel_state_->Restore(&stream)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!memory_->Restore(&stream)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the main thread.
|
||||||
|
auto threads = kernel_state_->object_table()->GetObjectsByType<kernel::XThread>();
|
||||||
|
for (auto thread : threads) {
|
||||||
|
if (thread->main_thread()) {
|
||||||
|
main_thread_ = thread->thread();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Resume();
|
||||||
|
|
||||||
|
restore_fence_.Signal();
|
||||||
|
restoring_ = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Emulator::ExceptionCallbackThunk(Exception* ex, void* data) {
|
bool Emulator::ExceptionCallbackThunk(Exception* ex, void* data) {
|
||||||
return reinterpret_cast<Emulator*>(data)->ExceptionCallback(ex);
|
return reinterpret_cast<Emulator*>(data)->ExceptionCallback(ex);
|
||||||
}
|
}
|
||||||
|
@ -348,19 +457,42 @@ bool Emulator::ExceptionCallback(Exception* ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Emulator::WaitUntilExit() {
|
||||||
|
while (true) {
|
||||||
|
xe::threading::Wait(main_thread_, false);
|
||||||
|
|
||||||
|
if (restoring_) {
|
||||||
|
restore_fence_.Wait();
|
||||||
|
} else {
|
||||||
|
// Not restoring and the thread exited. We're finished.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
|
X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
|
||||||
const std::string& module_path) {
|
const std::string& module_path) {
|
||||||
// Allow xam to request module loads.
|
// Allow xam to request module loads.
|
||||||
auto xam = kernel_state()->GetKernelModule<kernel::xam::XamModule>("xam.xex");
|
auto xam = kernel_state()->GetKernelModule<kernel::xam::XamModule>("xam.xex");
|
||||||
auto xboxkrnl =
|
|
||||||
kernel_state()->GetKernelModule<kernel::xboxkrnl::XboxkrnlModule>(
|
|
||||||
"xboxkrnl.exe");
|
|
||||||
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
auto next_module = module_path;
|
auto next_module = module_path;
|
||||||
while (next_module != "") {
|
while (next_module != "") {
|
||||||
XELOGI("Launching module %s", next_module.c_str());
|
XELOGI("Launching module %s", next_module.c_str());
|
||||||
result = xboxkrnl->LaunchModule(next_module.c_str());
|
auto module = kernel_state_->LoadUserModule(next_module.c_str());
|
||||||
|
if (!module) {
|
||||||
|
XELOGE("Failed to load user module %s", path);
|
||||||
|
return X_STATUS_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel_state_->SetExecutableModule(module);
|
||||||
|
auto main_xthread = module->Launch();
|
||||||
|
if (!main_xthread) {
|
||||||
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
main_thread_ = main_xthread->thread();
|
||||||
|
WaitUntilExit();
|
||||||
|
|
||||||
// Check xam and see if they want us to load another module.
|
// Check xam and see if they want us to load another module.
|
||||||
auto& loader_data = xam->loader_data();
|
auto& loader_data = xam->loader_data();
|
||||||
|
|
|
@ -118,6 +118,14 @@ class Emulator {
|
||||||
// Launches a game from an STFS container file.
|
// Launches a game from an STFS container file.
|
||||||
X_STATUS LaunchStfsContainer(std::wstring path);
|
X_STATUS LaunchStfsContainer(std::wstring path);
|
||||||
|
|
||||||
|
void Pause();
|
||||||
|
void Resume();
|
||||||
|
|
||||||
|
bool SaveToFile(const std::wstring& path);
|
||||||
|
bool RestoreFromFile(const std::wstring& path);
|
||||||
|
|
||||||
|
void WaitUntilExit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
static bool ExceptionCallbackThunk(Exception* ex, void* data);
|
||||||
bool ExceptionCallback(Exception* ex);
|
bool ExceptionCallback(Exception* ex);
|
||||||
|
@ -142,6 +150,11 @@ class Emulator {
|
||||||
std::unique_ptr<vfs::VirtualFileSystem> file_system_;
|
std::unique_ptr<vfs::VirtualFileSystem> file_system_;
|
||||||
|
|
||||||
std::unique_ptr<kernel::KernelState> kernel_state_;
|
std::unique_ptr<kernel::KernelState> kernel_state_;
|
||||||
|
threading::Thread* main_thread_ = nullptr;
|
||||||
|
|
||||||
|
bool paused_ = false;
|
||||||
|
bool restoring_ = false;
|
||||||
|
threading::Fence restore_fence_; // Fired on restore finish.
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "xenia/base/byte_stream.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/cpu/elf_module.h"
|
#include "xenia/cpu/elf_module.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
|
@ -101,7 +102,8 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the xex2 header into guest memory.
|
// Copy the xex2 header into guest memory.
|
||||||
const xex2_header* header = this->xex_module()->xex_header();
|
auto header = this->xex_module()->xex_header();
|
||||||
|
auto security_header = this->xex_module()->xex_security_info();
|
||||||
guest_xex_header_ = memory()->SystemHeapAlloc(header->header_size);
|
guest_xex_header_ = memory()->SystemHeapAlloc(header->header_size);
|
||||||
|
|
||||||
uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_);
|
uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_);
|
||||||
|
@ -113,6 +115,15 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||||
|
|
||||||
ldr_data->dll_base = 0; // GetProcAddress will read this.
|
ldr_data->dll_base = 0; // GetProcAddress will read this.
|
||||||
ldr_data->xex_header_base = guest_xex_header_;
|
ldr_data->xex_header_base = guest_xex_header_;
|
||||||
|
ldr_data->full_image_size = security_header->image_size;
|
||||||
|
this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT,
|
||||||
|
&ldr_data->entry_point);
|
||||||
|
|
||||||
|
xe::be<uint32_t>* image_base_ptr = nullptr;
|
||||||
|
if (this->xex_module()->GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS,
|
||||||
|
&image_base_ptr)) {
|
||||||
|
ldr_data->image_base = *image_base_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Cache some commonly used headers...
|
// Cache some commonly used headers...
|
||||||
this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_);
|
this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_);
|
||||||
|
@ -171,7 +182,7 @@ X_STATUS UserModule::GetSection(const char* name, uint32_t* out_section_data,
|
||||||
if (!cpu::XexModule::GetOptHeader(xex_header(), XEX_HEADER_RESOURCE_INFO,
|
if (!cpu::XexModule::GetOptHeader(xex_header(), XEX_HEADER_RESOURCE_INFO,
|
||||||
&resource_header)) {
|
&resource_header)) {
|
||||||
// No resources.
|
// No resources.
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t count = (resource_header->size - 4) / 16;
|
uint32_t count = (resource_header->size - 4) / 16;
|
||||||
|
@ -186,7 +197,7 @@ X_STATUS UserModule::GetSection(const char* name, uint32_t* out_section_data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) {
|
X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) {
|
||||||
|
@ -259,14 +270,14 @@ X_STATUS UserModule::GetOptHeader(uint8_t* membase, const xex2_header* header,
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS UserModule::Launch(uint32_t flags) {
|
object_ref<XThread> UserModule::Launch(uint32_t flags) {
|
||||||
XELOGI("Launching module...");
|
XELOGI("Launching module...");
|
||||||
|
|
||||||
// Create a thread to run in.
|
// Create a thread to run in.
|
||||||
// We start suspended so we can run the debugger prep.
|
// We start suspended so we can run the debugger prep.
|
||||||
auto thread = object_ref<XThread>(new XThread(kernel_state(), stack_size_, 0,
|
auto thread = object_ref<XThread>(
|
||||||
entry_point_, 0,
|
new XThread(kernel_state(), stack_size_, 0, entry_point_, 0,
|
||||||
X_CREATE_SUSPENDED, true));
|
X_CREATE_SUSPENDED, true, true));
|
||||||
|
|
||||||
// We know this is the 'main thread'.
|
// We know this is the 'main thread'.
|
||||||
char thread_name[32];
|
char thread_name[32];
|
||||||
|
@ -277,7 +288,7 @@ X_STATUS UserModule::Launch(uint32_t flags) {
|
||||||
X_STATUS result = thread->Create();
|
X_STATUS result = thread->Create();
|
||||||
if (XFAILED(result)) {
|
if (XFAILED(result)) {
|
||||||
XELOGE("Could not create launch thread: %.8X", result);
|
XELOGE("Could not create launch thread: %.8X", result);
|
||||||
return result;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waits for a debugger client, if desired.
|
// Waits for a debugger client, if desired.
|
||||||
|
@ -290,8 +301,8 @@ X_STATUS UserModule::Launch(uint32_t flags) {
|
||||||
// suspend count without resuming it until the debugger wants.
|
// suspend count without resuming it until the debugger wants.
|
||||||
thread->Resume();
|
thread->Resume();
|
||||||
|
|
||||||
// Wait until thread completes.
|
return thread;
|
||||||
thread->Wait(0, 0, 0, nullptr);
|
}
|
||||||
|
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ namespace cpu {
|
||||||
class XexModule;
|
class XexModule;
|
||||||
class ElfModule;
|
class ElfModule;
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
|
namespace kernel {
|
||||||
|
class XThread;
|
||||||
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -80,7 +83,7 @@ class UserModule : public XModule {
|
||||||
xe_xex2_header_keys key,
|
xe_xex2_header_keys key,
|
||||||
uint32_t* out_header_guest_ptr);
|
uint32_t* out_header_guest_ptr);
|
||||||
|
|
||||||
X_STATUS Launch(uint32_t flags);
|
object_ref<XThread> Launch(uint32_t flags = 0);
|
||||||
|
|
||||||
void Dump();
|
void Dump();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue