Added Signals; more runtime fixes

This commit is contained in:
Hamish Milne 2020-01-12 00:24:44 +00:00 committed by zhupengfei
parent e4f05884c3
commit 8abc5525be
17 changed files with 118 additions and 56 deletions

View file

@ -1504,19 +1504,12 @@ void GMainWindow::OnCheats() {
} }
void GMainWindow::OnSave() { void GMainWindow::OnSave() {
Core::System& system{Core::System::GetInstance()}; Core::System::GetInstance().SendSignal(Core::System::Signal::Save);
auto fs = std::ofstream("save0.citrasave");
emu_thread->SetRunning(false);
Core::System::GetInstance().Save(fs);
emu_thread->SetRunning(true);
} }
void GMainWindow::OnLoad() { void GMainWindow::OnLoad() {
if (QFileInfo("save0.citrasave").exists()) { if (QFileInfo("save0.citrasave").exists()) {
auto fs = std::ifstream("save0.citrasave"); Core::System::GetInstance().SendSignal(Core::System::Signal::Load);
emu_thread->SetRunning(false);
Core::System::GetInstance().Load(fs);
emu_thread->SetRunning(true);
} }
} }

View file

@ -160,15 +160,33 @@ private:
// The priority level queues of thread ids. // The priority level queues of thread ids.
std::array<Queue, NUM_QUEUES> queues; std::array<Queue, NUM_QUEUES> queues;
s32 ToIndex(Queue* q) const {
if (q == nullptr) {
return -2;
} else if (q == UnlinkedTag()) {
return -1;
} else {
return static_cast<s32>(q - &queues[0]);
}
}
Queue* ToPointer(s32 idx) {
if (idx == -1) {
return UnlinkedTag();
} else if (idx < 0) {
return nullptr;
} else {
return &queues[idx];
}
}
friend class boost::serialization::access; friend class boost::serialization::access;
template <class Archive> template <class Archive>
void save(Archive& ar, const unsigned int file_version) const { void save(Archive& ar, const unsigned int file_version) const {
s32 idx = first == UnlinkedTag() ? -1 : static_cast<s32>(first - &queues[0]); s32 idx = ToIndex(first);
ar << idx; ar << idx;
for (auto i = 0; i < NUM_QUEUES; i++) { for (auto i = 0; i < NUM_QUEUES; i++) {
s32 idx1 = first == UnlinkedTag() s32 idx1 = ToIndex(queues[i].next_nonempty);
? -1
: static_cast<s32>(queues[i].next_nonempty - &queues[0]);
ar << idx1; ar << idx1;
ar << queues[i].data; ar << queues[i].data;
} }
@ -178,10 +196,10 @@ private:
void load(Archive& ar, const unsigned int file_version) { void load(Archive& ar, const unsigned int file_version) {
s32 idx; s32 idx;
ar >> idx; ar >> idx;
first = idx < 0 ? UnlinkedTag() : &queues[idx]; first = ToPointer(idx);
for (auto i = 0; i < NUM_QUEUES; i++) { for (auto i = 0; i < NUM_QUEUES; i++) {
ar >> idx; ar >> idx;
queues[i].next_nonempty = idx < 0 ? UnlinkedTag() : &queues[idx]; queues[i].next_nonempty = ToPointer(idx);
ar >> queues[i].data; ar >> queues[i].data;
} }
} }

View file

@ -264,7 +264,7 @@ private:
ar >> r; ar >> r;
SetCP15Register(static_cast<CP15Register>(i), r); SetCP15Register(static_cast<CP15Register>(i), r);
} }
// TODO: Clear caches etc? ClearInstructionCache();
} }
BOOST_SERIALIZATION_SPLIT_MEMBER() BOOST_SERIALIZATION_SPLIT_MEMBER()

View file

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <fstream>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <boost/serialization/array.hpp> #include <boost/serialization/array.hpp>
@ -103,15 +104,38 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
HW::Update(); HW::Update();
Reschedule(); Reschedule();
if (reset_requested.exchange(false)) { auto signal = current_signal.exchange(Signal::None);
switch (signal) {
case Signal::Reset:
Reset(); Reset();
} else if (shutdown_requested.exchange(false)) { break;
case Signal::Shutdown:
return ResultStatus::ShutdownRequested; return ResultStatus::ShutdownRequested;
break;
case Signal::Load: {
auto stream = std::ifstream("save0.citrasave", std::fstream::binary);
System::Load(stream);
} break;
case Signal::Save: {
auto stream = std::ofstream("save0.citrasave", std::fstream::binary);
System::Save(stream);
} break;
default:
break;
} }
return status; return status;
} }
bool System::SendSignal(System::Signal signal) {
auto prev = System::Signal::None;
if (!current_signal.compare_exchange_strong(prev, signal)) {
LOG_ERROR(Core, "Unable to {} as {} is ongoing", signal, prev);
return false;
}
return true;
}
System::ResultStatus System::SingleStep() { System::ResultStatus System::SingleStep() {
return RunLoop(false); return RunLoop(false);
} }
@ -216,8 +240,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
timing = std::make_unique<Timing>(); timing = std::make_unique<Timing>();
kernel = std::make_unique<Kernel::KernelSystem>( kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing,
*memory, *timing, [this] { PrepareReschedule(); }, system_mode); [this] { PrepareReschedule(); }, system_mode);
if (Settings::values.use_cpu_jit) { if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64 #ifdef ARCHITECTURE_x86_64
@ -409,6 +433,7 @@ void System::Reset() {
template <class Archive> template <class Archive>
void System::serialize(Archive& ar, const unsigned int file_version) { void System::serialize(Archive& ar, const unsigned int file_version) {
Memory::RasterizerFlushAndInvalidateRegion(0, 0xFFFFFFFF);
ar&* cpu_core.get(); ar&* cpu_core.get();
ar&* service_manager.get(); ar&* service_manager.get();
ar& GPU::g_regs; ar& GPU::g_regs;
@ -436,11 +461,20 @@ void System::Save(std::ostream& stream) const {
} }
void System::Load(std::istream& stream) { void System::Load(std::istream& stream) {
{ try {
iarchive ia{stream};
ia&* this; {
iarchive ia{stream};
ia&* this;
}
VideoCore::Load(stream);
// Flush state through:
Kernel().SetCurrentProcess(Kernel().GetCurrentProcess());
} catch (const std::exception& e) {
LOG_ERROR(Core, "Error loading: {}", e.what());
} }
VideoCore::Load(stream);
} }
} // namespace Core } // namespace Core

View file

@ -116,14 +116,18 @@ public:
/// Shutdown and then load again /// Shutdown and then load again
void Reset(); void Reset();
enum class Signal : u32 { None, Shutdown, Reset, Save, Load };
bool SendSignal(Signal signal);
/// Request reset of the system /// Request reset of the system
void RequestReset() { void RequestReset() {
reset_requested = true; SendSignal(Signal::Reset);
} }
/// Request shutdown of the system /// Request shutdown of the system
void RequestShutdown() { void RequestShutdown() {
shutdown_requested = true; SendSignal(Signal::Shutdown);
} }
/** /**
@ -341,8 +345,7 @@ private:
Frontend::EmuWindow* m_emu_window; Frontend::EmuWindow* m_emu_window;
std::string m_filepath; std::string m_filepath;
std::atomic<bool> reset_requested; std::atomic<Signal> current_signal;
std::atomic<bool> shutdown_requested;
friend class boost::serialization::access; friend class boost::serialization::access;
template <typename Archive> template <typename Archive>

View file

@ -23,14 +23,10 @@ bool Timing::Event::operator<(const Event& right) const {
TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) { TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback callback) {
// check for existing type with same name. // check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization. // we want event type names to remain unique so that we can use them for serialization.
ASSERT_MSG(event_types.find(name) == event_types.end(), auto info = event_types.emplace(name, TimingEventType{});
"CoreTiming Event \"{}\" is already registered. Events should only be registered "
"during Init to avoid breaking save states.",
name);
auto info = event_types.emplace(name, TimingEventType{callback, nullptr});
TimingEventType* event_type = &info.first->second; TimingEventType* event_type = &info.first->second;
event_type->name = &info.first->first; event_type->name = &info.first->first;
event_type->callback = callback;
return event_type; return event_type;
} }
@ -129,6 +125,10 @@ void Timing::Advance() {
Event evt = std::move(event_queue.front()); Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back(); event_queue.pop_back();
if (event_types.find(*evt.type->name) == event_types.end()) {
LOG_ERROR(Core, "Unknown queued event");
continue;
}
evt.type->callback(evt.userdata, global_timer - evt.time); evt.type->callback(evt.userdata, global_timer - evt.time);
} }

View file

@ -14,6 +14,7 @@
// FileSys namespace // FileSys namespace
SERIALIZE_EXPORT_IMPL(FileSys::IVFCFile) SERIALIZE_EXPORT_IMPL(FileSys::IVFCFile)
SERIALIZE_EXPORT_IMPL(FileSys::IVFCFileInMemory)
SERIALIZE_EXPORT_IMPL(FileSys::IVFCDelayGenerator) SERIALIZE_EXPORT_IMPL(FileSys::IVFCDelayGenerator)
SERIALIZE_EXPORT_IMPL(FileSys::RomFSDelayGenerator) SERIALIZE_EXPORT_IMPL(FileSys::RomFSDelayGenerator)
SERIALIZE_EXPORT_IMPL(FileSys::ExeFSDelayGenerator) SERIALIZE_EXPORT_IMPL(FileSys::ExeFSDelayGenerator)

View file

@ -176,11 +176,23 @@ private:
std::vector<u8> romfs_file; std::vector<u8> romfs_file;
u64 data_offset; u64 data_offset;
u64 data_size; u64 data_size;
IVFCFileInMemory() = default;
template <class Archive>
void serialize(Archive& ar, const unsigned int) {
ar& boost::serialization::base_object<FileBackend>(*this);
ar& romfs_file;
ar& data_offset;
ar& data_size;
}
friend class boost::serialization::access;
}; };
} // namespace FileSys } // namespace FileSys
BOOST_CLASS_EXPORT_KEY(FileSys::IVFCFile) BOOST_CLASS_EXPORT_KEY(FileSys::IVFCFile)
BOOST_CLASS_EXPORT_KEY(FileSys::IVFCFileInMemory)
BOOST_CLASS_EXPORT_KEY(FileSys::IVFCDelayGenerator) BOOST_CLASS_EXPORT_KEY(FileSys::IVFCDelayGenerator)
BOOST_CLASS_EXPORT_KEY(FileSys::RomFSDelayGenerator) BOOST_CLASS_EXPORT_KEY(FileSys::RomFSDelayGenerator)
BOOST_CLASS_EXPORT_KEY(FileSys::ExeFSDelayGenerator) BOOST_CLASS_EXPORT_KEY(FileSys::ExeFSDelayGenerator)

View file

@ -23,6 +23,9 @@ KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
std::function<void()> prepare_reschedule_callback, u32 system_mode) std::function<void()> prepare_reschedule_callback, u32 system_mode)
: memory(memory), timing(timing), : memory(memory), timing(timing),
prepare_reschedule_callback(std::move(prepare_reschedule_callback)) { prepare_reschedule_callback(std::move(prepare_reschedule_callback)) {
for (auto i = 0; i < memory_regions.size(); i++) {
memory_regions[i] = std::make_shared<MemoryRegionInfo>();
}
MemoryInit(system_mode); MemoryInit(system_mode);
resource_limits = std::make_unique<ResourceLimitList>(*this); resource_limits = std::make_unique<ResourceLimitList>(*this);
@ -107,7 +110,7 @@ template <class Archive>
void KernelSystem::serialize(Archive& ar, const unsigned int file_version) { void KernelSystem::serialize(Archive& ar, const unsigned int file_version) {
ar& memory_regions; ar& memory_regions;
ar& named_ports; ar& named_ports;
ar&* current_cpu.get(); // current_cpu set externally
// NB: subsystem references and prepare_reschedule_callback are constant // NB: subsystem references and prepare_reschedule_callback are constant
ar&* resource_limits.get(); ar&* resource_limits.get();
ar& next_object_id; ar& next_object_id;

View file

@ -134,7 +134,8 @@ public:
*/ */
ResultVal<std::shared_ptr<Thread>> CreateThread(std::string name, VAddr entry_point, ResultVal<std::shared_ptr<Thread>> CreateThread(std::string name, VAddr entry_point,
u32 priority, u32 arg, s32 processor_id, u32 priority, u32 arg, s32 processor_id,
VAddr stack_top, std::shared_ptr<Process> owner_process); VAddr stack_top,
std::shared_ptr<Process> owner_process);
/** /**
* Creates a semaphore. * Creates a semaphore.
@ -232,11 +233,11 @@ public:
IPCDebugger::Recorder& GetIPCRecorder(); IPCDebugger::Recorder& GetIPCRecorder();
const IPCDebugger::Recorder& GetIPCRecorder() const; const IPCDebugger::Recorder& GetIPCRecorder() const;
MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); std::shared_ptr<MemoryRegionInfo> GetMemoryRegion(MemoryRegion region);
void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping);
std::array<MemoryRegionInfo, 3> memory_regions; std::array<std::shared_ptr<MemoryRegionInfo>, 3> memory_regions{};
/// Adds a port to the named port table /// Adds a port to the named port table
void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port); void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port);

View file

@ -49,9 +49,9 @@ void KernelSystem::MemoryInit(u32 mem_type) {
// the sizes specified in the memory_region_sizes table. // the sizes specified in the memory_region_sizes table.
VAddr base = 0; VAddr base = 0;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
memory_regions[i].Reset(base, memory_region_sizes[mem_type][i]); memory_regions[i]->Reset(base, memory_region_sizes[mem_type][i]);
base += memory_regions[i].size; base += memory_regions[i]->size;
} }
// We must've allocated the entire FCRAM by the end // We must've allocated the entire FCRAM by the end
@ -63,20 +63,20 @@ void KernelSystem::MemoryInit(u32 mem_type) {
// app_mem_malloc does not always match the configured size for memory_region[0]: in case the // app_mem_malloc does not always match the configured size for memory_region[0]: in case the
// n3DS type override is in effect it reports the size the game expects, not the real one. // n3DS type override is in effect it reports the size the game expects, not the real one.
config_mem.app_mem_alloc = memory_region_sizes[mem_type][0]; config_mem.app_mem_alloc = memory_region_sizes[mem_type][0];
config_mem.sys_mem_alloc = memory_regions[1].size; config_mem.sys_mem_alloc = memory_regions[1]->size;
config_mem.base_mem_alloc = memory_regions[2].size; config_mem.base_mem_alloc = memory_regions[2]->size;
shared_page_handler = std::make_shared<SharedPage::Handler>(timing); shared_page_handler = std::make_shared<SharedPage::Handler>(timing);
} }
MemoryRegionInfo* KernelSystem::GetMemoryRegion(MemoryRegion region) { std::shared_ptr<MemoryRegionInfo> KernelSystem::GetMemoryRegion(MemoryRegion region) {
switch (region) { switch (region) {
case MemoryRegion::APPLICATION: case MemoryRegion::APPLICATION:
return &memory_regions[0]; return memory_regions[0];
case MemoryRegion::SYSTEM: case MemoryRegion::SYSTEM:
return &memory_regions[1]; return memory_regions[1];
case MemoryRegion::BASE: case MemoryRegion::BASE:
return &memory_regions[2]; return memory_regions[2];
default: default:
UNREACHABLE(); UNREACHABLE();
} }

View file

@ -200,7 +200,7 @@ public:
u32 memory_used = 0; u32 memory_used = 0;
MemoryRegionInfo* memory_region = nullptr; std::shared_ptr<MemoryRegionInfo> memory_region = nullptr;
/// The Thread Local Storage area is allocated as processes create threads, /// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part

View file

@ -42,7 +42,7 @@ ResultVal<std::shared_ptr<SharedMemory>> KernelSystem::CreateSharedMemory(
if (address == 0) { if (address == 0) {
// We need to allocate a block from the Linear Heap ourselves. // We need to allocate a block from the Linear Heap ourselves.
// We'll manually allocate some memory from the linear heap in the specified region. // We'll manually allocate some memory from the linear heap in the specified region.
MemoryRegionInfo* memory_region = GetMemoryRegion(region); auto memory_region = GetMemoryRegion(region);
auto offset = memory_region->LinearAllocate(size); auto offset = memory_region->LinearAllocate(size);
ASSERT_MSG(offset, "Not enough space in region to allocate shared memory!"); ASSERT_MSG(offset, "Not enough space in region to allocate shared memory!");
@ -79,7 +79,7 @@ std::shared_ptr<SharedMemory> KernelSystem::CreateSharedMemoryForApplet(
auto shared_memory{std::make_shared<SharedMemory>(*this)}; auto shared_memory{std::make_shared<SharedMemory>(*this)};
// Allocate memory in heap // Allocate memory in heap
MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::SYSTEM); auto memory_region = GetMemoryRegion(MemoryRegion::SYSTEM);
auto backing_blocks = memory_region->HeapAllocate(size); auto backing_blocks = memory_region->HeapAllocate(size);
ASSERT_MSG(!backing_blocks.empty(), "Not enough space in region to allocate shared memory!"); ASSERT_MSG(!backing_blocks.empty(), "Not enough space in region to allocate shared memory!");
shared_memory->holding_memory = backing_blocks; shared_memory->holding_memory = backing_blocks;

View file

@ -32,7 +32,7 @@ namespace Kernel {
template <class Archive> template <class Archive>
void Thread::serialize(Archive& ar, const unsigned int file_version) { void Thread::serialize(Archive& ar, const unsigned int file_version) {
ar& boost::serialization::base_object<Object>(*this); ar& boost::serialization::base_object<WaitObject>(*this);
ar&* context.get(); ar&* context.get();
ar& thread_id; ar& thread_id;
ar& status; ar& status;
@ -363,7 +363,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
if (needs_allocation) { if (needs_allocation) {
// There are no already-allocated pages with free slots, lets allocate a new one. // There are no already-allocated pages with free slots, lets allocate a new one.
// TLS pages are allocated from the BASE region in the linear heap. // TLS pages are allocated from the BASE region in the linear heap.
MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE); auto memory_region = GetMemoryRegion(MemoryRegion::BASE);
// Allocate some memory from the end of the linear heap for this region. // Allocate some memory from the end of the linear heap for this region.
auto offset = memory_region->LinearAllocate(Memory::PAGE_SIZE); auto offset = memory_region->LinearAllocate(Memory::PAGE_SIZE);

View file

@ -824,10 +824,6 @@ std::unique_ptr<Kernel::SessionRequestHandler::SessionDataBase> GSP_GPU::MakeSes
return std::make_unique<SessionData>(this); return std::make_unique<SessionData>(this);
} }
SessionData::SessionData() {
UNREACHABLE();
}
SessionData::SessionData(GSP_GPU* gsp) : gsp(gsp) { SessionData::SessionData(GSP_GPU* gsp) : gsp(gsp) {
// Assign a new thread id to this session when it connects. Note: In the real GSP service this // Assign a new thread id to this session when it connects. Note: In the real GSP service this
// is done through a real thread (svcCreateThread) but we have to simulate it since our HLE // is done through a real thread (svcCreateThread) but we have to simulate it since our HLE

View file

@ -187,7 +187,7 @@ class GSP_GPU;
class SessionData : public Kernel::SessionRequestHandler::SessionDataBase { class SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
public: public:
SessionData(); SessionData() = default;
SessionData(GSP_GPU* gsp); SessionData(GSP_GPU* gsp);
~SessionData(); ~SessionData();

View file

@ -15,6 +15,7 @@
SERVICE_CONSTRUCT_IMPL(Service::LDR::RO) SERVICE_CONSTRUCT_IMPL(Service::LDR::RO)
SERIALIZE_EXPORT_IMPL(Service::LDR::RO) SERIALIZE_EXPORT_IMPL(Service::LDR::RO)
SERIALIZE_EXPORT_IMPL(Service::LDR::ClientSlot)
namespace Service::LDR { namespace Service::LDR {