diff --git a/Core/LuaApi.cpp b/Core/LuaApi.cpp index c498313a..994f4c12 100644 --- a/Core/LuaApi.cpp +++ b/Core/LuaApi.cpp @@ -19,6 +19,7 @@ #include "PPU.h" #include "CheatManager.h" #include "KeyManager.h" +#include "MemoryAccessCounter.h" #define lua_pushintvalue(name, value) lua_pushliteral(lua, #name); lua_pushinteger(lua, (int)value); lua_settable(lua, -3); #define lua_pushdoublevalue(name, value) lua_pushliteral(lua, #name); lua_pushnumber(lua, (double)value); lua_settable(lua, -3); @@ -96,6 +97,8 @@ int LuaApi::GetLibrary(lua_State *lua) { "setInput", LuaApi::SetInput }, { "addCheat", LuaApi::AddCheat }, { "clearCheats", LuaApi::ClearCheats }, + { "getAccessCounters", LuaApi::GetAccessCounters }, + { "resetAccessCounters", LuaApi::ResetAccessCounters }, { "setState", LuaApi::SetState }, { "getState", LuaApi::GetState }, { "getScriptDataFolder", LuaApi::GetScriptDataFolder }, @@ -132,6 +135,21 @@ int LuaApi::GetLibrary(lua_State *lua) lua_pushintvalue(ppuWrite, CallbackType::PpuWrite); lua_settable(lua, -3); + lua_pushliteral(lua, "counterMemType"); + lua_newtable(lua); + lua_pushintvalue(nesRam, AddressType::InternalRam); + lua_pushintvalue(prgRom, AddressType::PrgRom); + lua_pushintvalue(workRam, AddressType::WorkRam); + lua_pushintvalue(saveRam, AddressType::SaveRam); + lua_settable(lua, -3); + + lua_pushliteral(lua, "counterOpType"); + lua_newtable(lua); + lua_pushintvalue(read, MemoryOperationType::Read); + lua_pushintvalue(write, MemoryOperationType::Write); + lua_pushintvalue(exec, MemoryOperationType::ExecOpCode); + lua_settable(lua, -3); + lua_pushliteral(lua, "eventType"); lua_newtable(lua); lua_pushintvalue(reset, EventType::Reset); @@ -367,7 +385,7 @@ int LuaApi::GetScreenBuffer(lua_State *lua) lua_newtable(lua); for(int y = 0; y < PPU::ScreenHeight; y++) { for(int x = 0; x < PPU::ScreenWidth; x++) { - lua_pushnumber(lua, palette[PPU::GetPixel(x, y) & 0x3F] & 0xFFFFFF); + lua_pushinteger(lua, palette[PPU::GetPixel(x, y) & 0x3F] & 0xFFFFFF); lua_rawseti(lua, -2, (y << 8) + x); } } @@ -657,6 +675,45 @@ int LuaApi::ClearCheats(lua_State *lua) return l.ReturnCount(); } +int LuaApi::GetAccessCounters(lua_State *lua) +{ + LuaCallHelper l(lua); + l.ForceParamCount(2); + MemoryOperationType operationType = (MemoryOperationType)l.ReadInteger(); + AddressType memoryType = (AddressType)l.ReadInteger(); + errorCond(operationType >= MemoryOperationType::ExecOperand, "Invalid operation type"); + errorCond(memoryType >= AddressType::Register, "Invalid memory type"); + checkparams(); + + uint32_t size = 0; + switch(memoryType) { + case AddressType::InternalRam: size = 0x2000; break; + case AddressType::PrgRom: size = _debugger->GetMemoryDumper()->GetMemorySize(DebugMemoryType::PrgRom); break; + case AddressType::WorkRam: size = _debugger->GetMemoryDumper()->GetMemorySize(DebugMemoryType::WorkRam); break; + case AddressType::SaveRam: size = _debugger->GetMemoryDumper()->GetMemorySize(DebugMemoryType::SaveRam); break; + } + + vector counts; + counts.resize(size, 0); + _debugger->GetMemoryAccessCounter()->GetAccessCounts(memoryType, operationType, counts.data(), false); + + lua_newtable(lua); + for(uint32_t i = 0; i < size; i++) { + lua_pushinteger(lua, counts[i]); + lua_rawseti(lua, -2, i); + } + + return 1; +} + +int LuaApi::ResetAccessCounters(lua_State *lua) +{ + LuaCallHelper l(lua); + checkparams(); + _debugger->GetMemoryAccessCounter()->ResetCounts(); + return l.ReturnCount(); +} + int LuaApi::GetScriptDataFolder(lua_State *lua) { LuaCallHelper l(lua); diff --git a/Core/LuaApi.h b/Core/LuaApi.h index 0504c4a9..c0af9f2d 100644 --- a/Core/LuaApi.h +++ b/Core/LuaApi.h @@ -70,6 +70,9 @@ public: static int SetState(lua_State *lua); static int GetState(lua_State *lua); + static int GetAccessCounters(lua_State *lua); + static int ResetAccessCounters(lua_State *lua); + private: static Debugger* _debugger; static MemoryDumper* _memoryDumper; diff --git a/GUI.NET/Debugger/frmScript.cs b/GUI.NET/Debugger/frmScript.cs index da078672..a85c68ed 100644 --- a/GUI.NET/Debugger/frmScript.cs +++ b/GUI.NET/Debugger/frmScript.cs @@ -484,6 +484,9 @@ namespace Mesen.GUI.Debugger new List {"func","emu.getScreenBuffer","emu.getScreenBuffer()","", "*Array* 32-bit integers in ARGB format", "Returns an array of ARGB values for the entire screen (256px by 240px) - can be used with emu.setScreenBuffer() to alter the frame."}, new List {"func","emu.setScreenBuffer","emu.setScreenBuffer(screenBuffer)", "screenBuffer - *Array* An array of 32-bit integers in ARGB format", "","Replaces the current frame with the contents of the specified array."}, + new List {"func","emu.getAccessCounters","emu.getAccessCounters(counterMemType, counterOpType)", "counterMemType - *Enum* A value from the emu.counterMemType enum\ncounterOpType - *Enum* A value from the emu.counterOpType enum\n", "*Array* 32-bit integers", "Returns an array of counters for the specified memory and operation types."}, + new List {"func","emu.resetAccessCounters","emu.resetAccessCounters()", "", "", "Resets all access counters."}, + new List {"func","emu.saveSavestate","emu.saveSavestate()","","*String* A string containing a binary blob representing the emulation's current state.","Creates a savestate and returns it as a binary string. (The savestate is not saved on disk)\n Note: this can only be called from within a “startFrame” event callback or “cpuExec” memory callback."}, new List {"func","emu.loadSavestate","emu.loadSavestate(savestate)","savestate - *String* A binary blob representing a savestate, as returned by saveSavestate()","","Loads the specified savestate.\nNote: this can only be called from within a “startFrame” event callback or “cpuExec” memory callback."}, new List {"func","emu.saveSavestateAsync","emu.saveSavestateAsync()","slotNumber - *Integer* A slot number to which the savestate data will be stored (slotNumber must be >= 0)","","Queues a save savestate request. As soon at the emulator is able to process the request, it will be saved into the specified slot.\nThis API is asynchronous because save states can only be taken in-between 2 CPU instructions, not in the middle of an instruction.\nWhen called while the CPU is in-between 2 instructions (e.g: inside the callback of an cpuExec or startFrame event),\nthe save state will be taken immediately and its data will be available via getSavestateData right after the call to saveSavestateAsync.\nThe savestate can be loaded by calling the loadSavestateAsync function."}, @@ -534,6 +537,15 @@ namespace Mesen.GUI.Debugger new List {"enum","emu.memType.saveRam","Save RAM - Range varies by game","","",""}, new List {"enum","emu.memType.cpuDebug","CPU memory - $0000 to $FFFF","","","Same as \"memType.cpu\" but does NOT cause any side-effects."}, new List {"enum","emu.memType.ppuDebug","PPU memory - $0000 to $3FFF","","","Same as \"memType.ppu\" but does NOT cause any side-effects."}, + new List {"enum","emu.counterMemType","emu.counterMemType.[value]","","","Values:\nnesRam = 0,\nprgRom = 1,\nworkRam = 2,\nsaveRam = 3\n\nUsed by getAccessCounters calls."}, + new List {"enum","emu.counterMemType.nesRam","Returns access counter data for the built-in 2 KB NES RAM","","",""}, + new List {"enum","emu.counterMemType.prgRom","Returns access counter data for PRG ROM","","",""}, + new List {"enum","emu.counterMemType.workRam", "Returns access counter data for Work RAM", "","",""}, + new List {"enum","emu.counterMemType.saveRam", "Returns access counter data for Save RAM", "","",""}, + new List {"enum","emu.counterOpType","emu.counterOpType.[value]","","","Values:\nread = 0,\nwrite = 1,\nexec = 2\n\nUsed by getAccessCounters calls."}, + new List {"enum","emu.counterOpType.read","Returns access counter data for reads","","",""}, + new List {"enum","emu.counterOpType.write","Returns access counter data for writes","","",""}, + new List {"enum","emu.counterOpType.exec", "Returns access counter data for executed bytes", "","",""}, }; }