Experimental save state support

This commit is contained in:
SimoneN64
2023-10-30 08:39:54 +01:00
parent c4f148eec1
commit b1f7659359
17 changed files with 313 additions and 1 deletions

View File

@@ -90,4 +90,29 @@ void Core::Run(float volumeL, float volumeR) {
scheduler.tick(frameCycles, mem, regs);
}
}
void Core::Serialize() {
auto sMEM = cpu->mem.Serialize();
auto sCPU = cpu->Serialize();
auto sVER = std::vector<u8>{KAIZEN_VERSION >> 8, KAIZEN_VERSION >> 4, KAIZEN_VERSION & 0xFF};
memSize = sMEM.size();
cpuSize = sCPU.size();
verSize = sVER.size();
serialized[slot].insert(serialized[slot].begin(), sVER.begin(), sVER.end());
serialized[slot].insert(serialized[slot].end(), sMEM.begin(), sMEM.end());
serialized[slot].insert(serialized[slot].end(), sCPU.begin(), sCPU.end());
}
void Core::Deserialize() {
std::vector<u8> dVER(serialized[slot].begin(), serialized[slot].begin() + verSize);
if(dVER[0] != (KAIZEN_VERSION >> 8)
|| dVER[1] != (KAIZEN_VERSION >> 4)
|| dVER[2] != (KAIZEN_VERSION & 0xFF)) {
Util::panic("PROBLEMI!");
}
cpu->mem.Deserialize(std::vector<u8>(serialized[slot].begin() + verSize, serialized[slot].begin() + verSize + memSize));
cpu->Deserialize(std::vector<u8>(serialized[slot].begin() + verSize + memSize, serialized[slot].begin() + verSize + memSize + cpuSize));
serialized[slot].erase(serialized[slot].begin(), serialized[slot].end());
}
}

View File

@@ -14,6 +14,8 @@ struct Core {
void Stop();
void LoadROM(const std::string&);
void Run(float volumeL, float volumeR);
void Serialize();
void Deserialize();
void TogglePause() { pause = !pause; }
[[nodiscard]] VI& GetVI() { return cpu->mem.mmio.vi; }
@@ -25,5 +27,8 @@ struct Core {
bool romLoaded = false;
std::string rom;
std::unique_ptr<BaseCPU> cpu;
std::vector<u8> serialized[10]{};
int memSize, cpuSize, verSize;
int slot = 0;
};
}

View File

@@ -12,6 +12,8 @@ struct BaseCPU {
}
virtual bool ShouldServiceInterrupt() = 0;
virtual void CheckCompareInterrupt() = 0;
virtual std::vector<u8> Serialize() = 0;
virtual void Deserialize(const std::vector<u8>&) = 0;
Registers regs;
Mem mem;
};

View File

@@ -55,4 +55,18 @@ int Interpreter::Step() {
return 1;
}
std::vector<u8> Interpreter::Serialize() {
std::vector<u8> res{};
res.resize(sizeof(Registers));
memcpy(res.data(), &regs, sizeof(Registers));
return res;
}
void Interpreter::Deserialize(const std::vector<u8> &data) {
memcpy(&regs, data.data(), sizeof(Registers));
}
}

View File

@@ -15,6 +15,9 @@ private:
#define check_address_error(mask, vaddr) (((!regs.cop0.is_64bit_addressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
bool ShouldServiceInterrupt() override;
void CheckCompareInterrupt() override;
std::vector<u8> Serialize() override;
void Deserialize(const std::vector<u8>&) override;
void cop2Decode(u32);
void special(u32);
void regimm(u32);

View File

@@ -101,6 +101,9 @@ private:
return mem.Read8(regs, addr);
}
std::vector<u8> Serialize() override { return {}; }
void Deserialize(const std::vector<u8>&) override { }
void cop2Decode(u32);
void special(u32);
void regimm(u32);

View File

@@ -44,4 +44,80 @@ void MMIO::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
Util::panic("Unhandled mmio write at addr {:08X} with val {:08X}", addr, val);
}
}
std::vector<u8> MMIO::Serialize() {
std::vector<u8> res{};
auto sPIF = si.pif.Serialize();
constexpr u32 rdpSize = sizeof(DPC) +
0xFFFFF +
RDRAM_SIZE;
res.resize(
rdpSize +
sizeof(RSP) +
sizeof(MI) +
sizeof(VI) +
sizeof(SI) +
sizeof(PI) +
sizeof(RI) +
sizeof(AI) +
sizeof(u32)*2 +
sizeof(SIStatus));
u32 index = 0;
memcpy(res.data(), &rsp, sizeof(RSP));
index += sizeof(RSP);
memcpy(res.data() + index, &rdp.dpc, sizeof(DPC));
index += sizeof(DPC);
memcpy(res.data() + index, rdp.cmd_buf, 0xFFFFF);
index += 0xFFFFF;
memcpy(res.data() + index, rdp.rdram, RDRAM_SIZE);
index += RDRAM_SIZE;
memcpy(res.data() + index, &mi, sizeof(MI));
index += sizeof(MI);
memcpy(res.data() + index, &vi, sizeof(VI));
index += sizeof(VI);
memcpy(res.data() + index, &ai, sizeof(AI));
index += sizeof(AI);
memcpy(res.data() + index, &pi, sizeof(PI));
index += sizeof(PI);
memcpy(res.data() + index, &ri, sizeof(RI));
index += sizeof(RI);
memcpy(res.data() + index, &si.dramAddr, sizeof(u32));
index += sizeof(u32);
memcpy(res.data() + index, &si.pifAddr, sizeof(u32));
index += sizeof(u32);
memcpy(res.data() + index, &si.status, sizeof(SIStatus));
res.insert(res.end(), sPIF.begin(), sPIF.end());
return res;
}
void MMIO::Deserialize(const std::vector<u8> &data) {
u32 index = 0;
memcpy(&rsp, data.data(), sizeof(RSP));
index += sizeof(RSP);
memcpy(&rdp.dpc, data.data() + index, sizeof(DPC));
index += sizeof(DPC);
memcpy(rdp.cmd_buf, data.data() + index, 0xFFFFF);
index += 0xFFFFF;
memcpy(rdp.rdram, data.data() + index, RDRAM_SIZE);
index += RDRAM_SIZE;
memcpy(&mi, data.data() + index, sizeof(MI));
index += sizeof(MI);
memcpy(&vi, data.data() + index, sizeof(VI));
index += sizeof(VI);
memcpy(&ai, data.data() + index, sizeof(AI));
index += sizeof(AI);
memcpy(&pi, data.data() + index, sizeof(PI));
index += sizeof(PI);
memcpy(&ri, data.data() + index, sizeof(RI));
index += sizeof(RI);
memcpy(&si.dramAddr, data.data() + index, sizeof(u32));
index += sizeof(u32);
memcpy(&si.pifAddr, data.data() + index, sizeof(u32));
index += sizeof(u32);
memcpy(&si.status, data.data() + index, sizeof(SIStatus));
}
}

View File

@@ -27,5 +27,7 @@ struct MMIO {
u32 Read(u32);
void Write(Mem&, Registers&, u32, u32);
std::vector<u8> Serialize();
void Deserialize(const std::vector<u8>&);
};
}

View File

@@ -601,4 +601,25 @@ void Mem::Write64(Registers& regs, u32 paddr, u64 val) {
}
}
}
std::vector<u8> Mem::Serialize() {
std::vector<u8> res{};
auto sMMIO = mmio.Serialize();
auto sFLASH = flash.Serialize();
mmioSize = sMMIO.size();
flashSize = sFLASH.size();
res.insert(res.begin(), sMMIO.begin(), sMMIO.end());
res.insert(res.end(), sFLASH.begin(), sFLASH.end());
res.insert(res.end(), sram.begin(), sram.end());
return res;
}
void Mem::Deserialize(const std::vector<u8>& data) {
mmio.Deserialize(std::vector<u8>(data.begin(), data.begin() + mmioSize));
flash.Deserialize(std::vector<u8>(data.begin() + mmioSize, data.begin() + mmioSize + flashSize));
memcpy(sram.data(), data.data() + mmioSize + flashSize, sram.size());
}
}

View File

@@ -73,6 +73,45 @@ struct Flash {
void CommandWrite();
void CommandRead();
FORCE_INLINE std::vector<u8> Serialize() {
std::vector<u8> res{};
res.resize(
sizeof(state) +
sizeof(status) +
sizeof(eraseOffs) +
sizeof(writeOffs) +
128);
u32 index = 0;
memcpy(res.data() + index, &state, sizeof(state));
index += sizeof(state);
memcpy(res.data() + index, &status, sizeof(status));
index += sizeof(status);
memcpy(res.data() + index, &eraseOffs, sizeof(eraseOffs));
index += sizeof(eraseOffs);
memcpy(res.data() + index, &writeOffs, sizeof(writeOffs));
index += sizeof(writeOffs);
memcpy(res.data() + index, writeBuf, 128);
res.insert(res.begin(), flash.begin(), flash.end());
return res;
}
FORCE_INLINE void Deserialize(const std::vector<u8>& data) {
u32 index = 0;
memcpy(&state, data.data() + index, sizeof(state));
index += sizeof(state);
memcpy(&status, data.data() + index, sizeof(status));
index += sizeof(status);
memcpy(&eraseOffs, data.data() + index, sizeof(eraseOffs));
index += sizeof(eraseOffs);
memcpy(&writeOffs, data.data() + index, sizeof(writeOffs));
index += sizeof(writeOffs);
memcpy(writeBuf, data.data() + index, 128);
}
FORCE_INLINE void Write32(u32 index, u32 val) {
if(index > 0) {
u8 cmd = val >> 24;
@@ -141,6 +180,9 @@ struct Mem {
return mmio.rdp.rdram;
}
std::vector<u8> Serialize();
void Deserialize(const std::vector<u8>&);
u8 Read8(Registers&, u32);
u16 Read16(Registers&, u32);
u32 Read32(Registers&, u32);
@@ -194,6 +236,7 @@ private:
mio::mmap_sink sram;
u8 isviewer[ISVIEWER_SIZE]{};
std::string sramPath{};
int mmioSize, flashSize;
FORCE_INLINE bool IsROMPAL() {
static const char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'};

View File

@@ -988,5 +988,4 @@ void Interpreter::ctc2(u32) {
void Interpreter::cfc2(u32) {
}
}

View File

@@ -627,4 +627,30 @@ void PIF::ExecutePIF(Mem& mem, Registers& regs) {
DoPIFHLE(mem, regs, pal, cicType);
}
std::vector<u8> PIF::Serialize() {
std::vector<u8> res{};
res.resize(
6*sizeof(JoybusDevice) +
PIF_BOOTROM_SIZE +
PIF_RAM_SIZE +
mempak.size() +
eeprom.size() +
sizeof(int));
u32 index = 0;
memcpy(res.data() + index, joybusDevices, 6*sizeof(JoybusDevice));
index += 6*sizeof(JoybusDevice);
memcpy(res.data() + index, bootrom, PIF_BOOTROM_SIZE);
index += PIF_BOOTROM_SIZE;
memcpy(res.data() + index, ram, PIF_RAM_SIZE);
index += PIF_RAM_SIZE;
memcpy(res.data() + index, mempak.data(), mempak.size());
index += mempak.size();
memcpy(res.data() + index, eeprom.data(), eeprom.size());
index += eeprom.size();
memcpy(res.data() + index, &channel, sizeof(int));
return res;
}
}

View File

@@ -122,6 +122,7 @@ struct PIF {
void MempakWrite(u8*, u8*);
void EepromRead(const u8*, u8*, const Mem&) const;
void EepromWrite(const u8*, u8*, const Mem&);
std::vector<u8> Serialize();
bool gamepadConnected = false, mempakOpen = false;
SDL_GameController* gamepad{};

View File

@@ -18,6 +18,7 @@ using m128i = __m128i;
#define FORCE_INLINE inline __attribute__((always_inline))
constexpr u32 N64_CPU_FREQ = 93750000;
constexpr u16 KAIZEN_VERSION = 0x010;
static FORCE_INLINE constexpr u32 GetCyclesPerFrame(bool pal) {
if (pal) {

View File

@@ -41,6 +41,64 @@ void App::Run() {
case SDLK_o: {
OpenROMDialog(window, core);
} break;
case SDLK_F1: {
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 0;
}
} break;
case SDLK_F2: {
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 1;
}
} break;
case SDLK_F3: {
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 2;
}
} break;
case SDLK_F4: {
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 3;
}
} break;
case SDLK_F5: {
if(core.romLoaded) {
if(event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 4;
} else {
core.Deserialize();
}
}
} break;
case SDLK_F6: {
if(core.romLoaded) {
if(event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 5;
} else {
core.Serialize();
}
}
} break;
case SDLK_F7: {
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 6;
}
} break;
case SDLK_F8: {
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 7;
}
} break;
case SDLK_F9: {
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 8;
}
} break;
case SDLK_F10: {
if(core.romLoaded && event.key.keysym.mod == KMOD_SHIFT) {
core.slot = 9;
}
} break;
}
break;
case SDL_DROPFILE: {

View File

@@ -15,6 +15,10 @@ enum StringID {
EMULATION_ITEM_PAUSE,
EMULATION_ITEM_RESUME,
EMULATION_ITEM_SETTINGS,
EMULATION_ITEM_LOAD_STATE,
EMULATION_ITEM_SAVE_STATE,
EMULATION_MENU_STATES,
STATES_ITEM_SLOT,
SETTINGS_CATEGORY_CPU,
SETTINGS_CATEGORY_AUDIO,
SETTINGS_CATEGORY_INTERFACE,
@@ -38,6 +42,10 @@ static const std::map <StringID, const char*> english = {
{EMULATION_ITEM_PAUSE, "Pause"},
{EMULATION_ITEM_RESUME, "Resume"},
{EMULATION_ITEM_SETTINGS, "Settings"},
{EMULATION_ITEM_LOAD_STATE, "Load state..."},
{EMULATION_ITEM_SAVE_STATE, "Save state..."},
{EMULATION_MENU_STATES, "Select save slot"},
{STATES_ITEM_SLOT, "Slot {}"},
{SETTINGS_CATEGORY_CPU, "CPU"},
{SETTINGS_CATEGORY_AUDIO, "Audio"},
{SETTINGS_CATEGORY_INTERFACE, "Interface"},
@@ -60,6 +68,10 @@ static const std::map <StringID, const char*> italian = {
{EMULATION_ITEM_PAUSE, "Pausa"},
{EMULATION_ITEM_RESUME, "Riprendi"},
{EMULATION_ITEM_SETTINGS, "Opzioni"},
{EMULATION_ITEM_LOAD_STATE, "Carica stato..."},
{EMULATION_ITEM_SAVE_STATE, "Salva stato..."},
{EMULATION_MENU_STATES, "Seleziona slot"},
{STATES_ITEM_SLOT, "Slot {}"},
{SETTINGS_CATEGORY_CPU, "CPU"},
{SETTINGS_CATEGORY_AUDIO, "Audio"},
{SETTINGS_CATEGORY_INTERFACE, "Interfaccia"},

View File

@@ -219,6 +219,27 @@ void Window::RenderMainMenuBar(n64::Core &core) {
}
SDL_SetWindowTitle(window, windowTitle.c_str());
}
if(ImGui::BeginMenu(GET_TRANSLATED_STRING(Language::EMULATION_MENU_STATES))) {
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 0).c_str())) { core.slot = 0; }
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 1).c_str())) { core.slot = 1; }
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 2).c_str())) { core.slot = 2; }
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 3).c_str())) { core.slot = 3; }
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 4).c_str())) { core.slot = 4; }
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 5).c_str())) { core.slot = 5; }
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 6).c_str())) { core.slot = 6; }
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 7).c_str())) { core.slot = 7; }
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 8).c_str())) { core.slot = 8; }
if(ImGui::MenuItem(fmt::format(GET_TRANSLATED_STRING(Language::STATES_ITEM_SLOT), 9).c_str())) { core.slot = 9; }
ImGui::EndMenu();
}
if(ImGui::MenuItem(GET_TRANSLATED_STRING(Language::EMULATION_ITEM_LOAD_STATE), "F5", false, core.romLoaded)) {
core.Deserialize();
}
if(ImGui::MenuItem(GET_TRANSLATED_STRING(Language::EMULATION_ITEM_SAVE_STATE), "F6", false, core.romLoaded)) {
core.Serialize();
}
if (ImGui::MenuItem(GET_TRANSLATED_STRING(Language::EMULATION_ITEM_SETTINGS))) {
showSettings = true;
}