From 2560efbebd888dde47e1c601c1450aadbdac9d66 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 14 Sep 2019 20:28:47 +1000 Subject: [PATCH] Save state support --- src/common/state_wrapper.h | 23 +++++++++++++++ src/pse-sdl/main.cpp | 36 ++++++++++++++---------- src/pse-sdl/sdl_interface.cpp | 37 ++++++------------------ src/pse-sdl/sdl_interface.h | 5 ++-- src/pse/bus.cpp | 6 +++- src/pse/cpu_core.cpp | 25 ++++++++++++++++- src/pse/dma.cpp | 17 +++++++++++ src/pse/dma.h | 3 ++ src/pse/gpu.cpp | 44 +++++++++++++++++++++++++++-- src/pse/gpu.h | 8 ++---- src/pse/host_interface.cpp | 53 +++++++++++++++++++++++++++++++++++ src/pse/host_interface.h | 13 +++++++++ src/pse/pse.vcxproj | 1 + src/pse/pse.vcxproj.filters | 1 + src/pse/system.cpp | 32 +++++++++++++++++++++ src/pse/system.h | 8 ++++++ 16 files changed, 256 insertions(+), 56 deletions(-) create mode 100644 src/pse/host_interface.cpp diff --git a/src/common/state_wrapper.h b/src/common/state_wrapper.h index d4eaea13a..35aa39a3f 100644 --- a/src/common/state_wrapper.h +++ b/src/common/state_wrapper.h @@ -2,6 +2,7 @@ #include "YBaseLib/ByteStream.h" #include "types.h" #include +#include #include #include #include @@ -111,6 +112,28 @@ public: DoArray(data->data(), data->size()); } + template + void Do(std::deque* data) + { + u32 length = static_cast(data->size()); + Do(&length); + if (m_mode == Mode::Read) + { + data->clear(); + for (u32 i = 0; i < length; i++) + { + T value; + Do(&value); + data->push_back(value); + } + } + else + { + for (u32 i = 0; i < length; i++) + Do(&data[i]); + } + } + bool DoMarker(const char* marker); private: diff --git a/src/pse-sdl/main.cpp b/src/pse-sdl/main.cpp index 5783dc7df..a582bed26 100644 --- a/src/pse-sdl/main.cpp +++ b/src/pse-sdl/main.cpp @@ -1,9 +1,9 @@ #include "YBaseLib/Assert.h" #include "YBaseLib/Log.h" #include "YBaseLib/StringConverter.h" -#include "sdl_interface.h" -#include "pse/types.h" #include "pse/system.h" +#include "pse/types.h" +#include "sdl_interface.h" #include #include @@ -24,14 +24,6 @@ static int NoGUITest() static int Run(int argc, char* argv[]) { -#if 0 - if (argc < 2) - { - std::fprintf(stderr, "Usage: %s [save state index]\n", argv[0]); - return -1; - } -#endif - // init sdl if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { @@ -48,11 +40,25 @@ static int Run(int argc, char* argv[]) return -1; } - // create system + // parameters + const char* filename = nullptr; s32 state_index = -1; - if (argc > 2) - state_index = StringConverter::StringToInt32(argv[2]); - if (!host_interface->InitializeSystem("", state_index)) + for (int i = 1; i < argc; i++) + { +#define CHECK_ARG(str) !std::strcmp(argv[i], str) +#define CHECK_ARG_PARAM(str) (!std::strcmp(argv[i], str) && ((i + 1) < argc)) + + if (CHECK_ARG_PARAM("-state")) + state_index = std::atoi(argv[++i]); + else + filename = argv[i]; + +#undef CHECK_ARG +#undef CHECK_ARG_PARAM + } + + // create system + if (!host_interface->InitializeSystem(filename, state_index)) { host_interface.reset(); SDL_Quit(); @@ -83,6 +89,6 @@ int main(int argc, char* argv[]) g_pLog->SetFilterLevel(LOGLEVEL_DEBUG); #endif - //return NoGUITest(); + // return NoGUITest(); return Run(argc, argv); } diff --git a/src/pse-sdl/sdl_interface.cpp b/src/pse-sdl/sdl_interface.cpp index ec714c572..984b3155d 100644 --- a/src/pse-sdl/sdl_interface.cpp +++ b/src/pse-sdl/sdl_interface.cpp @@ -148,15 +148,13 @@ std::unique_ptr SDLInterface::Create() return intf; } -// TinyString SDLInterface::GetSaveStateFilename(u32 index) -// { -// return TinyString::FromFormat("savestate_%u.bin", index); -// } +TinyString SDLInterface::GetSaveStateFilename(u32 index) +{ + return TinyString::FromFormat("savestate_%u.bin", index); +} bool SDLInterface::InitializeSystem(const char* filename, s32 save_state_index /* = -1 */) { - Error error; - m_system = std::make_unique(this); if (!m_system->Initialize()) { @@ -164,21 +162,13 @@ bool SDLInterface::InitializeSystem(const char* filename, s32 save_state_index / return false; } -#if 0 + m_system->Reset(); + if (save_state_index >= 0) { // Load the save state. - if (!HostInterface::LoadSystemState(GetSaveStateFilename(static_cast(save_state_index)), &error)) - { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Loading save state failed", error.GetErrorCodeAndDescription(), - m_window); - } + LoadState(GetSaveStateFilename(static_cast(save_state_index))); } -#endif - - m_system->Reset(); - - //m_system->LoadEXE("tests/psxtest_cpu.psxexe"); // Resume execution. m_running = true; @@ -458,21 +448,12 @@ void SDLInterface::RenderOSDMessages() void SDLInterface::DoLoadState(u32 index) { -#if 0 - Error error; - if (!LoadSystemState(TinyString::FromFormat("savestate_%u.bin", index), &error)) - { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Loading save state failed", error.GetErrorCodeAndDescription(), - m_window); - } -#endif + LoadState(GetSaveStateFilename(index)); } void SDLInterface::DoSaveState(u32 index) { -#if 0 - SaveSystemState(TinyString::FromFormat("savestate_%u.bin", index)); -#endif + SaveState(GetSaveStateFilename(index)); } void SDLInterface::Run() diff --git a/src/pse-sdl/sdl_interface.h b/src/pse-sdl/sdl_interface.h index 17fed0e41..f3d245b9e 100644 --- a/src/pse-sdl/sdl_interface.h +++ b/src/pse-sdl/sdl_interface.h @@ -19,6 +19,8 @@ public: static std::unique_ptr Create(); + static TinyString GetSaveStateFilename(u32 index); + void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height) override; void ReportMessage(const char* message) override; @@ -61,7 +63,6 @@ private: int m_window_width = 0; int m_window_height = 0; - std::unique_ptr m_system; GL::Program m_display_program; GLuint m_display_vao = 0; GL::Texture* m_display_texture = nullptr; @@ -73,6 +74,4 @@ private: std::deque m_osd_messages; std::mutex m_osd_messages_lock; - - bool m_running = false; }; diff --git a/src/pse/bus.cpp b/src/pse/bus.cpp index fb566d9f6..eed10a537 100644 --- a/src/pse/bus.cpp +++ b/src/pse/bus.cpp @@ -2,6 +2,7 @@ #include "YBaseLib/ByteStream.h" #include "YBaseLib/Log.h" #include "YBaseLib/String.h" +#include "common/state_wrapper.h" #include "cpu_disasm.h" #include "dma.h" #include "gpu.h" @@ -29,7 +30,10 @@ void Bus::Reset() bool Bus::DoState(StateWrapper& sw) { - return false; + sw.DoBytes(m_ram.data(), m_ram.size()); + sw.DoBytes(m_bios.data(), m_bios.size()); + sw.Do(&m_tty_line_buffer); + return !sw.HasError(); } bool Bus::ReadByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8* value) diff --git a/src/pse/cpu_core.cpp b/src/pse/cpu_core.cpp index e3463f298..3f0e7eafc 100644 --- a/src/pse/cpu_core.cpp +++ b/src/pse/cpu_core.cpp @@ -39,7 +39,30 @@ void Core::Reset() bool Core::DoState(StateWrapper& sw) { - return false; + sw.DoArray(m_regs.r, countof(m_regs.r)); + sw.Do(&m_regs.pc); + sw.Do(&m_regs.hi); + sw.Do(&m_regs.lo); + sw.Do(&m_regs.npc); + sw.Do(&m_cop0_regs.BPC); + sw.Do(&m_cop0_regs.BDA); + sw.Do(&m_cop0_regs.JUMPDEST); + sw.Do(&m_cop0_regs.BadVaddr); + sw.Do(&m_cop0_regs.BDAM); + sw.Do(&m_cop0_regs.BPCM); + sw.Do(&m_cop0_regs.EPC); + sw.Do(&m_cop0_regs.PRID); + sw.Do(&m_cop0_regs.sr.bits); + sw.Do(&m_cop0_regs.cause.bits); + sw.Do(&m_cop0_regs.dcic.bits); + sw.Do(&m_next_instruction.bits); + sw.Do(&m_in_branch_delay_slot); + sw.Do(&m_branched); + sw.Do(&m_load_delay_reg); + sw.Do(&m_load_delay_old_value); + sw.Do(&m_cache_control); + sw.DoBytes(m_dcache.data(), m_dcache.size()); + return !sw.HasError(); } void Core::SetPC(u32 new_pc) diff --git a/src/pse/dma.cpp b/src/pse/dma.cpp index b8d1ebfe4..842d61dd3 100644 --- a/src/pse/dma.cpp +++ b/src/pse/dma.cpp @@ -1,6 +1,7 @@ #include "dma.h" #include "YBaseLib/Log.h" #include "bus.h" +#include "common/state_wrapper.h" #include "gpu.h" Log_SetChannel(DMA); @@ -22,6 +23,22 @@ void DMA::Reset() m_DCIR = 0; } +bool DMA::DoState(StateWrapper& sw) +{ + for (u32 i = 0; i < NUM_CHANNELS; i++) + { + ChannelState& cs = m_state[i]; + sw.Do(&cs.base_address); + sw.Do(&cs.block_control.bits); + sw.Do(&cs.channel_control.bits); + sw.Do(&cs.request); + } + + sw.Do(&m_DPCR.bits); + sw.Do(&m_DCIR); + return !sw.HasError(); +} + u32 DMA::ReadRegister(u32 offset) { const u32 channel_index = offset >> 4; diff --git a/src/pse/dma.h b/src/pse/dma.h index 8a2282c18..5155c874e 100644 --- a/src/pse/dma.h +++ b/src/pse/dma.h @@ -3,6 +3,8 @@ #include "types.h" #include +class StateWrapper; + class Bus; class GPU; @@ -30,6 +32,7 @@ public: bool Initialize(Bus* bus, GPU* gpu); void Reset(); + bool DoState(StateWrapper& sw); u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); diff --git a/src/pse/gpu.cpp b/src/pse/gpu.cpp index 7cd705437..c3222fa48 100644 --- a/src/pse/gpu.cpp +++ b/src/pse/gpu.cpp @@ -1,6 +1,7 @@ #include "gpu.h" #include "YBaseLib/Log.h" #include "bus.h" +#include "common/state_wrapper.h" #include "dma.h" #include "system.h" Log_SetChannel(GPU); @@ -28,6 +29,46 @@ void GPU::SoftReset() UpdateGPUSTAT(); } +bool GPU::DoState(StateWrapper& sw) +{ + if (sw.IsReading()) + FlushRender(); + + sw.Do(&m_GPUSTAT.bits); + sw.Do(&m_texture_config.base_x); + sw.Do(&m_texture_config.base_y); + sw.Do(&m_texture_config.palette_x); + sw.Do(&m_texture_config.palette_y); + sw.Do(&m_texture_config.page_attribute); + sw.Do(&m_texture_config.palette_attribute); + sw.Do(&m_texture_config.color_mode); + sw.Do(&m_texture_config.page_changed); + sw.Do(&m_texture_config.window_mask_x); + sw.Do(&m_texture_config.window_mask_y); + sw.Do(&m_texture_config.window_offset_x); + sw.Do(&m_texture_config.window_offset_y); + sw.Do(&m_texture_config.x_flip); + sw.Do(&m_texture_config.y_flip); + sw.Do(&m_drawing_area.top_left_x); + sw.Do(&m_drawing_area.top_left_y); + sw.Do(&m_drawing_area.bottom_right_x); + sw.Do(&m_drawing_area.bottom_right_y); + sw.Do(&m_drawing_offset.x); + sw.Do(&m_drawing_offset.y); + sw.Do(&m_drawing_offset.x); + + sw.Do(&m_GP0_command); + sw.Do(&m_GPUREAD_buffer); + + if (sw.IsReading()) + { + m_texture_config.page_changed = true; + UpdateGPUSTAT(); + } + + return !sw.HasError(); +} + void GPU::UpdateGPUSTAT() { m_GPUSTAT.ready_to_send_vram = !m_GPUREAD_buffer.empty(); @@ -352,7 +393,6 @@ bool GPU::HandleRenderCommand() ZeroExtend32(words_per_vertex)); DispatchRenderCommand(rc, num_vertices); - UpdateDisplay(); return true; } @@ -370,7 +410,6 @@ bool GPU::HandleFillRectangleCommand() Log_DebugPrintf("Fill VRAM rectangle offset=(%u,%u), size=(%u,%u)", dst_x, dst_y, width, height); FillVRAM(dst_x, dst_y, width, height, color); - UpdateDisplay(); return true; } @@ -400,7 +439,6 @@ bool GPU::HandleCopyRectangleCPUToVRAMCommand() FlushRender(); UpdateVRAM(dst_x, dst_y, copy_width, copy_height, &m_GP0_command[3]); - UpdateDisplay(); return true; } diff --git a/src/pse/gpu.h b/src/pse/gpu.h index 65fcc64f1..180f55883 100644 --- a/src/pse/gpu.h +++ b/src/pse/gpu.h @@ -5,6 +5,8 @@ #include #include +class StateWrapper; + class System; class Bus; class DMA; @@ -17,6 +19,7 @@ public: virtual bool Initialize(System* system, Bus* bus, DMA* dma); virtual void Reset(); + virtual bool DoState(StateWrapper& sw); u32 ReadRegister(u32 offset); void WriteRegister(u32 offset, u32 value); @@ -207,11 +210,6 @@ protected: s32 y; } m_drawing_offset = {}; - struct TexturePageConfig - { - - } m_texture_page_config = {}; - std::vector m_GP0_command; std::deque m_GPUREAD_buffer; }; diff --git a/src/pse/host_interface.cpp b/src/pse/host_interface.cpp new file mode 100644 index 000000000..9b1dba6a5 --- /dev/null +++ b/src/pse/host_interface.cpp @@ -0,0 +1,53 @@ +#include "host_interface.h" +#include "YBaseLib/ByteStream.h" +#include "system.h" + +HostInterface::HostInterface() = default; + +HostInterface::~HostInterface() = default; + +bool HostInterface::LoadState(const char* filename) +{ + ByteStream* stream; + if (!ByteStream_OpenFileStream(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, &stream)) + return false; + + ReportMessage(SmallString::FromFormat("Loading state from %s...", filename)); + + const bool result = m_system->LoadState(stream); + if (!result) + { + ReportMessage(SmallString::FromFormat("Loading state from %s failed. Resetting.", filename)); + m_system->Reset(); + } + + stream->Release(); + return result; +} + +bool HostInterface::SaveState(const char* filename) +{ + ByteStream* stream; + if (!ByteStream_OpenFileStream(filename, + BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE | + BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED, + &stream)) + { + return false; + } + + const bool result = m_system->SaveState(stream); + if (!result) + { + ReportMessage(SmallString::FromFormat("Saving state to %s failed.", filename)); + stream->Discard(); + } + else + { + ReportMessage(SmallString::FromFormat("State saved to %s.", filename)); + stream->Commit(); + } + + stream->Release(); + return result; +} diff --git a/src/pse/host_interface.h b/src/pse/host_interface.h index cd571a034..0228ee4b6 100644 --- a/src/pse/host_interface.h +++ b/src/pse/host_interface.h @@ -1,16 +1,29 @@ #pragma once #include "types.h" +#include namespace GL { class Texture; } +class System; + class HostInterface { public: + HostInterface(); + virtual ~HostInterface(); + virtual void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height) = 0; virtual void ReportMessage(const char* message) = 0; // Adds OSD messages, duration is in seconds. virtual void AddOSDMessage(const char* message, float duration = 2.0f) = 0; + +protected: + bool LoadState(const char* filename); + bool SaveState(const char* filename); + + std::unique_ptr m_system; + bool m_running = false; }; diff --git a/src/pse/pse.vcxproj b/src/pse/pse.vcxproj index 266d913ab..3f26b44d3 100644 --- a/src/pse/pse.vcxproj +++ b/src/pse/pse.vcxproj @@ -42,6 +42,7 @@ + diff --git a/src/pse/pse.vcxproj.filters b/src/pse/pse.vcxproj.filters index 41b27a8b9..c4e20b9b4 100644 --- a/src/pse/pse.vcxproj.filters +++ b/src/pse/pse.vcxproj.filters @@ -9,6 +9,7 @@ + diff --git a/src/pse/system.cpp b/src/pse/system.cpp index 35afe7b06..2b9044879 100644 --- a/src/pse/system.cpp +++ b/src/pse/system.cpp @@ -1,5 +1,7 @@ #include "system.h" +#include "YBaseLib/ByteStream.h" #include "bus.h" +#include "common/state_wrapper.h" #include "cpu_core.h" #include "dma.h" #include "gpu.h" @@ -32,6 +34,23 @@ bool System::Initialize() return true; } +bool System::DoState(StateWrapper& sw) +{ + if (!sw.DoMarker("CPU") || !m_cpu->DoState(sw)) + return false; + + if (!sw.DoMarker("Bus") || !m_bus->DoState(sw)) + return false; + + if (!sw.DoMarker("DMA") || !m_dma->DoState(sw)) + return false; + + if (!sw.DoMarker("GPU") || !m_gpu->DoState(sw)) + return false; + + return !sw.HasError(); +} + void System::Reset() { m_cpu->Reset(); @@ -41,6 +60,18 @@ void System::Reset() m_frame_number = 1; } +bool System::LoadState(ByteStream* state) +{ + StateWrapper sw(state, StateWrapper::Mode::Read); + return DoState(sw); +} + +bool System::SaveState(ByteStream* state) +{ + StateWrapper sw(state, StateWrapper::Mode::Write); + return DoState(sw); +} + void System::RunFrame() { u32 current_frame_number = m_frame_number; @@ -134,3 +165,4 @@ bool System::LoadEXE(const char* filename) return true; } + diff --git a/src/pse/system.h b/src/pse/system.h index 615818885..83365788b 100644 --- a/src/pse/system.h +++ b/src/pse/system.h @@ -1,6 +1,9 @@ #pragma once #include "types.h" +class ByteStream; +class StateWrapper; + class HostInterface; namespace CPU @@ -26,11 +29,16 @@ public: bool Initialize(); void Reset(); + bool LoadState(ByteStream* state); + bool SaveState(ByteStream* state); + void RunFrame(); bool LoadEXE(const char* filename); private: + bool DoState(StateWrapper& sw); + HostInterface* m_host_interface; std::unique_ptr m_cpu; std::unique_ptr m_bus;