Debugger: Added stateLoaded/stateSaved Lua callbacks (+ fixed issues with savestate API & Lua reference cleanup)

This commit is contained in:
Souryo 2017-10-07 19:48:45 -04:00
parent 11a06926ff
commit 837032f8ea
11 changed files with 130 additions and 33 deletions

View file

@ -430,7 +430,7 @@ bool Debugger::PrivateProcessRamOperation(MemoryOperationType type, uint16_t &ad
} }
addr = _nextReadAddr; addr = _nextReadAddr;
value = _memoryManager->DebugRead(addr, false); value = _memoryManager->DebugRead(addr, true);
_cpu->SetDebugPC(addr); _cpu->SetDebugPC(addr);
_nextReadAddr = -1; _nextReadAddr = -1;
} else if(_needRewind) { } else if(_needRewind) {
@ -445,9 +445,7 @@ bool Debugger::PrivateProcessRamOperation(MemoryOperationType type, uint16_t &ad
} else { } else {
RewindManager::StartRewinding(true); RewindManager::StartRewinding(true);
} }
addr = _cpu->GetState().PC; UpdateProgramCounter(addr, value);
value = _memoryManager->DebugRead(addr, false);
_cpu->SetDebugPC(addr);
_needRewind = false; _needRewind = false;
} }
ProcessScriptSaveState(addr, value); ProcessScriptSaveState(addr, value);
@ -789,7 +787,7 @@ void Debugger::SetNextStatement(uint16_t addr)
if(_currentReadAddr) { if(_currentReadAddr) {
_cpu->SetDebugPC(addr); _cpu->SetDebugPC(addr);
*_currentReadAddr = addr; *_currentReadAddr = addr;
*_currentReadValue = _memoryManager->DebugRead(addr, false); *_currentReadValue = _memoryManager->DebugRead(addr, true);
} else { } else {
//Can't change the address right away (CPU is in the middle of an instruction) //Can't change the address right away (CPU is in the middle of an instruction)
//Address will change after current instruction is done executing //Address will change after current instruction is done executing
@ -1095,24 +1093,34 @@ void Debugger::ResetCounters()
_profiler->Reset(); _profiler->Reset();
} }
void Debugger::UpdateProgramCounter(uint16_t &addr, uint8_t &value)
{
addr = _cpu->GetState().PC;
value = _memoryManager->DebugRead(addr, true);
_cpu->SetDebugPC(addr);
}
void Debugger::ProcessScriptSaveState(uint16_t &addr, uint8_t &value) void Debugger::ProcessScriptSaveState(uint16_t &addr, uint8_t &value)
{ {
if(_hasScript) { if(_hasScript) {
for(shared_ptr<ScriptHost> &script : _scripts) { for(shared_ptr<ScriptHost> &script : _scripts) {
if(script->ProcessSavestate()) { if(script->ProcessSavestate()) {
addr = _cpu->GetState().PC; //Adjust PC and current addr/value if a state was loaded due to a call to loadSavestateAsync
value = _memoryManager->DebugRead(addr, false); UpdateProgramCounter(addr, value);
_cpu->SetDebugPC(addr);
} }
} }
} }
} }
void Debugger::ProcessCpuOperation(uint16_t addr, uint8_t &value, MemoryOperationType type) void Debugger::ProcessCpuOperation(uint16_t &addr, uint8_t &value, MemoryOperationType type)
{ {
if(_hasScript) { if(_hasScript) {
for(shared_ptr<ScriptHost> &script : _scripts) { for(shared_ptr<ScriptHost> &script : _scripts) {
script->ProcessCpuOperation(addr, value, type); script->ProcessCpuOperation(addr, value, type);
if(type == MemoryOperationType::ExecOpCode && script->CheckStateLoadedFlag()) {
//Adjust PC and current addr/value when a state was loaded during a CpuExec callback
UpdateProgramCounter(addr, value);
}
} }
} }
} }

View file

@ -231,8 +231,10 @@ public:
void ResetCounters(); void ResetCounters();
void UpdateProgramCounter(uint16_t &addr, uint8_t &value);
void ProcessScriptSaveState(uint16_t &addr, uint8_t &value); void ProcessScriptSaveState(uint16_t &addr, uint8_t &value);
void ProcessCpuOperation(uint16_t addr, uint8_t &value, MemoryOperationType type); void ProcessCpuOperation(uint16_t &addr, uint8_t &value, MemoryOperationType type);
void ProcessPpuOperation(uint16_t addr, uint8_t &value, MemoryOperationType type); void ProcessPpuOperation(uint16_t addr, uint8_t &value, MemoryOperationType type);
void ProcessEvent(EventType type); void ProcessEvent(EventType type);
}; };

View file

@ -85,5 +85,7 @@ enum class EventType
Irq = 2, Irq = 2,
StartFrame = 3, StartFrame = 3,
EndFrame = 4, EndFrame = 4,
CodeBreak = 5 CodeBreak = 5,
StateLoaded = 6,
StateSaved = 7,
}; };

View file

@ -29,7 +29,7 @@
#define errorCond(cond, text) if(cond) { luaL_error(lua, text); return 0; } #define errorCond(cond, text) if(cond) { luaL_error(lua, text); return 0; }
#define checkparams() if(!l.CheckParamCount()) { return 0; } #define checkparams() if(!l.CheckParamCount()) { return 0; }
#define checkminparams(x) if(!l.CheckParamCount(x)) { return 0; } #define checkminparams(x) if(!l.CheckParamCount(x)) { return 0; }
#define checkstartframe() if(!_context->CheckInStartFrameEvent()) { error("This function cannot be called outside StartFrame event callback"); return 0; } #define checksavestateconditions() if(!_context->CheckInStartFrameEvent() && !_context->CheckInExecOpEvent()) { error("This function must be called inside a StartFrame event callback or a CpuExec memory operation callback"); return 0; }
enum class ExecuteCountType enum class ExecuteCountType
{ {
@ -133,6 +133,8 @@ int LuaApi::GetLibrary(lua_State *lua)
lua_pushintvalue(startFrame, EventType::StartFrame); lua_pushintvalue(startFrame, EventType::StartFrame);
lua_pushintvalue(endFrame, EventType::EndFrame); lua_pushintvalue(endFrame, EventType::EndFrame);
lua_pushintvalue(codeBreak, EventType::CodeBreak); lua_pushintvalue(codeBreak, EventType::CodeBreak);
lua_pushintvalue(stateLoaded, EventType::StateLoaded);
lua_pushintvalue(stateSaved, EventType::StateSaved);
lua_settable(lua, -3); lua_settable(lua, -3);
lua_pushliteral(lua, "executeCountType"); lua_pushliteral(lua, "executeCountType");
@ -253,7 +255,7 @@ int LuaApi::RegisterEventCallback(lua_State *lua)
EventType type = (EventType)l.ReadInteger(); EventType type = (EventType)l.ReadInteger();
int reference = l.GetReference(); int reference = l.GetReference();
checkparams(); checkparams();
errorCond(type < EventType::Reset || type > EventType::CodeBreak, "the specified type is invalid"); errorCond(type < EventType::Reset || type > EventType::StateSaved, "the specified type is invalid");
errorCond(reference == LUA_NOREF, "the specified function could not be found"); errorCond(reference == LUA_NOREF, "the specified function could not be found");
_context->RegisterEventCallback(type, reference); _context->RegisterEventCallback(type, reference);
l.Return(reference); l.Return(reference);
@ -266,7 +268,7 @@ int LuaApi::UnregisterEventCallback(lua_State *lua)
EventType type = (EventType)l.ReadInteger(); EventType type = (EventType)l.ReadInteger();
int reference = l.ReadInteger(); int reference = l.ReadInteger();
checkparams(); checkparams();
errorCond(type < EventType::Reset || type > EventType::CodeBreak, "the specified type is invalid"); errorCond(type < EventType::Reset || type > EventType::StateSaved, "the specified type is invalid");
errorCond(reference == LUA_NOREF, "function reference is invalid"); errorCond(reference == LUA_NOREF, "function reference is invalid");
_context->UnregisterEventCallback(type, reference); _context->UnregisterEventCallback(type, reference);
return l.ReturnCount(); return l.ReturnCount();
@ -438,7 +440,7 @@ int LuaApi::Rewind(lua_State *lua)
LuaCallHelper l(lua); LuaCallHelper l(lua);
int seconds = l.ReadInteger(); int seconds = l.ReadInteger();
checkparams(); checkparams();
checkstartframe(); checksavestateconditions();
errorCond(seconds <= 0, "seconds must be >= 1"); errorCond(seconds <= 0, "seconds must be >= 1");
RewindManager::RewindSeconds(seconds); RewindManager::RewindSeconds(seconds);
return l.ReturnCount(); return l.ReturnCount();
@ -457,7 +459,7 @@ int LuaApi::TakeScreenshot(lua_State *lua)
int LuaApi::SaveSavestate(lua_State *lua) int LuaApi::SaveSavestate(lua_State *lua)
{ {
LuaCallHelper l(lua); LuaCallHelper l(lua);
checkstartframe(); checksavestateconditions();
stringstream ss; stringstream ss;
SaveStateManager::SaveState(ss); SaveStateManager::SaveState(ss);
l.Return(ss.str()); l.Return(ss.str());
@ -469,10 +471,8 @@ int LuaApi::LoadSavestate(lua_State *lua)
LuaCallHelper l(lua); LuaCallHelper l(lua);
string savestate = l.ReadString(); string savestate = l.ReadString();
checkparams(); checkparams();
checkstartframe(); checksavestateconditions();
stringstream ss; l.Return(_context->LoadState(savestate));
ss << savestate;
l.Return(SaveStateManager::LoadState(ss, true));
return l.ReturnCount(); return l.ReturnCount();
} }
@ -492,7 +492,7 @@ int LuaApi::LoadSavestateAsync(lua_State *lua)
int32_t slot = l.ReadInteger(); int32_t slot = l.ReadInteger();
checkparams(); checkparams();
errorCond(slot < 0, "Slot must be >= 0"); errorCond(slot < 0, "Slot must be >= 0");
_context->RequestLoadState(slot); l.Return(_context->RequestLoadState(slot));
return l.ReturnCount(); return l.ReturnCount();
} }

View file

@ -11,6 +11,26 @@ LuaScriptingContext::LuaScriptingContext() { }
LuaScriptingContext::~LuaScriptingContext() LuaScriptingContext::~LuaScriptingContext()
{ {
if(_lua) { if(_lua) {
//Cleanup all references, this is required to prevent crashes that can occur when calling lua_close
std::unordered_set<int> references;
for(int i = (int)CallbackType::CpuRead; i <= (int)CallbackType::PpuWrite; i++) {
for(int addr = 0; addr < 0x10000; addr++ ){
for(int &ref : _callbacks[i][addr]) {
references.emplace(ref);
}
}
}
for(int i = (int)EventType::Reset; i <= (int)EventType::StateSaved; i++) {
for(int &ref : _eventCallbacks[i]) {
references.emplace(ref);
}
}
for(const int &ref : references) {
luaL_unref(_lua, LUA_REGISTRYINDEX, ref);
}
lua_close(_lua); lua_close(_lua);
_lua = nullptr; _lua = nullptr;
} }
@ -42,6 +62,18 @@ bool LuaScriptingContext::LoadScript(string scriptName, string scriptContent, De
return false; return false;
} }
void LuaScriptingContext::UnregisterMemoryCallback(CallbackType type, int startAddr, int endAddr, int reference)
{
ScriptingContext::UnregisterMemoryCallback(type, startAddr, endAddr, reference);
luaL_unref(_lua, LUA_REGISTRYINDEX, reference);
}
void LuaScriptingContext::UnregisterEventCallback(EventType type, int reference)
{
ScriptingContext::UnregisterEventCallback(type, reference);
luaL_unref(_lua, LUA_REGISTRYINDEX, reference);
}
void LuaScriptingContext::InternalCallMemoryCallback(uint16_t addr, uint8_t &value, CallbackType type) void LuaScriptingContext::InternalCallMemoryCallback(uint16_t addr, uint8_t &value, CallbackType type)
{ {
LuaApi::SetContext(this); LuaApi::SetContext(this);

View file

@ -19,4 +19,7 @@ public:
~LuaScriptingContext(); ~LuaScriptingContext();
bool LoadScript(string scriptName, string scriptContent, Debugger* debugger); bool LoadScript(string scriptName, string scriptContent, Debugger* debugger);
void UnregisterMemoryCallback(CallbackType type, int startAddr, int endAddr, int reference) override;
void UnregisterEventCallback(EventType type, int reference) override;
}; };

View file

@ -7,6 +7,7 @@
#include "Console.h" #include "Console.h"
#include "EmulationSettings.h" #include "EmulationSettings.h"
#include "VideoDecoder.h" #include "VideoDecoder.h"
#include "Debugger.h"
const uint32_t SaveStateManager::FileFormatVersion; const uint32_t SaveStateManager::FileFormatVersion;
atomic<uint32_t> SaveStateManager::_lastIndex(1); atomic<uint32_t> SaveStateManager::_lastIndex(1);
@ -54,8 +55,6 @@ bool SaveStateManager::LoadState()
void SaveStateManager::SaveState(ostream &stream) void SaveStateManager::SaveState(ostream &stream)
{ {
Console::Pause();
uint32_t emuVersion = EmulationSettings::GetMesenVersion(); uint32_t emuVersion = EmulationSettings::GetMesenVersion();
stream.write("MST", 3); stream.write("MST", 3);
stream.write((char*)&emuVersion, sizeof(emuVersion)); stream.write((char*)&emuVersion, sizeof(emuVersion));
@ -70,7 +69,6 @@ void SaveStateManager::SaveState(ostream &stream)
stream.write(romName.c_str(), romName.size()); stream.write(romName.c_str(), romName.size());
Console::SaveState(stream); Console::SaveState(stream);
Console::Resume();
} }
bool SaveStateManager::SaveState(string filepath) bool SaveStateManager::SaveState(string filepath)
@ -78,8 +76,16 @@ bool SaveStateManager::SaveState(string filepath)
ofstream file(filepath, ios::out | ios::binary); ofstream file(filepath, ios::out | ios::binary);
if(file) { if(file) {
Console::Pause();
SaveState(file); SaveState(file);
file.close(); file.close();
shared_ptr<Debugger> debugger = Console::GetInstance()->GetDebugger(false);
if(debugger) {
debugger->ProcessEvent(EventType::StateSaved);
}
Console::Resume();
return true; return true;
} }
return false; return false;
@ -141,9 +147,7 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
} }
} }
Console::Pause();
Console::LoadState(stream); Console::LoadState(stream);
Console::Resume();
return true; return true;
} }
@ -157,10 +161,16 @@ bool SaveStateManager::LoadState(string filepath, bool hashCheckRequired)
bool result = false; bool result = false;
if(file.good()) { if(file.good()) {
Console::Pause();
if(LoadState(file, hashCheckRequired)) { if(LoadState(file, hashCheckRequired)) {
result = true; result = true;
} }
file.close(); file.close();
shared_ptr<Debugger> debugger = Console::GetInstance()->GetDebugger(false);
if(debugger) {
debugger->ProcessEvent(EventType::StateLoaded);
}
Console::Resume();
} else { } else {
MessageManager::DisplayMessage("SaveStates", "SaveStateEmpty"); MessageManager::DisplayMessage("SaveStates", "SaveStateEmpty");
} }

View file

@ -62,3 +62,11 @@ bool ScriptHost::ProcessSavestate()
} }
return false; return false;
} }
bool ScriptHost::CheckStateLoadedFlag()
{
if(_context) {
return _context->CheckStateLoadedFlag();
}
return false;
}

View file

@ -23,4 +23,6 @@ public:
void ProcessPpuOperation(uint16_t addr, uint8_t &value, MemoryOperationType type); void ProcessPpuOperation(uint16_t addr, uint8_t &value, MemoryOperationType type);
void ProcessEvent(EventType eventType); void ProcessEvent(EventType eventType);
bool ProcessSavestate(); bool ProcessSavestate();
bool CheckStateLoadedFlag();
}; };

View file

@ -2,7 +2,7 @@
#include <algorithm> #include <algorithm>
#include "ScriptingContext.h" #include "ScriptingContext.h"
#include "DebuggerTypes.h" #include "DebuggerTypes.h"
#include "Console.h" #include "SaveStateManager.h"
string ScriptingContext::_log = ""; string ScriptingContext::_log = "";
@ -52,6 +52,18 @@ bool ScriptingContext::CheckInStartFrameEvent()
return _inStartFrameEvent; return _inStartFrameEvent;
} }
bool ScriptingContext::CheckInExecOpEvent()
{
return _inExecOpEvent;
}
bool ScriptingContext::CheckStateLoadedFlag()
{
bool stateLoaded = _stateLoaded;
_stateLoaded = false;
return stateLoaded;
}
void ScriptingContext::RegisterMemoryCallback(CallbackType type, int startAddr, int endAddr, int reference) void ScriptingContext::RegisterMemoryCallback(CallbackType type, int startAddr, int endAddr, int reference)
{ {
if(endAddr < startAddr) { if(endAddr < startAddr) {
@ -129,7 +141,7 @@ void ScriptingContext::SaveState()
{ {
if(_saveSlot >= 0) { if(_saveSlot >= 0) {
stringstream ss; stringstream ss;
Console::SaveState(ss); SaveStateManager::SaveState(ss);
ss.seekg(0, std::ios::end); ss.seekg(0, std::ios::end);
uint32_t fileSize = (uint32_t)ss.tellg(); uint32_t fileSize = (uint32_t)ss.tellg();
@ -145,13 +157,27 @@ bool ScriptingContext::LoadState()
if(_loadSlot >= 0 && _saveSlotData.find(_loadSlot) != _saveSlotData.end()) { if(_loadSlot >= 0 && _saveSlotData.find(_loadSlot) != _saveSlotData.end()) {
stringstream ss; stringstream ss;
ss << _saveSlotData[_loadSlot]; ss << _saveSlotData[_loadSlot];
Console::LoadState(ss); bool result = SaveStateManager::LoadState(ss);
_loadSlot = -1; _loadSlot = -1;
return true; if(result) {
_stateLoaded = true;
}
return result;
} }
return false; return false;
} }
bool ScriptingContext::LoadState(string stateData)
{
stringstream ss;
ss << stateData;
bool result = SaveStateManager::LoadState(ss);
if(result) {
_stateLoaded = true;
}
return result;
}
bool ScriptingContext::ProcessSavestate() bool ScriptingContext::ProcessSavestate()
{ {
SaveState(); SaveState();

View file

@ -30,12 +30,13 @@ private:
std::unordered_map<int32_t, string> _saveSlotData; std::unordered_map<int32_t, string> _saveSlotData;
int32_t _saveSlot = -1; int32_t _saveSlot = -1;
int32_t _loadSlot = -1; int32_t _loadSlot = -1;
bool _stateLoaded = false;
protected: protected:
string _scriptName; string _scriptName;
vector<int> _callbacks[5][0x10000]; vector<int> _callbacks[5][0x10000];
vector<int> _eventCallbacks[7]; vector<int> _eventCallbacks[8];
virtual void InternalCallMemoryCallback(uint16_t addr, uint8_t &value, CallbackType type) = 0; virtual void InternalCallMemoryCallback(uint16_t addr, uint8_t &value, CallbackType type) = 0;
virtual int InternalCallEventCallback(EventType type) = 0; virtual int InternalCallEventCallback(EventType type) = 0;
@ -52,6 +53,7 @@ public:
bool RequestLoadState(int slot); bool RequestLoadState(int slot);
void SaveState(); void SaveState();
bool LoadState(); bool LoadState();
bool LoadState(string stateData);
string GetSavestateData(int slot); string GetSavestateData(int slot);
void ClearSavestateData(int slot); void ClearSavestateData(int slot);
bool ProcessSavestate(); bool ProcessSavestate();
@ -59,9 +61,11 @@ public:
void CallMemoryCallback(uint16_t addr, uint8_t &value, CallbackType type); void CallMemoryCallback(uint16_t addr, uint8_t &value, CallbackType type);
int CallEventCallback(EventType type); int CallEventCallback(EventType type);
bool CheckInStartFrameEvent(); bool CheckInStartFrameEvent();
bool CheckInExecOpEvent();
bool CheckStateLoadedFlag();
void RegisterMemoryCallback(CallbackType type, int startAddr, int endAddr, int reference); void RegisterMemoryCallback(CallbackType type, int startAddr, int endAddr, int reference);
void UnregisterMemoryCallback(CallbackType type, int startAddr, int endAddr, int reference); virtual void UnregisterMemoryCallback(CallbackType type, int startAddr, int endAddr, int reference);
void RegisterEventCallback(EventType type, int reference); void RegisterEventCallback(EventType type, int reference);
void UnregisterEventCallback(EventType type, int reference); virtual void UnregisterEventCallback(EventType type, int reference);
}; };