From 6595ae7837608876bb83b882204f470e36093aed Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Sun, 14 Sep 2025 20:17:01 +0800 Subject: [PATCH] Debugger: Lua - Add `emu.readRegister` and `emu.writeRegister` --- Core/Debugger/DebugTypes.h | 1 + Core/Debugger/Debugger.cpp | 10 ++ Core/Debugger/Debugger.h | 3 + Core/Debugger/IDebugger.h | 3 + Core/Debugger/LuaApi.cpp | 39 ++++++ Core/Debugger/LuaApi.h | 3 + Core/GBA/Debugger/GbaDebugger.cpp | 51 ++++++++ Core/GBA/Debugger/GbaDebugger.h | 2 + Core/GBA/GbaTypes.h | 12 ++ Core/Gameboy/Debugger/GbDebugger.cpp | 45 +++++++ Core/Gameboy/Debugger/GbDebugger.h | 2 + Core/NES/Debugger/NesDebugger.cpp | 29 +++++ Core/NES/Debugger/NesDebugger.h | 2 + Core/PCE/Debugger/PceDebugger.cpp | 29 +++++ Core/PCE/Debugger/PceDebugger.h | 2 + Core/SMS/Debugger/SmsDebugger.cpp | 77 +++++++++++ Core/SMS/Debugger/SmsDebugger.h | 2 + Core/SNES/Coprocessors/GSU/GsuTypes.h | 20 +++ Core/SNES/Coprocessors/ST018/ArmV3Types.h | 11 ++ Core/SNES/Debugger/Cx4Debugger.cpp | 69 ++++++++++ Core/SNES/Debugger/Cx4Debugger.h | 2 + Core/SNES/Debugger/GsuDebugger.cpp | 61 +++++++++ Core/SNES/Debugger/GsuDebugger.h | 2 + Core/SNES/Debugger/NecDspDebugger.cpp | 45 +++++++ Core/SNES/Debugger/NecDspDebugger.h | 2 + Core/SNES/Debugger/SnesDebugger.cpp | 33 +++++ Core/SNES/Debugger/SnesDebugger.h | 2 + Core/SNES/Debugger/SpcDebugger.cpp | 29 +++++ Core/SNES/Debugger/SpcDebugger.h | 2 + Core/SNES/Debugger/St018Debugger.cpp | 51 ++++++++ Core/SNES/Debugger/St018Debugger.h | 2 + Core/Shared/RegisterType.h | 114 +++++++++++++++++ Core/WS/Debugger/WsDebugger.cpp | 62 +++++++++ Core/WS/Debugger/WsDebugger.h | 2 + .../Documentation/LuaDocumentation.json | 121 ++++++++++++++++++ 35 files changed, 942 insertions(+) create mode 100644 Core/Shared/RegisterType.h diff --git a/Core/Debugger/DebugTypes.h b/Core/Debugger/DebugTypes.h index 6e2aae076..106eb0e31 100644 --- a/Core/Debugger/DebugTypes.h +++ b/Core/Debugger/DebugTypes.h @@ -2,6 +2,7 @@ #include "pch.h" #include "Shared/MemoryOperationType.h" #include "Shared/CpuType.h" +#include "Shared/RegisterType.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/AddressInfo.h" diff --git a/Core/Debugger/Debugger.cpp b/Core/Debugger/Debugger.cpp index 46e26b70e..3a44eba00 100644 --- a/Core/Debugger/Debugger.cpp +++ b/Core/Debugger/Debugger.cpp @@ -830,6 +830,16 @@ void Debugger::BreakImmediately(CpuType sourceCpu, BreakSource source) } } +optional Debugger::GetRegisterValue(RegisterType registerType, CpuType cpuType) +{ + return _debuggers[(int)cpuType].Debugger->GetRegisterValue(registerType); +} + +bool Debugger::SetRegisterValue(RegisterType registerType, CpuType cpuType, uint32_t value) +{ + return _debuggers[(int)cpuType].Debugger->SetRegisterValue(registerType, value); +} + void Debugger::GetCpuState(BaseState &dstState, CpuType cpuType) { BaseState& srcState = GetCpuStateRef(cpuType); diff --git a/Core/Debugger/Debugger.h b/Core/Debugger/Debugger.h index 3f78502f8..4025a23cf 100644 --- a/Core/Debugger/Debugger.h +++ b/Core/Debugger/Debugger.h @@ -148,6 +148,9 @@ class Debugger void SleepUntilResume(CpuType sourceCpu, BreakSource source, MemoryOperationInfo* operation = nullptr, int breakpointId = -1); + optional GetRegisterValue(RegisterType registerType, CpuType cpuType); + bool SetRegisterValue(RegisterType registerType, CpuType cpuType, uint32_t value); + void GetCpuState(BaseState& dstState, CpuType cpuType); void SetCpuState(BaseState& srcState, CpuType cpuType); BaseState& GetCpuStateRef(CpuType cpuType); diff --git a/Core/Debugger/IDebugger.h b/Core/Debugger/IDebugger.h index 28a0fa8e0..fc19fdac1 100644 --- a/Core/Debugger/IDebugger.h +++ b/Core/Debugger/IDebugger.h @@ -76,6 +76,9 @@ class IDebugger virtual void GetRomHeader(uint8_t* headerData, uint32_t& size) {} + virtual optional GetRegisterValue(RegisterType registerType) { return {}; } + virtual bool SetRegisterValue(RegisterType registerType, uint32_t value) { return false; } + virtual BaseState& GetState() = 0; virtual void GetPpuState(BaseState& state) {}; virtual void SetPpuState(BaseState& state) {}; diff --git a/Core/Debugger/LuaApi.cpp b/Core/Debugger/LuaApi.cpp index 4deebdeda..db25ede40 100644 --- a/Core/Debugger/LuaApi.cpp +++ b/Core/Debugger/LuaApi.cpp @@ -98,6 +98,9 @@ int LuaApi::GetLibrary(lua_State *lua) { "readWord", LuaApi::ReadMemory16 }, //for backward compatibility { "writeWord", LuaApi::WriteMemory16 }, //for backward compatibility + + { "readRegister", LuaApi::ReadRegister }, + { "writeRegister", LuaApi::WriteRegister }, { "convertAddress", LuaApi::ConvertAddress }, { "getLabelAddress", LuaApi::GetLabelAddress }, @@ -181,6 +184,7 @@ int LuaApi::GetLibrary(lua_State *lua) GenerateEnumDefinition(lua, "cheatType"); GenerateEnumDefinition(lua, "counterType"); GenerateEnumDefinition(lua, "cpuType"); + GenerateEnumDefinition(lua, "registerType"); GenerateEnumDefinition(lua, "drawSurface"); GenerateEnumDefinition(lua, "eventType", { EventType::LastValue }); GenerateEnumDefinition(lua, "stepType", { StepType::StepBack }); @@ -308,6 +312,24 @@ int LuaApi::ReadMemory32(lua_State* lua) return l.ReturnCount(); } +int LuaApi::ReadRegister(lua_State *lua) +{ + LuaCallHelper l(lua); + l.ForceParamCount(2); + CpuType cpuType = (CpuType)l.ReadInteger((uint32_t)_context->GetDefaultCpuType()); + RegisterType registerType = (RegisterType)l.ReadInteger(); + checkminparams(1); + checkEnum(CpuType, cpuType, "invalid cpu type"); + checkEnum(RegisterType, registerType, "invalid register type"); + + if(optional value = _debugger->GetRegisterValue(registerType, cpuType)) { + l.Return(*value); + return l.ReturnCount(); + } else { + error("unsupported register type for the given CPU"); + } +} + int LuaApi::WriteMemory16(lua_State *lua) { LuaCallHelper l(lua); @@ -339,6 +361,23 @@ int LuaApi::WriteMemory32(lua_State* lua) return l.ReturnCount(); } +int LuaApi::WriteRegister(lua_State *lua) +{ + LuaCallHelper l(lua); + l.ForceParamCount(3); + CpuType cpuType = (CpuType)l.ReadInteger((uint32_t)_context->GetDefaultCpuType()); + uint32_t value = l.ReadInteger(); + RegisterType registerType = (RegisterType)l.ReadInteger(); + checkminparams(2); + checkEnum(CpuType, cpuType, "invalid cpu type"); + checkEnum(RegisterType, registerType, "invalid register type"); + + if(!_debugger->SetRegisterValue(registerType, cpuType, value)) { + error("unsupported register type for the given CPU"); + } + return l.ReturnCount(); +} + int LuaApi::ConvertAddress(lua_State *lua) { LuaCallHelper l(lua); diff --git a/Core/Debugger/LuaApi.h b/Core/Debugger/LuaApi.h index 9a052abe6..c1bfab949 100644 --- a/Core/Debugger/LuaApi.h +++ b/Core/Debugger/LuaApi.h @@ -33,6 +33,9 @@ class LuaApi static int ReadMemory32(lua_State* lua); static int WriteMemory32(lua_State* lua); + static int ReadRegister(lua_State* lua); + static int WriteRegister(lua_State* lua); + static int GetLabelAddress(lua_State* lua); static int ConvertAddress(lua_State *lua); diff --git a/Core/GBA/Debugger/GbaDebugger.cpp b/Core/GBA/Debugger/GbaDebugger.cpp index d1dc0e365..c51a86040 100644 --- a/Core/GBA/Debugger/GbaDebugger.cpp +++ b/Core/GBA/Debugger/GbaDebugger.cpp @@ -400,6 +400,57 @@ BreakpointManager* GbaDebugger::GetBreakpointManager() return _breakpointManager.get(); } +optional GbaDebugger::GetRegisterValue(RegisterType registerType) +{ + GbaCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::R0: return s.R[0]; + case RegisterType::R1: return s.R[1]; + case RegisterType::R2: return s.R[2]; + case RegisterType::R3: return s.R[3]; + case RegisterType::R4: return s.R[4]; + case RegisterType::R5: return s.R[5]; + case RegisterType::R6: return s.R[6]; + case RegisterType::R7: return s.R[7]; + case RegisterType::R8: return s.R[8]; + case RegisterType::R9: return s.R[9]; + case RegisterType::R10: return s.R[10]; + case RegisterType::R11: return s.R[11]; + case RegisterType::R12: return s.R[12]; + case RegisterType::R13: case RegisterType::Sp: return s.R[13]; + case RegisterType::R14: case RegisterType::Lr: return s.R[14]; + case RegisterType::R15: case RegisterType::Pc: return s.R[15]; + case RegisterType::Cpsr: return s.CPSR.ToInt32(); + } +} + +bool GbaDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + GbaCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return false; + case RegisterType::R0: s.R[0] = value; break; + case RegisterType::R1: s.R[1] = value; break; + case RegisterType::R2: s.R[2] = value; break; + case RegisterType::R3: s.R[3] = value; break; + case RegisterType::R4: s.R[4] = value; break; + case RegisterType::R5: s.R[5] = value; break; + case RegisterType::R6: s.R[6] = value; break; + case RegisterType::R7: s.R[7] = value; break; + case RegisterType::R8: s.R[8] = value; break; + case RegisterType::R9: s.R[9] = value; break; + case RegisterType::R10: s.R[10] = value; break; + case RegisterType::R11: s.R[11] = value; break; + case RegisterType::R12: s.R[12] = value; break; + case RegisterType::R13: case RegisterType::Sp: s.R[13] = value; break; + case RegisterType::R14: case RegisterType::Lr: s.R[14] = value; break; + case RegisterType::R15: case RegisterType::Pc: s.R[15] = value; break; + case RegisterType::Cpsr: s.CPSR.SetFromInt32(value); break; + } + return true; +} + BaseState& GbaDebugger::GetState() { return _cpu->GetState(); diff --git a/Core/GBA/Debugger/GbaDebugger.h b/Core/GBA/Debugger/GbaDebugger.h index fb073c291..1b212b997 100644 --- a/Core/GBA/Debugger/GbaDebugger.h +++ b/Core/GBA/Debugger/GbaDebugger.h @@ -93,6 +93,8 @@ class GbaDebugger final : public IDebugger ITraceLogger* GetTraceLogger() override; PpuTools* GetPpuTools() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; void GetPpuState(BaseState& state) override; void SetPpuState(BaseState& state) override; diff --git a/Core/GBA/GbaTypes.h b/Core/GBA/GbaTypes.h index 8fbd8d520..4c6749586 100644 --- a/Core/GBA/GbaTypes.h +++ b/Core/GBA/GbaTypes.h @@ -69,6 +69,18 @@ struct GbaCpuFlags (uint8_t)Mode ); } + + void SetFromInt32(uint32_t Bits) + { + Negative = (Bits & (1 << 31)) != 0; + Zero = (Bits & (1 << 30)) != 0; + Carry = (Bits & (1 << 29)) != 0; + Overflow = (Bits & (1 << 28)) != 0; + IrqDisable = (Bits & (1 << 7)) != 0; + FiqDisable = (Bits & (1 << 6)) != 0; + Thumb = (Bits & (1 << 5)) != 0; + Mode = (GbaCpuMode)((uint8_t)Bits); + } }; struct GbaInstructionData diff --git a/Core/Gameboy/Debugger/GbDebugger.cpp b/Core/Gameboy/Debugger/GbDebugger.cpp index 346c4adf4..26b4031c7 100644 --- a/Core/Gameboy/Debugger/GbDebugger.cpp +++ b/Core/Gameboy/Debugger/GbDebugger.cpp @@ -410,6 +410,51 @@ BreakpointManager* GbDebugger::GetBreakpointManager() return _breakpointManager.get(); } +optional GbDebugger::GetRegisterValue(RegisterType registerType) +{ + GbCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::A: return s.A; + case RegisterType::B: return s.B; + case RegisterType::C: return s.C; + case RegisterType::D: return s.D; + case RegisterType::E: return s.E; + case RegisterType::F: return s.Flags; + case RegisterType::H: return s.H; + case RegisterType::L: return s.L; + case RegisterType::Af: return (s.A << 8) | s.Flags; + case RegisterType::Bc: return (s.B << 8) | s.C; + case RegisterType::De: return (s.D << 8) | s.E; + case RegisterType::Hl: return (s.H << 8) | s.L; + case RegisterType::Sp: return s.SP; + case RegisterType::Pc: return s.PC; + } +} + +bool GbDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + GbCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return false; + case RegisterType::A: s.A = (uint8_t)value; break; + case RegisterType::B: s.B = (uint8_t)value; break; + case RegisterType::C: s.C = (uint8_t)value; break; + case RegisterType::D: s.D = (uint8_t)value; break; + case RegisterType::E: s.E = (uint8_t)value; break; + case RegisterType::F: s.Flags = (uint8_t)value; break; + case RegisterType::H: s.H = (uint8_t)value; break; + case RegisterType::L: s.L = (uint8_t)value; break; + case RegisterType::Af: s.A = (uint8_t)(value >> 8); s.Flags = (uint8_t)value; break; + case RegisterType::Bc: s.B = (uint8_t)(value >> 8); s.C = (uint8_t)value; break; + case RegisterType::De: s.D = (uint8_t)(value >> 8); s.E = (uint8_t)value; break; + case RegisterType::Hl: s.H = (uint8_t)(value >> 8); s.L = (uint8_t)value; break; + case RegisterType::Sp: s.SP = (uint16_t)value; break; + case RegisterType::Pc: s.PC = (uint16_t)value; break; + } + return true; +} + BaseState& GbDebugger::GetState() { return _cpu->GetState(); diff --git a/Core/Gameboy/Debugger/GbDebugger.h b/Core/Gameboy/Debugger/GbDebugger.h index 8863ecb6d..595a31bcc 100644 --- a/Core/Gameboy/Debugger/GbDebugger.h +++ b/Core/Gameboy/Debugger/GbDebugger.h @@ -90,6 +90,8 @@ class GbDebugger final : public IDebugger ITraceLogger* GetTraceLogger() override; PpuTools* GetPpuTools() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; void GetPpuState(BaseState& state) override; void SetPpuState(BaseState& state) override; diff --git a/Core/NES/Debugger/NesDebugger.cpp b/Core/NES/Debugger/NesDebugger.cpp index d813a7824..f88724e74 100644 --- a/Core/NES/Debugger/NesDebugger.cpp +++ b/Core/NES/Debugger/NesDebugger.cpp @@ -457,6 +457,35 @@ BaseEventManager* NesDebugger::GetEventManager() return _eventManager.get(); } +optional NesDebugger::GetRegisterValue(RegisterType registerType) +{ + NesCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::A: return s.A; + case RegisterType::X: return s.X; + case RegisterType::Y: return s.Y; + case RegisterType::Sp: return s.SP; + case RegisterType::Ps: return s.PS; + case RegisterType::Pc: return s.PC; + } +} + +bool NesDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + NesCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return false; + case RegisterType::A: s.A = (uint8_t)value; break; + case RegisterType::X: s.X = (uint8_t)value; break; + case RegisterType::Y: s.Y = (uint8_t)value; break; + case RegisterType::Sp: s.SP = (uint8_t)value; break; + case RegisterType::Ps: s.PS = (uint8_t)value; break; + case RegisterType::Pc: s.PC = (uint16_t)value; break; + } + return true; +} + BaseState& NesDebugger::GetState() { return _cpu->GetState(); diff --git a/Core/NES/Debugger/NesDebugger.h b/Core/NES/Debugger/NesDebugger.h index 92ade571f..a814623d5 100644 --- a/Core/NES/Debugger/NesDebugger.h +++ b/Core/NES/Debugger/NesDebugger.h @@ -99,6 +99,8 @@ class NesDebugger final : public IDebugger IAssembler* GetAssembler() override; BaseEventManager* GetEventManager() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; void GetPpuState(BaseState& state) override; void SetPpuState(BaseState& state) override; diff --git a/Core/PCE/Debugger/PceDebugger.cpp b/Core/PCE/Debugger/PceDebugger.cpp index 362fea7d7..96c5c3fc4 100644 --- a/Core/PCE/Debugger/PceDebugger.cpp +++ b/Core/PCE/Debugger/PceDebugger.cpp @@ -421,6 +421,35 @@ BaseEventManager* PceDebugger::GetEventManager() return _eventManager.get(); } +optional PceDebugger::GetRegisterValue(RegisterType registerType) +{ + PceCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::A: return s.A; + case RegisterType::X: return s.X; + case RegisterType::Y: return s.Y; + case RegisterType::Sp: return s.SP; + case RegisterType::Ps: return s.PS; + case RegisterType::Pc: return s.PC; + } +} + +bool PceDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + PceCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return false; + case RegisterType::A: s.A = (uint8_t)value; break; + case RegisterType::X: s.X = (uint8_t)value; break; + case RegisterType::Y: s.Y = (uint8_t)value; break; + case RegisterType::Sp: s.SP = (uint8_t)value; break; + case RegisterType::Ps: s.PS = (uint8_t)value; break; + case RegisterType::Pc: s.PC = (uint16_t)value; break; + } + return true; +} + BaseState& PceDebugger::GetState() { return _cpu->GetState(); diff --git a/Core/PCE/Debugger/PceDebugger.h b/Core/PCE/Debugger/PceDebugger.h index 05add668e..515354388 100644 --- a/Core/PCE/Debugger/PceDebugger.h +++ b/Core/PCE/Debugger/PceDebugger.h @@ -102,6 +102,8 @@ class PceDebugger final : public IDebugger PceConsole* GetConsole() { return _console; } + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; void GetPpuState(BaseState& state) override; void SetPpuState(BaseState& state) override; diff --git a/Core/SMS/Debugger/SmsDebugger.cpp b/Core/SMS/Debugger/SmsDebugger.cpp index 2da47046f..d268b4b02 100644 --- a/Core/SMS/Debugger/SmsDebugger.cpp +++ b/Core/SMS/Debugger/SmsDebugger.cpp @@ -409,6 +409,83 @@ BreakpointManager* SmsDebugger::GetBreakpointManager() return _breakpointManager.get(); } +optional SmsDebugger::GetRegisterValue(RegisterType registerType) +{ + SmsCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::A: return s.A; + case RegisterType::B: return s.B; + case RegisterType::C: return s.C; + case RegisterType::D: return s.D; + case RegisterType::E: return s.E; + case RegisterType::F: return s.Flags; + case RegisterType::H: return s.H; + case RegisterType::L: return s.L; + case RegisterType::Af: return (s.A << 8) | s.Flags; + case RegisterType::Bc: return (s.B << 8) | s.C; + case RegisterType::De: return (s.D << 8) | s.E; + case RegisterType::Hl: return (s.H << 8) | s.L; + case RegisterType::AltA: return s.AltA; + case RegisterType::AltB: return s.AltB; + case RegisterType::AltC: return s.AltC; + case RegisterType::AltD: return s.AltD; + case RegisterType::AltE: return s.AltE; + case RegisterType::AltF: return s.AltFlags; + case RegisterType::AltH: return s.AltH; + case RegisterType::AltL: return s.AltL; + case RegisterType::AltAf: return (s.AltA << 8) | s.AltFlags; + case RegisterType::AltBc: return (s.AltB << 8) | s.AltC; + case RegisterType::AltDe: return (s.AltD << 8) | s.AltE; + case RegisterType::AltHl: return (s.AltH << 8) | s.AltL; + case RegisterType::Ix: return (s.IXH << 8) | s.IXL; + case RegisterType::Iy: return (s.IYH << 8) | s.IYL; + case RegisterType::I: return s.I; + case RegisterType::R: return s.R; + case RegisterType::Sp: return s.SP; + case RegisterType::Pc: return s.PC; + } +} + +bool SmsDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + SmsCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return false; + case RegisterType::A: s.A = (uint8_t)value; break; + case RegisterType::B: s.B = (uint8_t)value; break; + case RegisterType::C: s.C = (uint8_t)value; break; + case RegisterType::D: s.D = (uint8_t)value; break; + case RegisterType::E: s.E = (uint8_t)value; break; + case RegisterType::F: s.Flags = (uint8_t)value; break; + case RegisterType::H: s.H = (uint8_t)value; break; + case RegisterType::L: s.L = (uint8_t)value; break; + case RegisterType::Af: s.A = (uint8_t)(value >> 8); s.Flags = (uint8_t)value; break; + case RegisterType::Bc: s.B = (uint8_t)(value >> 8); s.C = (uint8_t)value; break; + case RegisterType::De: s.D = (uint8_t)(value >> 8); s.E = (uint8_t)value; break; + case RegisterType::Hl: s.H = (uint8_t)(value >> 8); s.L = (uint8_t)value; break; + case RegisterType::AltA: s.AltA = (uint8_t)value; break; + case RegisterType::AltB: s.AltB = (uint8_t)value; break; + case RegisterType::AltC: s.AltC = (uint8_t)value; break; + case RegisterType::AltD: s.AltD = (uint8_t)value; break; + case RegisterType::AltE: s.AltE = (uint8_t)value; break; + case RegisterType::AltF: s.AltFlags = (uint8_t)value; break; + case RegisterType::AltH: s.AltH = (uint8_t)value; break; + case RegisterType::AltL: s.AltL = (uint8_t)value; break; + case RegisterType::AltAf: s.AltA = (uint8_t)(value >> 8); s.AltFlags = (uint8_t)value; break; + case RegisterType::AltBc: s.AltB = (uint8_t)(value >> 8); s.AltC = (uint8_t)value; break; + case RegisterType::AltDe: s.AltD = (uint8_t)(value >> 8); s.AltE = (uint8_t)value; break; + case RegisterType::AltHl: s.AltH = (uint8_t)(value >> 8); s.AltL = (uint8_t)value; break; + case RegisterType::Ix: s.IXH = (uint8_t)(value >> 8); s.IXL = (uint8_t)value; break; + case RegisterType::Iy: s.IYH = (uint8_t)(value >> 8); s.IYL = (uint8_t)value; break; + case RegisterType::I: s.I = (uint8_t)value; break; + case RegisterType::R: s.R = (uint8_t)value; break; + case RegisterType::Sp: s.SP = (uint16_t)value; break; + case RegisterType::Pc: s.PC = (uint16_t)value; break; + } + return true; +} + BaseState& SmsDebugger::GetState() { return _cpu->GetState(); diff --git a/Core/SMS/Debugger/SmsDebugger.h b/Core/SMS/Debugger/SmsDebugger.h index 2ea43369e..dbdc66965 100644 --- a/Core/SMS/Debugger/SmsDebugger.h +++ b/Core/SMS/Debugger/SmsDebugger.h @@ -96,6 +96,8 @@ class SmsDebugger final : public IDebugger ITraceLogger* GetTraceLogger() override; PpuTools* GetPpuTools() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; void GetPpuState(BaseState& state) override; void SetPpuState(BaseState& state) override; diff --git a/Core/SNES/Coprocessors/GSU/GsuTypes.h b/Core/SNES/Coprocessors/GSU/GsuTypes.h index a83a85236..c81294bdf 100644 --- a/Core/SNES/Coprocessors/GSU/GsuTypes.h +++ b/Core/SNES/Coprocessors/GSU/GsuTypes.h @@ -40,6 +40,26 @@ struct GsuFlags (Irq << 7) ); } + + void SetFlagsLow(uint8_t Bits) + { + Zero = (Bits & (1 << 1)) != 0; + Carry = (Bits & (1 << 2)) != 0; + Sign = (Bits & (1 << 3)) != 0; + Overflow = (Bits & (1 << 4)) != 0; + Running = (Bits & (1 << 5)) != 0; + RomReadPending = (Bits & (1 << 6)) != 0; + } + + void SetFlagsHigh(uint8_t Bits) + { + Alt1 = (Bits & (1 << 0)) != 0; + Alt2 = (Bits & (1 << 1)) != 0; + ImmLow = (Bits & (1 << 2)) != 0; + ImmHigh = (Bits & (1 << 3)) != 0; + Prefix = (Bits & (1 << 4)) != 0; + Irq = (Bits & (1 << 7)) != 0; + } }; struct GsuPixelCache diff --git a/Core/SNES/Coprocessors/ST018/ArmV3Types.h b/Core/SNES/Coprocessors/ST018/ArmV3Types.h index 302a20517..4dadcc707 100644 --- a/Core/SNES/Coprocessors/ST018/ArmV3Types.h +++ b/Core/SNES/Coprocessors/ST018/ArmV3Types.h @@ -63,6 +63,17 @@ struct ArmV3CpuFlags (uint8_t)Mode ); } + + void SetFromInt32(uint32_t Bits) + { + Negative = (Bits & (1 << 31)) != 0; + Zero = (Bits & (1 << 30)) != 0; + Carry = (Bits & (1 << 29)) != 0; + Overflow = (Bits & (1 << 28)) != 0; + IrqDisable = (Bits & (1 << 7)) != 0; + FiqDisable = (Bits & (1 << 6)) != 0; + Mode = (ArmV3CpuMode)((uint8_t)Bits); + } }; struct ArmV3InstructionData diff --git a/Core/SNES/Debugger/Cx4Debugger.cpp b/Core/SNES/Debugger/Cx4Debugger.cpp index 5f72d7ef7..eec1c3dd2 100644 --- a/Core/SNES/Debugger/Cx4Debugger.cpp +++ b/Core/SNES/Debugger/Cx4Debugger.cpp @@ -226,6 +226,75 @@ BaseEventManager* Cx4Debugger::GetEventManager() return nullptr; } +optional Cx4Debugger::GetRegisterValue(RegisterType registerType) +{ + Cx4State& s = _cx4->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::R0: return s.Regs[0]; + case RegisterType::R1: return s.Regs[1]; + case RegisterType::R2: return s.Regs[2]; + case RegisterType::R3: return s.Regs[3]; + case RegisterType::R4: return s.Regs[4]; + case RegisterType::R5: return s.Regs[5]; + case RegisterType::R6: return s.Regs[6]; + case RegisterType::R7: return s.Regs[7]; + case RegisterType::R8: return s.Regs[8]; + case RegisterType::R9: return s.Regs[9]; + case RegisterType::R10: return s.Regs[10]; + case RegisterType::R11: return s.Regs[11]; + case RegisterType::R12: return s.Regs[12]; + case RegisterType::R13: return s.Regs[13]; + case RegisterType::R14: return s.Regs[14]; + case RegisterType::R15: return s.Regs[15]; + case RegisterType::Pb: return s.PB; + case RegisterType::Pc: return s.PC; + case RegisterType::A: return s.A; + case RegisterType::P: return s.P; + case RegisterType::Sp: return s.SP; + case RegisterType::Ml: return (uint32_t)(s.Mult & 0xFFFFFF); + case RegisterType::Mh: return (uint32_t)(s.Mult >> 24); + case RegisterType::Mdr: return s.MemoryDataReg; + case RegisterType::Mar: return s.MemoryAddressReg; + case RegisterType::Dpr: return s.DataPointerReg; + } +} + +bool Cx4Debugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + Cx4State& s = _cx4->GetState(); + switch(registerType) { + default: return false; + case RegisterType::R0: s.Regs[0] = value & 0xFFFFFF; break; + case RegisterType::R1: s.Regs[1] = value & 0xFFFFFF; break; + case RegisterType::R2: s.Regs[2] = value & 0xFFFFFF; break; + case RegisterType::R3: s.Regs[3] = value & 0xFFFFFF; break; + case RegisterType::R4: s.Regs[4] = value & 0xFFFFFF; break; + case RegisterType::R5: s.Regs[5] = value & 0xFFFFFF; break; + case RegisterType::R6: s.Regs[6] = value & 0xFFFFFF; break; + case RegisterType::R7: s.Regs[7] = value & 0xFFFFFF; break; + case RegisterType::R8: s.Regs[8] = value & 0xFFFFFF; break; + case RegisterType::R9: s.Regs[9] = value & 0xFFFFFF; break; + case RegisterType::R10: s.Regs[10] = value & 0xFFFFFF; break; + case RegisterType::R11: s.Regs[11] = value & 0xFFFFFF; break; + case RegisterType::R12: s.Regs[12] = value & 0xFFFFFF; break; + case RegisterType::R13: s.Regs[13] = value & 0xFFFFFF; break; + case RegisterType::R14: s.Regs[14] = value & 0xFFFFFF; break; + case RegisterType::R15: s.Regs[15] = value & 0xFFFFFF; break; + case RegisterType::Pb: s.PB = (uint16_t)value; break; + case RegisterType::Pc: s.PC = (uint8_t)value; break; + case RegisterType::A: s.A = value & 0xFFFFFF; break; + case RegisterType::P: s.P = (uint16_t)value; break; + case RegisterType::Sp: s.SP = (uint8_t)value; break; + case RegisterType::Ml: s.Mult = (s.Mult & 0xFFFFFF000000) | (value & 0xFFFFFF); break; + case RegisterType::Mh: s.Mult = (s.Mult & 0xFFFFFF) | ((uint64_t)(value & 0xFFFFFF) << 24); break; + case RegisterType::Mdr: s.MemoryDataReg = value & 0xFFFFFF; break; + case RegisterType::Mar: s.MemoryAddressReg = value & 0xFFFFFF; break; + case RegisterType::Dpr: s.DataPointerReg = value & 0xFFFFFF; break; + } + return true; +} + BaseState& Cx4Debugger::GetState() { return _cx4->GetState(); diff --git a/Core/SNES/Debugger/Cx4Debugger.h b/Core/SNES/Debugger/Cx4Debugger.h index 6710bd413..6b8539661 100644 --- a/Core/SNES/Debugger/Cx4Debugger.h +++ b/Core/SNES/Debugger/Cx4Debugger.h @@ -57,5 +57,7 @@ class Cx4Debugger final : public IDebugger BaseEventManager* GetEventManager() override; ITraceLogger* GetTraceLogger() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/SNES/Debugger/GsuDebugger.cpp b/Core/SNES/Debugger/GsuDebugger.cpp index 05deacb01..72daa6119 100644 --- a/Core/SNES/Debugger/GsuDebugger.cpp +++ b/Core/SNES/Debugger/GsuDebugger.cpp @@ -185,6 +185,67 @@ BaseEventManager* GsuDebugger::GetEventManager() return nullptr; } +optional GsuDebugger::GetRegisterValue(RegisterType registerType) +{ + GsuState& s = _gsu->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::R0: return s.R[0]; + case RegisterType::R1: return s.R[1]; + case RegisterType::R2: return s.R[2]; + case RegisterType::R3: return s.R[3]; + case RegisterType::R4: return s.R[4]; + case RegisterType::R5: return s.R[5]; + case RegisterType::R6: return s.R[6]; + case RegisterType::R7: return s.R[7]; + case RegisterType::R8: return s.R[8]; + case RegisterType::R9: return s.R[9]; + case RegisterType::R10: return s.R[10]; + case RegisterType::R11: return s.R[11]; + case RegisterType::R12: return s.R[12]; + case RegisterType::R13: return s.R[13]; + case RegisterType::R14: return s.R[14]; + case RegisterType::R15: return s.R[15]; + case RegisterType::SrcReg: return s.SrcReg; + case RegisterType::DstReg: return s.DestReg; + case RegisterType::Sfr: return (s.SFR.GetFlagsHigh() << 8) | s.SFR.GetFlagsLow(); + case RegisterType::Pbr: return s.ProgramBank; + case RegisterType::Rombr: return s.RomBank; + case RegisterType::Rambr: return s.RamBank; + } +} + +bool GsuDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + GsuState& s = _gsu->GetState(); + switch(registerType) { + default: return false; + case RegisterType::R0: s.R[0] = (uint16_t)value; break; + case RegisterType::R1: s.R[1] = (uint16_t)value; break; + case RegisterType::R2: s.R[2] = (uint16_t)value; break; + case RegisterType::R3: s.R[3] = (uint16_t)value; break; + case RegisterType::R4: s.R[4] = (uint16_t)value; break; + case RegisterType::R5: s.R[5] = (uint16_t)value; break; + case RegisterType::R6: s.R[6] = (uint16_t)value; break; + case RegisterType::R7: s.R[7] = (uint16_t)value; break; + case RegisterType::R8: s.R[8] = (uint16_t)value; break; + case RegisterType::R9: s.R[9] = (uint16_t)value; break; + case RegisterType::R10: s.R[10] = (uint16_t)value; break; + case RegisterType::R11: s.R[11] = (uint16_t)value; break; + case RegisterType::R12: s.R[12] = (uint16_t)value; break; + case RegisterType::R13: s.R[13] = (uint16_t)value; break; + case RegisterType::R14: s.R[14] = (uint16_t)value; break; + case RegisterType::R15: s.R[15] = (uint16_t)value; break; + case RegisterType::SrcReg: s.SrcReg = (uint8_t)value; break; + case RegisterType::DstReg: s.DestReg = (uint8_t)value; break; + case RegisterType::Sfr: s.SFR.SetFlagsLow((uint8_t)value); s.SFR.SetFlagsHigh((uint8_t)(value >> 8)); break; + case RegisterType::Pbr: s.ProgramBank = (uint8_t)value; break; + case RegisterType::Rombr: s.RomBank = (uint8_t)value; break; + case RegisterType::Rambr: s.RamBank = (uint8_t)value; break; + } + return true; +} + BaseState& GsuDebugger::GetState() { return _gsu->GetState(); diff --git a/Core/SNES/Debugger/GsuDebugger.h b/Core/SNES/Debugger/GsuDebugger.h index 7c85f87ee..2936a76fd 100644 --- a/Core/SNES/Debugger/GsuDebugger.h +++ b/Core/SNES/Debugger/GsuDebugger.h @@ -54,5 +54,7 @@ class GsuDebugger final : public IDebugger BaseEventManager* GetEventManager() override; ITraceLogger* GetTraceLogger() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/SNES/Debugger/NecDspDebugger.cpp b/Core/SNES/Debugger/NecDspDebugger.cpp index 476bb2112..c069081aa 100644 --- a/Core/SNES/Debugger/NecDspDebugger.cpp +++ b/Core/SNES/Debugger/NecDspDebugger.cpp @@ -198,6 +198,51 @@ BaseEventManager* NecDspDebugger::GetEventManager() return nullptr; } +optional NecDspDebugger::GetRegisterValue(RegisterType registerType) +{ + NecDspState& s = _dsp->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::A: return s.A; + case RegisterType::B: return s.B; + case RegisterType::Tr: return s.TR; + case RegisterType::Trb: return s.TRB; + case RegisterType::Rp: return s.RP; + case RegisterType::Dp: return s.DP; + case RegisterType::Dr: return s.DR; + case RegisterType::Sr: return s.SR; + case RegisterType::K: return s.K; + case RegisterType::L: return s.L; + case RegisterType::M: return s.M; + case RegisterType::N: return s.N; + case RegisterType::Sp: return s.SP; + case RegisterType::Pc: return s.PC; + } +} + +bool NecDspDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + NecDspState& s = _dsp->GetState(); + switch(registerType) { + default: return false; + case RegisterType::A: s.A = (uint16_t)value; break; + case RegisterType::B: s.B = (uint16_t)value; break; + case RegisterType::Tr: s.TR = (uint16_t)value; break; + case RegisterType::Trb: s.TRB = (uint16_t)value; break; + case RegisterType::Rp: s.RP = (uint16_t)value; break; + case RegisterType::Dp: s.DP = (uint16_t)value; break; + case RegisterType::Dr: s.DR = (uint16_t)value; break; + case RegisterType::Sr: s.SR = (uint16_t)value; break; + case RegisterType::K: s.K = (uint16_t)value; break; + case RegisterType::L: s.L = (uint16_t)value; break; + case RegisterType::M: s.M = (uint16_t)value; break; + case RegisterType::N: s.N = (uint16_t)value; break; + case RegisterType::Sp: s.SP = (uint8_t)value; break; + case RegisterType::Pc: s.PC = (uint16_t)value; break; + } + return true; +} + BaseState& NecDspDebugger::GetState() { return _dsp->GetState(); diff --git a/Core/SNES/Debugger/NecDspDebugger.h b/Core/SNES/Debugger/NecDspDebugger.h index db2fe1189..4ca5b89d2 100644 --- a/Core/SNES/Debugger/NecDspDebugger.h +++ b/Core/SNES/Debugger/NecDspDebugger.h @@ -54,5 +54,7 @@ class NecDspDebugger final : public IDebugger BaseEventManager* GetEventManager() override; ITraceLogger* GetTraceLogger() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/SNES/Debugger/SnesDebugger.cpp b/Core/SNES/Debugger/SnesDebugger.cpp index c1017d232..56106e60c 100644 --- a/Core/SNES/Debugger/SnesDebugger.cpp +++ b/Core/SNES/Debugger/SnesDebugger.cpp @@ -529,6 +529,39 @@ PpuTools* SnesDebugger::GetPpuTools() return _ppuTools.get(); } +optional SnesDebugger::GetRegisterValue(RegisterType registerType) +{ + SnesCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::A: return s.A; + case RegisterType::X: return s.X; + case RegisterType::Y: return s.Y; + case RegisterType::Sp: return s.SP; + case RegisterType::Ps: return s.PS; + case RegisterType::Db: return s.DBR; + case RegisterType::D: return s.D; + case RegisterType::Pc: return (s.K << 16) | s.PC; + } +} + +bool SnesDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + SnesCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return false; + case RegisterType::A: s.A = (uint16_t)value; break; + case RegisterType::X: s.X = (uint16_t)value; break; + case RegisterType::Y: s.Y = (uint16_t)value; break; + case RegisterType::Sp: s.SP = (uint16_t)value; break; + case RegisterType::Ps: s.PS = (uint8_t)value; break; + case RegisterType::Db: s.DBR = (uint8_t)value; break; + case RegisterType::D: s.D = (uint16_t)value; break; + case RegisterType::Pc: s.K = (uint8_t)(value >> 16); s.PC = (uint16_t)value; break; + } + return true; +} + BaseState& SnesDebugger::GetState() { return GetCpuState(); diff --git a/Core/SNES/Debugger/SnesDebugger.h b/Core/SNES/Debugger/SnesDebugger.h index 69a0dedf3..0436f3232 100644 --- a/Core/SNES/Debugger/SnesDebugger.h +++ b/Core/SNES/Debugger/SnesDebugger.h @@ -118,6 +118,8 @@ class SnesDebugger final : public IDebugger BaseEventManager* GetEventManager() override; PpuTools* GetPpuTools() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; void GetPpuState(BaseState& state) override; void SetPpuState(BaseState& state) override; diff --git a/Core/SNES/Debugger/SpcDebugger.cpp b/Core/SNES/Debugger/SpcDebugger.cpp index c06f50cfe..be803ec68 100644 --- a/Core/SNES/Debugger/SpcDebugger.cpp +++ b/Core/SNES/Debugger/SpcDebugger.cpp @@ -264,6 +264,35 @@ BaseEventManager* SpcDebugger::GetEventManager() return nullptr; } +optional SpcDebugger::GetRegisterValue(RegisterType registerType) +{ + SpcState& s = _spc->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::A: return s.A; + case RegisterType::X: return s.X; + case RegisterType::Y: return s.Y; + case RegisterType::Sp: return s.SP; + case RegisterType::Ps: return s.PS; + case RegisterType::Pc: return s.PC; + } +} + +bool SpcDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + SpcState& s = _spc->GetState(); + switch(registerType) { + default: return false; + case RegisterType::A: s.A = (uint8_t)value; break; + case RegisterType::X: s.X = (uint8_t)value; break; + case RegisterType::Y: s.Y = (uint8_t)value; break; + case RegisterType::Sp: s.SP = (uint8_t)value; break; + case RegisterType::Ps: s.PS = (uint8_t)value; break; + case RegisterType::Pc: s.PC = (uint16_t)value; break; + } + return true; +} + BaseState& SpcDebugger::GetState() { return _spc->GetState(); diff --git a/Core/SNES/Debugger/SpcDebugger.h b/Core/SNES/Debugger/SpcDebugger.h index aae171774..6b83e0cfb 100644 --- a/Core/SNES/Debugger/SpcDebugger.h +++ b/Core/SNES/Debugger/SpcDebugger.h @@ -67,5 +67,7 @@ class SpcDebugger final : public IDebugger BaseEventManager* GetEventManager() override; ITraceLogger* GetTraceLogger() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/SNES/Debugger/St018Debugger.cpp b/Core/SNES/Debugger/St018Debugger.cpp index 91e2b7e6b..98d9a28cc 100644 --- a/Core/SNES/Debugger/St018Debugger.cpp +++ b/Core/SNES/Debugger/St018Debugger.cpp @@ -274,6 +274,57 @@ BreakpointManager* St018Debugger::GetBreakpointManager() return _breakpointManager.get(); } +optional St018Debugger::GetRegisterValue(RegisterType registerType) +{ + ArmV3CpuState& s = _cpu->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::R0: return s.R[0]; + case RegisterType::R1: return s.R[1]; + case RegisterType::R2: return s.R[2]; + case RegisterType::R3: return s.R[3]; + case RegisterType::R4: return s.R[4]; + case RegisterType::R5: return s.R[5]; + case RegisterType::R6: return s.R[6]; + case RegisterType::R7: return s.R[7]; + case RegisterType::R8: return s.R[8]; + case RegisterType::R9: return s.R[9]; + case RegisterType::R10: return s.R[10]; + case RegisterType::R11: return s.R[11]; + case RegisterType::R12: return s.R[12]; + case RegisterType::R13: case RegisterType::Sp: return s.R[13]; + case RegisterType::R14: case RegisterType::Lr: return s.R[14]; + case RegisterType::R15: case RegisterType::Pc: return s.R[15]; + case RegisterType::Cpsr: return s.CPSR.ToInt32(); + } +} + +bool St018Debugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + ArmV3CpuState& s = _cpu->GetState(); + switch(registerType) { + default: return false; + case RegisterType::R0: s.R[0] = value; break; + case RegisterType::R1: s.R[1] = value; break; + case RegisterType::R2: s.R[2] = value; break; + case RegisterType::R3: s.R[3] = value; break; + case RegisterType::R4: s.R[4] = value; break; + case RegisterType::R5: s.R[5] = value; break; + case RegisterType::R6: s.R[6] = value; break; + case RegisterType::R7: s.R[7] = value; break; + case RegisterType::R8: s.R[8] = value; break; + case RegisterType::R9: s.R[9] = value; break; + case RegisterType::R10: s.R[10] = value; break; + case RegisterType::R11: s.R[11] = value; break; + case RegisterType::R12: s.R[12] = value; break; + case RegisterType::R13: case RegisterType::Sp: s.R[13] = value; break; + case RegisterType::R14: case RegisterType::Lr: s.R[14] = value; break; + case RegisterType::R15: case RegisterType::Pc: s.R[15] = value; break; + case RegisterType::Cpsr: s.CPSR.SetFromInt32(value); break; + } + return true; +} + BaseState& St018Debugger::GetState() { return _cpu->GetState(); diff --git a/Core/SNES/Debugger/St018Debugger.h b/Core/SNES/Debugger/St018Debugger.h index ba19ee75c..63df6e0dd 100644 --- a/Core/SNES/Debugger/St018Debugger.h +++ b/Core/SNES/Debugger/St018Debugger.h @@ -69,5 +69,7 @@ class St018Debugger final : public IDebugger IAssembler* GetAssembler() override; BaseEventManager* GetEventManager() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/Shared/RegisterType.h b/Core/Shared/RegisterType.h new file mode 100644 index 000000000..100c9066d --- /dev/null +++ b/Core/Shared/RegisterType.h @@ -0,0 +1,114 @@ +#pragma once +#include "pch.h" + +enum class RegisterType : uint8_t +{ + A, + X, + Y, + + R0, + R1, + R2, + R3, + R4, + R5, + R6, + R7, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, + SrcReg, + DstReg, + Sfr, + Pbr, + Rombr, + Rambr, + + B, + C, + D, + E, + F, + H, + L, + Af, + Bc, + De, + Hl, + Ix, + Iy, + + AltA, + AltB, + AltC, + AltD, + AltE, + AltF, + AltH, + AltL, + AltAf, + AltBc, + AltDe, + AltHl, + I, + R, + + Tr, + Trb, + Rp, + Dp, + Dr, + Sr, + K, + M, + N, + + Pb, + P, + Ml, + Mh, + + Mdr, + Mar, + Dpr, + + Sp, + Db, + Ps, + + Pc, + Lr, + + Cpsr, + + Ax, + Bx, + Cx, + Dx, + + Al, + Bl, + Cl, + Dl, + + Ah, + Bh, + Ch, + Dh, + + Cs, + Ds, + Es, + Ss, + + Si, + Di, + Bp, + Ip, +}; diff --git a/Core/WS/Debugger/WsDebugger.cpp b/Core/WS/Debugger/WsDebugger.cpp index 13ff81e84..81168e911 100644 --- a/Core/WS/Debugger/WsDebugger.cpp +++ b/Core/WS/Debugger/WsDebugger.cpp @@ -433,6 +433,68 @@ BreakpointManager* WsDebugger::GetBreakpointManager() return _breakpointManager.get(); } +optional WsDebugger::GetRegisterValue(RegisterType registerType) +{ + WsCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return {}; + case RegisterType::Ax: return s.AX; + case RegisterType::Bx: return s.BX; + case RegisterType::Cx: return s.CX; + case RegisterType::Dx: return s.DX; + case RegisterType::Al: return s.AX & 0xFF; + case RegisterType::Bl: return s.BX & 0xFF; + case RegisterType::Cl: return s.CX & 0xFF; + case RegisterType::Dl: return s.DX & 0xFF; + case RegisterType::Ah: return s.AX >> 8; + case RegisterType::Bh: return s.BX >> 8; + case RegisterType::Ch: return s.CX >> 8; + case RegisterType::Dh: return s.DX >> 8; + case RegisterType::Cs: return s.CS; + case RegisterType::Ds: return s.DS; + case RegisterType::Es: return s.ES; + case RegisterType::Ss: return s.SS; + case RegisterType::Si: return s.SI; + case RegisterType::Di: return s.DI; + case RegisterType::Bp: return s.BP; + case RegisterType::Ip: return s.IP; + case RegisterType::F: return s.Flags.Get(); + case RegisterType::Sp: return s.SP; + case RegisterType::Pc: return (s.CS << 4) + s.IP; + } +} + +bool WsDebugger::SetRegisterValue(RegisterType registerType, uint32_t value) +{ + WsCpuState& s = _cpu->GetState(); + switch(registerType) { + default: return false; + case RegisterType::Ax: s.AX = (uint16_t)value; break; + case RegisterType::Bx: s.BX = (uint16_t)value; break; + case RegisterType::Cx: s.CX = (uint16_t)value; break; + case RegisterType::Dx: s.DX = (uint16_t)value; break; + case RegisterType::Al: s.AX = (s.AX & 0xFF00) | (uint8_t)value; break; + case RegisterType::Bl: s.BX = (s.BX & 0xFF00) | (uint8_t)value; break; + case RegisterType::Cl: s.CX = (s.CX & 0xFF00) | (uint8_t)value; break; + case RegisterType::Dl: s.DX = (s.DX & 0xFF00) | (uint8_t)value; break; + case RegisterType::Ah: s.AX = (s.AX & 0xFF) | ((uint8_t)value << 8); break; + case RegisterType::Bh: s.BX = (s.BX & 0xFF) | ((uint8_t)value << 8); break; + case RegisterType::Ch: s.CX = (s.CX & 0xFF) | ((uint8_t)value << 8); break; + case RegisterType::Dh: s.DX = (s.DX & 0xFF) | ((uint8_t)value << 8); break; + case RegisterType::Cs: s.CS = (uint16_t)value; break; + case RegisterType::Ds: s.DS = (uint16_t)value; break; + case RegisterType::Es: s.ES = (uint16_t)value; break; + case RegisterType::Ss: s.SS = (uint16_t)value; break; + case RegisterType::Si: s.SI = (uint16_t)value; break; + case RegisterType::Di: s.DI = (uint16_t)value; break; + case RegisterType::Bp: s.BP = (uint16_t)value; break; + case RegisterType::Ip: s.IP = (uint16_t)value; break; + case RegisterType::F: s.Flags.Set((uint16_t)value); break; + case RegisterType::Sp: s.SP = (uint16_t)value; break; + } + return true; +} + BaseState& WsDebugger::GetState() { return _cpu->GetState(); diff --git a/Core/WS/Debugger/WsDebugger.h b/Core/WS/Debugger/WsDebugger.h index da1562b4b..964a67eca 100644 --- a/Core/WS/Debugger/WsDebugger.h +++ b/Core/WS/Debugger/WsDebugger.h @@ -99,6 +99,8 @@ class WsDebugger final : public IDebugger ITraceLogger* GetTraceLogger() override; PpuTools* GetPpuTools() override; + optional GetRegisterValue(RegisterType registerType) override; + bool SetRegisterValue(RegisterType registerType, uint32_t value) override; BaseState& GetState() override; void GetPpuState(BaseState& state) override; void SetPpuState(BaseState& state) override; diff --git a/UI/Debugger/Documentation/LuaDocumentation.json b/UI/Debugger/Documentation/LuaDocumentation.json index 3c5e31a7b..af25661b9 100644 --- a/UI/Debugger/Documentation/LuaDocumentation.json +++ b/UI/Debugger/Documentation/LuaDocumentation.json @@ -313,6 +313,16 @@ ], "returnValue": { "type": "Int", "description": "A 32-bit (signed or unsigned) value." } }, +{ + "name": "readRegister", + "category": "MemoryAccess", + "description": "Reads a register from the specified CPU and register type.", + "parameters": [ + { "name": "registerType", "type": "Enum", "enumName": "registerType", "description": "Register type" }, + { "name": "cpuType", "type": "Enum", "enumName": "cpuType", "description": "CPU type", "defaultValue": "main CPU" } + ], + "returnValue": { "type": "Int", "description": "The unsigned register value." } +}, { "name": "reset", "category": "Emulation", @@ -449,6 +459,16 @@ { "name": "memoryType", "type": "Enum", "enumName": "memType", "description": "Memory type to write to" } ] }, +{ + "name": "writeRegister", + "category": "MemoryAccess", + "description": "Writes a register to the specified CPU and register type.", + "parameters": [ + { "name": "registerType", "type": "Enum", "enumName": "registerType", "description": "Register type" }, + { "name": "value", "type": "Int", "description": "Value to write" }, + { "name": "cpuType", "type": "Enum", "enumName": "cpuType", "description": "CPU type", "defaultValue": "main CPU" } + ] +}, { "name": "callbackType", "category": "Enums", @@ -652,6 +672,107 @@ { "name": "wsPort", "description": "WS - I/O Port" } ] }, +{ + "name": "registerType", + "category": "Enums", + "description": "Used by emu.readRegister() and emu.writeRegister(). Numbers in parentheses are their bit widths.", + "enumValues": [ + { "name": "a", "description": "SNES (16), SPC (8), DSP-n (16), CX4 (24), ST018 (32), Game Boy (8), NES (8), PC Engine (8), SMS (8)" }, + { "name": "x", "description": "SNES (16), SPC (8), NES (8), PC Engine (8)" }, + { "name": "y", "description": "SNES (16), SPC (8), NES (8), PC Engine (8)" }, + { "name": "r0", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r1", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r2", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r3", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r4", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r5", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r6", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r7", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r8", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r9", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r10", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r11", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r12", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r13", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r14", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "r15", "description": "GSU (16), CX4 (24), ST018 (32), GBA (32)" }, + { "name": "srcReg", "description": "GSU (8)" }, + { "name": "dstReg", "description": "GSU (8)" }, + { "name": "sfr", "description": "GSU (16)" }, + { "name": "pbr", "description": "GSU (8)" }, + { "name": "rombr", "description": "GSU (8)" }, + { "name": "rambr", "description": "GSU (8)" }, + { "name": "b", "description": "DSP-n (16), Game Boy (8), SMS (8)" }, + { "name": "c", "description": "Game Boy (8), SMS (8)" }, + { "name": "d", "description": "SNES (16), Game Boy (8), SMS (8)" }, + { "name": "e", "description": "Game Boy (8), SMS (8)" }, + { "name": "f", "description": "Game Boy (8), SMS (8), WS (16)" }, + { "name": "h", "description": "Game Boy (8), SMS (8)" }, + { "name": "l", "description": "DSP-n (16), Game Boy (8), SMS (8)" }, + { "name": "af", "description": "Game Boy (16), SMS (16)" }, + { "name": "bc", "description": "Game Boy (16), SMS (16)" }, + { "name": "de", "description": "Game Boy (16), SMS (16)" }, + { "name": "hl", "description": "Game Boy (16), SMS (16)" }, + { "name": "ix", "description": "SMS (16)" }, + { "name": "iy", "description": "SMS (16)" }, + { "name": "altA", "description": "SMS (8)" }, + { "name": "altB", "description": "SMS (8)" }, + { "name": "altC", "description": "SMS (8)" }, + { "name": "altD", "description": "SMS (8)" }, + { "name": "altE", "description": "SMS (8)" }, + { "name": "altF", "description": "SMS (8)" }, + { "name": "altH", "description": "SMS (8)" }, + { "name": "altL", "description": "SMS (8)" }, + { "name": "altAf", "description": "SMS (16)" }, + { "name": "altBc", "description": "SMS (16)" }, + { "name": "altDe", "description": "SMS (16)" }, + { "name": "altHl", "description": "SMS (16)" }, + { "name": "i", "description": "SMS (8)" }, + { "name": "r", "description": "SMS (8)" }, + { "name": "tr", "description": "DSP-n (16)" }, + { "name": "trb", "description": "DSP-n (16)" }, + { "name": "rp", "description": "DSP-n (16)" }, + { "name": "dp", "description": "DSP-n (16)" }, + { "name": "dr", "description": "DSP-n (16)" }, + { "name": "sr", "description": "DSP-n (16)" }, + { "name": "k", "description": "DSP-n (16)" }, + { "name": "m", "description": "DSP-n (16)" }, + { "name": "n", "description": "DSP-n (16)" }, + { "name": "pb", "description": "CX4 (16)" }, + { "name": "p", "description": "CX4 (16)" }, + { "name": "ml", "description": "CX4 (24)" }, + { "name": "mh", "description": "CX4 (24)" }, + { "name": "mdr", "description": "CX4 (24)" }, + { "name": "mar", "description": "CX4 (24)" }, + { "name": "dpr", "description": "CX4 (24)" }, + { "name": "sp", "description": "SNES (16), SPC (8), DSP-n (8), CX4 (8), ST018 (32), Game Boy (16), NES (8), PC Engine (8), SMS (16), GBA (32), WS (16)" }, + { "name": "db", "description": "SNES (8)" }, + { "name": "ps", "description": "SNES (8), SPC (8), NES (8), PC Engine (8)" }, + { "name": "pc", "description": "SNES (24), SPC (16), DSP-n (16), CX4 (8), ST018 (32), Game Boy (16), NES (16), PC Engine (16), SMS (16), GBA (32), WS (20, read-only)" }, + { "name": "lr", "description": "ST018 (32), GBA (32)" }, + { "name": "cpsr", "description": "ST018 (32), GBA (32)" }, + { "name": "ax", "description": "WS (16)" }, + { "name": "bx", "description": "WS (16)" }, + { "name": "cx", "description": "WS (16)" }, + { "name": "dx", "description": "WS (16)" }, + { "name": "al", "description": "WS (8)" }, + { "name": "bl", "description": "WS (8)" }, + { "name": "cl", "description": "WS (8)" }, + { "name": "dl", "description": "WS (8)" }, + { "name": "ah", "description": "WS (8)" }, + { "name": "bh", "description": "WS (8)" }, + { "name": "ch", "description": "WS (8)" }, + { "name": "dh", "description": "WS (8)" }, + { "name": "cs", "description": "WS (16)" }, + { "name": "ds", "description": "WS (16)" }, + { "name": "es", "description": "WS (16)" }, + { "name": "ss", "description": "WS (16)" }, + { "name": "si", "description": "WS (16)" }, + { "name": "di", "description": "WS (16)" }, + { "name": "bp", "description": "WS (16)" }, + { "name": "ip", "description": "WS (16)" } + ] +}, { "name": "stepType", "category": "Enums",