Debugger: Implemented "move program counter" for most CPUs

This commit is contained in:
Sour 2022-02-24 23:00:08 -05:00
parent a4391d0bea
commit 56b05cced6
34 changed files with 389 additions and 151 deletions

View file

@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="Debugger\BaseTraceLogger.h" />
<ClInclude Include="Debugger\DebuggerFeatures.h" />
<ClInclude Include="Debugger\ITraceLogger.h" />
<ClInclude Include="Debugger\stdafx.h" />
<ClInclude Include="Debugger\TraceLogFileSaver.h" />

View file

@ -818,6 +818,7 @@
<ClInclude Include="Gameboy\Input\GbController.h" />
<ClInclude Include="NES\Debugger\DummyNesCpu.h" />
<ClInclude Include="Gameboy\Debugger\DummyGbCpu.h" />
<ClInclude Include="Debugger\DebuggerFeatures.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="SNES\SnesCpu.cpp">

View file

@ -80,7 +80,6 @@ Debugger::Debugger(Emulator* emu, IConsole* console)
}
_debuggers[(int)type].Evaluator.reset(new ExpressionEvaluator(this, _debuggers[(int)type].Debugger.get(), type));
_debuggers[(int)type].IgnoreBreakpoints = false;
}
for(CpuType type : _cpuTypes) {
@ -146,7 +145,8 @@ DebuggerType* Debugger::GetDebugger()
template<CpuType type>
void Debugger::ProcessInstruction()
{
_debuggers[(int)type].IgnoreBreakpoints = false;
_debuggers[(int)type].Debugger->IgnoreBreakpoints = false;
_debuggers[(int)type].Debugger->AllowChangeProgramCounter = true;
switch(type) {
case CpuType::Snes: GetDebugger<type, SnesDebugger>()->ProcessInstruction(); break;
@ -158,6 +158,8 @@ void Debugger::ProcessInstruction()
case CpuType::Gameboy: GetDebugger<type, GbDebugger>()->ProcessInstruction(); break;
case CpuType::Nes: GetDebugger<type, NesDebugger>()->ProcessInstruction(); break;
}
_debuggers[(int)type].Debugger->AllowChangeProgramCounter = false;
}
template<CpuType type>
@ -244,7 +246,7 @@ void Debugger::SleepUntilResume(CpuType sourceCpu, BreakSource source, MemoryOpe
_emu->GetSoundMixer()->StopAudio();
if(_settings->CheckDebuggerFlag(DebuggerFlags::SingleBreakpointPerInstruction)) {
_debuggers[(int)sourceCpu].IgnoreBreakpoints = true;
_debuggers[(int)sourceCpu].Debugger->IgnoreBreakpoints = true;
}
//Only trigger code break event if the pause was caused by user action
@ -255,6 +257,7 @@ void Debugger::SleepUntilResume(CpuType sourceCpu, BreakSource source, MemoryOpe
if(operation) {
evt.Operation = *operation;
}
_waitForBreakResume = true;
_emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak, &evt);
notificationSent = true;
@ -274,10 +277,10 @@ void Debugger::SleepUntilResume(CpuType sourceCpu, BreakSource source, MemoryOpe
void Debugger::ProcessBreakConditions(CpuType sourceCpu, StepRequest& step, BreakpointManager* bpManager, MemoryOperationInfo& operation, AddressInfo& addressInfo)
{
int breakpointId = bpManager->CheckBreakpoint(operation, addressInfo, true);
if(_breakRequestCount || _waitForBreakResume || (step.BreakNeeded && !_debuggers[(int)sourceCpu].IgnoreBreakpoints)) {
if(_breakRequestCount || _waitForBreakResume || (step.BreakNeeded && !_debuggers[(int)sourceCpu].Debugger->IgnoreBreakpoints)) {
SleepUntilResume(sourceCpu, step.Source);
} else {
if(breakpointId >= 0 && !_debuggers[(int)sourceCpu].IgnoreBreakpoints) {
if(breakpointId >= 0 && !_debuggers[(int)sourceCpu].Debugger->IgnoreBreakpoints) {
SleepUntilResume(sourceCpu, BreakSource::Breakpoint, &operation, breakpointId);
}
}
@ -285,7 +288,7 @@ void Debugger::ProcessBreakConditions(CpuType sourceCpu, StepRequest& step, Brea
void Debugger::ProcessPredictiveBreakpoint(CpuType sourceCpu, BreakpointManager* bpManager, MemoryOperationInfo& operation, AddressInfo& addressInfo)
{
if(_debuggers[(int)sourceCpu].IgnoreBreakpoints) {
if(_debuggers[(int)sourceCpu].Debugger->IgnoreBreakpoints) {
return;
}
@ -548,6 +551,26 @@ void Debugger::GetConsoleState(BaseState& state, ConsoleType consoleType)
_console->GetConsoleState(state, consoleType);
}
DebuggerFeatures Debugger::GetDebuggerFeatures(CpuType cpuType)
{
if(_debuggers[(int)cpuType].Debugger) {
return _debuggers[(int)cpuType].Debugger->GetSupportedFeatures();
}
return {};
}
void Debugger::SetProgramCounter(CpuType cpuType, uint32_t addr)
{
if(_debuggers[(int)cpuType].Debugger->AllowChangeProgramCounter) {
_debuggers[(int)cpuType].Debugger->SetProgramCounter(addr);
}
}
uint32_t Debugger::GetProgramCounter(CpuType cpuType, bool getInstPc)
{
return _debuggers[(int)cpuType].Debugger->GetProgramCounter(getInstPc);
}
AddressInfo Debugger::GetAbsoluteAddress(AddressInfo relAddress)
{
return _console->GetAbsoluteAddress(relAddress);

View file

@ -3,6 +3,7 @@
#include "Utilities/SimpleLock.h"
#include "Debugger/DebugUtilities.h"
#include "Debugger/DebugTypes.h"
#include "Debugger/DebuggerFeatures.h"
#include "Shared/SettingTypes.h"
class IConsole;
@ -44,7 +45,6 @@ struct CpuInfo
{
unique_ptr<IDebugger> Debugger;
unique_ptr<ExpressionEvaluator> Evaluator;
bool IgnoreBreakpoints;
};
class Debugger
@ -129,6 +129,10 @@ public:
void GetConsoleState(BaseState& state, ConsoleType consoleType);
DebuggerFeatures GetDebuggerFeatures(CpuType cpuType);
uint32_t GetProgramCounter(CpuType cpuType, bool forInstStart);
void SetProgramCounter(CpuType cpuType, uint32_t addr);
AddressInfo GetAbsoluteAddress(AddressInfo relAddress);
AddressInfo GetRelativeAddress(AddressInfo absAddress, CpuType cpuType);

View file

@ -0,0 +1,13 @@
#pragma once
#include "stdafx.h"
struct DebuggerFeatures
{
bool RunToIrq;
bool RunToNmi;
bool StepOver;
bool StepOut;
bool StepBack;
bool ChangeProgramCounter;
bool CallStack;
};

View file

@ -1,5 +1,6 @@
#pragma once
#include "stdafx.h"
#include "DebuggerFeatures.h"
enum class StepType;
class BreakpointManager;
@ -16,6 +17,9 @@ enum class MemoryOperationType;
class IDebugger
{
public:
bool IgnoreBreakpoints = false;
bool AllowChangeProgramCounter = false;
virtual ~IDebugger() = default;
virtual void Step(int32_t stepCount, StepType type) = 0;
@ -27,6 +31,11 @@ public:
virtual void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) {}
virtual DebuggerFeatures GetSupportedFeatures() { return {}; }
virtual uint32_t GetProgramCounter(bool getInstPc) = 0;
virtual void SetProgramCounter(uint32_t addr) = 0;
virtual BreakpointManager* GetBreakpointManager() = 0;
virtual CallstackManager* GetCallstackManager() = 0;
virtual IAssembler* GetAssembler() = 0;
@ -38,4 +47,4 @@ public:
virtual BaseState& GetState() = 0;
virtual void GetPpuState(BaseState& state) {};
virtual void SetPpuState(BaseState& state) {};
};
};

View file

@ -83,18 +83,11 @@ void GbDebugger::ProcessInstruction()
uint8_t value = _gameboy->GetMemoryManager()->DebugRead(pc);
MemoryOperationInfo operation(pc, value, MemoryOperationType::ExecOpCode, MemoryType::GameboyMemory);
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled)) {
if(addressInfo.Address >= 0) {
if(addressInfo.Type == MemoryType::GbPrgRom) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code);
}
_disassembler->BuildCache(addressInfo, 0, CpuType::Gameboy);
}
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, pc, 0, CpuType::Gameboy);
_traceLogger->Log(state, disInfo, operation);
if(addressInfo.Address >= 0) {
if(addressInfo.Type == MemoryType::GbPrgRom) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code);
}
_disassembler->BuildCache(addressInfo, 0, CpuType::Gameboy);
}
if(GameboyDisUtils::IsJumpToSub(_prevOpCode) && pc != _prevProgramCounter + GameboyDisUtils::GetOpSize(_prevOpCode)) {
@ -131,7 +124,7 @@ void GbDebugger::ProcessInstruction()
break;
}
}
_prevOpCode = value;
_prevProgramCounter = pc;
@ -158,6 +151,10 @@ void GbDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType t
MemoryOperationInfo operation(addr, value, type, MemoryType::GameboyMemory);
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Gameboy);
_traceLogger->Log(_cpu->GetState(), disInfo, operation);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _emu->GetMasterClock());
} else if(type == MemoryOperationType::ExecOperand) {
if(addressInfo.Address >= 0 && addressInfo.Type == MemoryType::GbPrgRom) {
@ -297,6 +294,30 @@ void GbDebugger::ProcessPpuCycle()
}
}
DebuggerFeatures GbDebugger::GetSupportedFeatures()
{
DebuggerFeatures features = {};
features.RunToIrq = true;
features.RunToNmi = false;
features.StepOver = true;
features.StepOut = true;
features.CallStack = true;
features.ChangeProgramCounter = AllowChangeProgramCounter;
return features;
}
void GbDebugger::SetProgramCounter(uint32_t addr)
{
_cpu->GetState().PC = (uint16_t)addr;
_prevOpCode = _gameboy->GetMemoryManager()->DebugRead((uint16_t)addr);
_prevProgramCounter = (uint16_t)addr;
}
uint32_t GbDebugger::GetProgramCounter(bool getInstPc)
{
return getInstPc ? _prevProgramCounter : _cpu->GetState().PC;
}
BaseEventManager* GbDebugger::GetEventManager()
{
return _eventManager.get();

View file

@ -67,6 +67,11 @@ public:
void SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption);
void SetProgramCounter(uint32_t addr) override;
uint32_t GetProgramCounter(bool getInstPc) override;
DebuggerFeatures GetSupportedFeatures() override;
BaseEventManager* GetEventManager() override;
IAssembler* GetAssembler() override;
CallstackManager* GetCallstackManager() override;

View file

@ -95,11 +95,6 @@ void NesDebugger::ProcessInstruction()
}
}
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, CpuType::Nes);
_traceLogger->Log(state, disInfo, operation);
}
uint32_t pc = state.PC;
if(_prevOpCode == 0x20) {
//JSR
@ -156,6 +151,12 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
}
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsEnabled()) {
NesCpuState& state = _cpu->GetState();
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, CpuType::Nes);
_traceLogger->Log(state, disInfo, operation);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _cpu->GetCycleCount());
} else if(type == MemoryOperationType::ExecOperand) {
if(addressInfo.Type == MemoryType::NesPrgRom && addressInfo.Address >= 0) {
@ -312,6 +313,30 @@ bool NesDebugger::IsRegister(MemoryOperationInfo& op)
return false;
}
DebuggerFeatures NesDebugger::GetSupportedFeatures()
{
DebuggerFeatures features = {};
features.RunToIrq = true;
features.RunToNmi = true;
features.StepOver = true;
features.StepOut = true;
features.CallStack = true;
features.ChangeProgramCounter = AllowChangeProgramCounter;
return features;
}
void NesDebugger::SetProgramCounter(uint32_t addr)
{
_cpu->GetState().PC = (uint16_t)addr;
_prevOpCode = _memoryManager->DebugRead(addr);
_prevProgramCounter = (uint16_t)addr;
}
uint32_t NesDebugger::GetProgramCounter(bool getInstPc)
{
return getInstPc ? _prevProgramCounter : _cpu->GetState().PC;
}
CallstackManager* NesDebugger::GetCallstackManager()
{
return _callstackManager.get();

View file

@ -58,6 +58,7 @@ class NesDebugger final : public IDebugger
bool IsRegister(MemoryOperationInfo& op);
public:
NesDebugger(Debugger* debugger);
@ -74,6 +75,10 @@ public:
void Run() override;
void Step(int32_t stepCount, StepType type) override;
DebuggerFeatures GetSupportedFeatures() override;
void SetProgramCounter(uint32_t addr) override;
uint32_t GetProgramCounter(bool getInstPc) override;
BreakpointManager* GetBreakpointManager() override;
ITraceLogger* GetTraceLogger() override;
PpuTools* GetPpuTools() override;

View file

@ -148,6 +148,7 @@ void NecDsp::BuildProgramCache()
void NecDsp::ReadOpCode()
{
_opCode = _prgCache[_state.PC & _progMask];
_emu->ProcessMemoryRead<CpuType::NecDsp>(_state.PC, _opCode, MemoryOperationType::ExecOpCode);
}
void NecDsp::Run()
@ -249,7 +250,6 @@ void NecDsp::Write(uint32_t addr, uint8_t value)
uint32_t NecDsp::GetOpCode(uint32_t addr)
{
//Avoid side effects for now
return _prgCache[addr & _progMask];
}

View file

@ -53,13 +53,8 @@ void Cx4Debugger::ProcessInstruction()
_codeDataLogger->SetFlags(addressInfo.Address + 1, CdlFlags::Code | CdlFlags::Cx4);
}
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::Cx4DebuggerEnabled)) {
if(_settings->CheckDebuggerFlag(DebuggerFlags::Cx4DebuggerEnabled)) {
_disassembler->BuildCache(addressInfo, 0, CpuType::Cx4);
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Cx4);
_traceLogger->Log(state, disInfo, operation);
}
}
_prevProgramCounter = addr;
@ -77,6 +72,11 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
MemoryOperationInfo operation(addr, value, type, MemoryType::Cx4Memory);
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Cx4);
_traceLogger->Log(state, disInfo, operation);
}
AddressInfo opCodeHighAddr = _cx4->GetMemoryMappings()->GetAbsoluteAddress(addr + 1);
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
_memoryAccessCounter->ProcessMemoryExec(opCodeHighAddr, _memoryManager->GetMasterClock());
@ -129,6 +129,17 @@ void Cx4Debugger::Step(int32_t stepCount, StepType type)
_step.reset(new StepRequest(step));
}
void Cx4Debugger::SetProgramCounter(uint32_t addr)
{
//Not implemented
}
uint32_t Cx4Debugger::GetProgramCounter(bool getInstPc)
{
Cx4State& state = _cx4->GetState();
return getInstPc ? _prevProgramCounter : ((state.Cache.Address[state.Cache.Page] + (state.PC * 2)) & 0xFFFFFF);
}
BreakpointManager* Cx4Debugger::GetBreakpointManager()
{
return _breakpointManager.get();

View file

@ -44,6 +44,9 @@ public:
void Run() override;
void Step(int32_t stepCount, StepType type) override;
void SetProgramCounter(uint32_t addr) override;
uint32_t GetProgramCounter(bool getInstPc) override;
BreakpointManager* GetBreakpointManager() override;
CallstackManager* GetCallstackManager() override;
IAssembler* GetAssembler() override;

View file

@ -51,13 +51,8 @@ void GsuDebugger::ProcessInstruction()
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | CdlFlags::Gsu);
}
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::GsuDebuggerEnabled)) {
if(_settings->CheckDebuggerFlag(DebuggerFlags::GsuDebuggerEnabled)) {
_disassembler->BuildCache(addressInfo, state.SFR.GetFlagsHigh() & 0x13, CpuType::Gsu);
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Gsu);
_traceLogger->Log(state, disInfo, operation);
}
}
_prevOpCode = state.ProgramReadBuffer;
@ -72,8 +67,13 @@ void GsuDebugger::ProcessInstruction()
void GsuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo = _gsu->GetMemoryMappings()->GetAbsoluteAddress(addr);
MemoryOperationInfo operation(addr, value, type, MemoryType::GsuMemory);
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Gsu);
_traceLogger->Log(_gsu->GetState(), disInfo, operation);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
} else if(type == MemoryOperationType::ExecOperand) {
if(addressInfo.Type == MemoryType::SnesPrgRom) {
@ -81,7 +81,6 @@ void GsuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
} else {
MemoryOperationInfo operation(addr, value, type, MemoryType::GsuMemory);
if(addressInfo.Type == MemoryType::SnesPrgRom) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | CdlFlags::Gsu);
@ -130,6 +129,17 @@ void GsuDebugger::Step(int32_t stepCount, StepType type)
_step.reset(new StepRequest(step));
}
void GsuDebugger::SetProgramCounter(uint32_t addr)
{
//Not implemented
}
uint32_t GsuDebugger::GetProgramCounter(bool getInstPc)
{
GsuState& state = _gsu->GetState();
return getInstPc ? _prevProgramCounter : ((state.ProgramBank << 16) | state.R[15]);
}
BreakpointManager* GsuDebugger::GetBreakpointManager()
{
return _breakpointManager.get();

View file

@ -44,6 +44,9 @@ public:
void Run() override;
void Step(int32_t stepCount, StepType type) override;
void SetProgramCounter(uint32_t addr) override;
uint32_t GetProgramCounter(bool getInstPc) override;
BreakpointManager* GetBreakpointManager() override;
CallstackManager* GetCallstackManager() override;
IAssembler* GetAssembler() override;

View file

@ -42,14 +42,7 @@ void NecDspDebugger::ProcessInstruction()
AddressInfo addressInfo = { (int32_t)addr, MemoryType::DspProgramRom };
MemoryOperationInfo operation(addr, value, MemoryOperationType::ExecOpCode, MemoryType::NecDspMemory);
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::NecDspDebuggerEnabled)) {
_disassembler->BuildCache(addressInfo, 0, CpuType::NecDsp);
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::NecDsp);
_traceLogger->Log(_dsp->GetState(), disInfo, operation);
}
}
_disassembler->BuildCache(addressInfo, 0, CpuType::NecDsp);
_prevProgramCounter = addr;
_step->ProcessCpuExec();
@ -58,7 +51,13 @@ void NecDspDebugger::ProcessInstruction()
void NecDspDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type)
{
//TODO
AddressInfo addressInfo = { (int32_t)addr, MemoryType::DspProgramRom };
MemoryOperationInfo operation(addr, value, MemoryOperationType::ExecOpCode, MemoryType::NecDspMemory);
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::NecDsp);
_traceLogger->Log(_dsp->GetState(), disInfo, operation);
}
}
void NecDspDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
@ -91,6 +90,24 @@ void NecDspDebugger::Step(int32_t stepCount, StepType type)
_step.reset(new StepRequest(step));
}
DebuggerFeatures NecDspDebugger::GetSupportedFeatures()
{
DebuggerFeatures features = {};
features.ChangeProgramCounter = AllowChangeProgramCounter;
return features;
}
void NecDspDebugger::SetProgramCounter(uint32_t addr)
{
_dsp->GetState().PC = addr / 3;
_prevProgramCounter = addr;
}
uint32_t NecDspDebugger::GetProgramCounter(bool getInstPc)
{
return getInstPc ? _prevProgramCounter : (_dsp->GetState().PC * 3);
}
CallstackManager* NecDspDebugger::GetCallstackManager()
{
return nullptr;

View file

@ -40,6 +40,10 @@ public:
void Run() override;
void Step(int32_t stepCount, StepType type) override;
DebuggerFeatures GetSupportedFeatures() override;
void SetProgramCounter(uint32_t addr) override;
uint32_t GetProgramCounter(bool getInstPc) override;
CallstackManager* GetCallstackManager() override;
BreakpointManager* GetBreakpointManager() override;
IAssembler* GetAssembler() override;

View file

@ -128,11 +128,6 @@ void SnesDebugger::ProcessInstruction()
}
}
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, _cpuType);
_traceLogger->Log(state, disInfo, operation);
}
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) {
//JSR, JSL
uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, state.PS, _cpuType);
@ -196,6 +191,10 @@ void SnesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
}
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, _cpuType);
_traceLogger->Log(state, disInfo, operation);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
} else if(type == MemoryOperationType::ExecOperand) {
if(addressInfo.Type == MemoryType::SnesPrgRom && addressInfo.Address >= 0) {
@ -354,6 +353,32 @@ bool SnesDebugger::IsRegister(uint32_t addr)
return _cpuType == CpuType::Snes && _memoryManager->IsRegister(addr);
}
DebuggerFeatures SnesDebugger::GetSupportedFeatures()
{
DebuggerFeatures features = {};
features.RunToIrq = true;
features.RunToNmi = true;
features.StepOver = true;
features.StepOut = true;
features.CallStack = true;
features.ChangeProgramCounter = AllowChangeProgramCounter;
return features;
}
void SnesDebugger::SetProgramCounter(uint32_t addr)
{
GetCpuState().PC = (uint16_t)addr;
GetCpuState().K = (uint8_t)(addr >> 16);
_prevOpCode = _memoryManager->Peek(addr);
_prevProgramCounter = addr;
}
uint32_t SnesDebugger::GetProgramCounter(bool getInstPc)
{
return getInstPc ? _prevProgramCounter : ((GetCpuState().K << 16) | GetCpuState().PC);
}
CallstackManager* SnesDebugger::GetCallstackManager()
{
return _callstackManager.get();

View file

@ -91,6 +91,10 @@ public:
void Run() override;
void Step(int32_t stepCount, StepType type) override;
DebuggerFeatures GetSupportedFeatures() override;
void SetProgramCounter(uint32_t addr) override;
uint32_t GetProgramCounter(bool getInstPc) override;
ITraceLogger* GetTraceLogger() override;
BreakpointManager* GetBreakpointManager() override;

View file

@ -58,11 +58,6 @@ void SpcDebugger::ProcessInstruction()
_disassembler->BuildCache(addressInfo, 0, CpuType::Spc);
if(_traceLogger->IsEnabled()) {
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Spc);
_traceLogger->Log(state, disInfo, operation);
}
if(_prevOpCode == 0x3F || _prevOpCode == 0x0F) {
//JSR, BRK
uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, 0, CpuType::Spc);
@ -115,6 +110,11 @@ void SpcDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
MemoryOperationInfo operation(addr, value, type, MemoryType::SpcMemory);
if(type == MemoryOperationType::ExecOpCode) {
if(_traceLogger->IsEnabled()) {
SpcState& state = _spc->GetState();
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Spc);
_traceLogger->Log(state, disInfo, operation);
}
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
} else if(type == MemoryOperationType::ExecOperand) {
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
@ -176,6 +176,30 @@ void SpcDebugger::Step(int32_t stepCount, StepType type)
_step.reset(new StepRequest(step));
}
DebuggerFeatures SpcDebugger::GetSupportedFeatures()
{
DebuggerFeatures features = {};
features.RunToIrq = false;
features.RunToNmi = false;
features.StepOver = true;
features.StepOut = true;
features.CallStack = true;
features.ChangeProgramCounter = AllowChangeProgramCounter;
return features;
}
void SpcDebugger::SetProgramCounter(uint32_t addr)
{
_spc->GetState().PC = (uint16_t)addr;
_prevOpCode = _spc->DebugRead(addr);
_prevProgramCounter = (uint16_t)addr;
}
uint32_t SpcDebugger::GetProgramCounter(bool getInstPc)
{
return getInstPc ? _prevProgramCounter : _spc->GetState().PC;
}
CallstackManager* SpcDebugger::GetCallstackManager()
{
return _callstackManager.get();

View file

@ -51,6 +51,10 @@ public:
void Run() override;
void Step(int32_t stepCount, StepType type) override;
DebuggerFeatures GetSupportedFeatures() override;
void SetProgramCounter(uint32_t addr) override;
uint32_t GetProgramCounter(bool getInstPc) override;
CallstackManager* GetCallstackManager() override;
BreakpointManager* GetBreakpointManager() override;
IAssembler* GetAssembler() override;

View file

@ -102,6 +102,10 @@ extern "C"
DllExport void __stdcall SetCpuState(BaseState& state, CpuType cpuType) { WithDebugger(void, SetCpuState(state, cpuType)); }
DllExport void __stdcall SetPpuState(BaseState& state, CpuType cpuType) { WithDebugger(void, SetPpuState(state, cpuType)); }
DllExport uint32_t __stdcall GetProgramCounter(CpuType cpuType, bool getInstPc) { return WithDebugger(uint32_t, GetProgramCounter(cpuType, getInstPc)); }
DllExport void __stdcall SetProgramCounter(CpuType cpuType, uint32_t addr) { WithDebugger(void, SetProgramCounter(cpuType, addr)); }
DllExport DebuggerFeatures __stdcall GetDebuggerFeatures(CpuType cpuType) { return WithDebugger(DebuggerFeatures, GetDebuggerFeatures(cpuType)); }
DllExport const char* __stdcall GetDebuggerLog()
{
_logString = WithDebugger(string, GetLog());

View file

@ -150,7 +150,7 @@ namespace Mesen.Config
Add(new() { Shortcut = DebuggerShortcut.CodeWindow_ViewInMemoryViewer, KeyBinding = new(Key.F1) });
Add(new() { Shortcut = DebuggerShortcut.CodeWindow_AddToWatch, KeyBinding = new() });
Add(new() { Shortcut = DebuggerShortcut.CodeWindow_GoToLocation, KeyBinding = new() });
Add(new() { Shortcut = DebuggerShortcut.CodeWindow_SetNextStatement, KeyBinding = new(KeyModifiers.Control | KeyModifiers.Shift, Key.F10) });
Add(new() { Shortcut = DebuggerShortcut.CodeWindow_MoveProgramCounter, KeyBinding = new(KeyModifiers.Control | KeyModifiers.Shift, Key.F10) });
Add(new() { Shortcut = DebuggerShortcut.CodeWindow_EditSelectedCode, KeyBinding = new() });
Add(new() { Shortcut = DebuggerShortcut.CodeWindow_EditSourceFile, KeyBinding = new(Key.F4) });
Add(new() { Shortcut = DebuggerShortcut.CodeWindow_EditLabel, KeyBinding = new(Key.F2) });
@ -274,7 +274,7 @@ namespace Mesen.Config
BreakOn,
FindOccurrences,
GoToProgramCounter,
CodeWindow_SetNextStatement,
CodeWindow_MoveProgramCounter,
CodeWindow_EditSelectedCode,
CodeWindow_EditSourceFile,
CodeWindow_EditLabel,

View file

@ -38,7 +38,7 @@
</ItemsPresenter.ItemsPanel>
<ItemsPresenter.DataTemplates>
<DataTemplate DataType="{x:Type du:ContextMenuSeparator}">
<StackPanel Width="2" Height="16" Margin="2 0">
<StackPanel Width="2" Height="16" Margin="2 0" IsVisible="{Binding Visible}">
<Rectangle
Stroke="LightGray"
StrokeThickness="1"
@ -49,7 +49,7 @@
</DataTemplate>
<DataTemplate DataType="{x:Type du:ContextMenuAction}">
<Button ToolTip.Tip="{Binding Name}" Command="{Binding ClickCommand}" IsEnabled="{Binding Enabled}">
<Button ToolTip.Tip="{Binding Name}" Command="{Binding ClickCommand}" IsEnabled="{Binding Enabled}" IsVisible="{Binding Visible}">
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{Binding Icon}" />
<TextBlock

View file

@ -1,57 +0,0 @@
using Mesen.Interop;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.Debugger
{
public static class DebugUtilities
{
public static uint GetProgramCounter(CpuType cpuType)
{
//TODO move to C++ core
switch(cpuType) {
case CpuType.Snes:
case CpuType.Sa1: {
SnesCpuState state = DebugApi.GetCpuState<SnesCpuState>(cpuType);
return (uint)(state.K << 16) | state.PC;
}
case CpuType.Spc: {
SpcState state = DebugApi.GetCpuState<SpcState>(cpuType);
return (uint)state.PC;
}
case CpuType.Gameboy: {
GbCpuState state = DebugApi.GetCpuState<GbCpuState>(cpuType);
return (uint)state.PC;
}
case CpuType.Nes: {
NesCpuState state = DebugApi.GetCpuState<NesCpuState>(cpuType);
return (uint)state.PC;
}
case CpuType.NecDsp: {
NecDspState state = DebugApi.GetCpuState<NecDspState>(cpuType);
return (uint)(state.PC * 3);
}
case CpuType.Gsu: {
GsuState state = DebugApi.GetCpuState<GsuState>(cpuType);
return (uint)((state.ProgramBank << 16) | state.R[15]);
}
case CpuType.Cx4: {
Cx4State state = DebugApi.GetCpuState<Cx4State>(cpuType);
return (uint)(state.Cache.Address[state.Cache.Page] + (state.PC * 2)) & 0xFFFFFF;
}
default: throw new Exception("Invalid cpu type");
}
}
}
}

View file

@ -93,6 +93,8 @@ namespace Mesen.Debugger.Utilities
public Func<bool>? IsEnabled { get; set; }
public Func<bool>? IsSelected { get; set; }
public Func<bool>? IsVisible { get; set; }
public bool AllowedWhenHidden { get; set; }
public abstract string ShortcutText { get; }
@ -117,7 +119,7 @@ namespace Mesen.Debugger.Utilities
set
{
_onClick = () => {
if((IsVisible == null || IsVisible()) && (IsEnabled == null || IsEnabled())) {
if((IsVisible == null || AllowedWhenHidden || IsVisible()) && (IsEnabled == null || IsEnabled())) {
if(ActionType == ActionType.Exit) {
//When using exit, the command is disposed while the command is running, which causes a crash
//Run the code in a posted action to prevent the crash
@ -598,5 +600,7 @@ namespace Mesen.Debugger.Utilities
LoadCdl,
[IconFile("SaveFloppy")]
SaveCdl,
MoveProgramCounter,
}
}

View file

@ -124,7 +124,7 @@ namespace Mesen.Debugger.Utilities
switch(e.NotificationType) {
case ConsoleNotificationType.GameLoaded:
DebugWorkspaceManager.Load();
Dispatcher.UIThread.Post(() => DebugWorkspaceManager.Load());
break;
case ConsoleNotificationType.EmulationStopped:

View file

@ -7,6 +7,7 @@ using Mesen.Debugger.Labels;
using Mesen.Debugger.Utilities;
using Mesen.Debugger.Windows;
using Mesen.Interop;
using Mesen.Utilities;
using Mesen.ViewModels;
using ReactiveUI.Fody.Helpers;
using System;
@ -20,7 +21,7 @@ namespace Mesen.Debugger.ViewModels
public CpuType CpuType { get; }
public DisassemblyViewModel Disassembly { get; }
[Reactive] public List<StackInfo> CallStackContent { get; private set; } = new List<StackInfo>();
[Reactive] public SwappableList<StackInfo> CallStackContent { get; private set; } = new();
private StackFrameInfo[] _stackFrames = Array.Empty<StackFrameInfo>();
@ -47,7 +48,7 @@ namespace Mesen.Debugger.ViewModels
public void RefreshCallStack()
{
CallStackContent = GetStackInfo();
CallStackContent.Swap(GetStackInfo());
}
private List<StackInfo> GetStackInfo()
@ -71,8 +72,8 @@ namespace Mesen.Debugger.ViewModels
stack.Insert(0, new StackInfo() {
EntryPoint = GetEntryPoint(stackFrames.Length > 0 ? stackFrames[^1] : null),
EntryPointAddr = stackFrames.Length > 0 ? stackFrames[^1].AbsTarget : null,
RelAddress = DebugUtilities.GetProgramCounter(CpuType),
Address = DebugApi.GetAbsoluteAddress(new AddressInfo() { Address = (int)DebugUtilities.GetProgramCounter(CpuType), Type = CpuType.ToMemoryType() })
RelAddress = DebugApi.GetProgramCounter(CpuType, true),
Address = DebugApi.GetAbsoluteAddress(new AddressInfo() { Address = (int)DebugApi.GetProgramCounter(CpuType, true), Type = CpuType.ToMemoryType() })
});
return stack;

View file

@ -123,17 +123,17 @@ namespace Mesen.Debugger.ViewModels
DebuggerShortcut.RunToIrq,
//DebuggerShortcut.FindOccurrences,
DebuggerShortcut.GoToProgramCounter,
//DebuggerShortcut.CodeWindow_SetNextStatement,
DebuggerShortcut.CodeWindow_EditSelectedCode,
//DebuggerShortcut.CodeWindow_EditSourceFile,
DebuggerShortcut.CodeWindow_AddToWatch,
DebuggerShortcut.CodeWindow_GoToLocation,
DebuggerShortcut.CodeWindow_ViewInMemoryViewer,
DebuggerShortcut.CodeWindow_EditLabel,
//DebuggerShortcut.CodeWindow_NavigateBack,
//DebuggerShortcut.CodeWindow_NavigateForward,
DebuggerShortcut.CodeWindow_ToggleBreakpoint,
DebuggerShortcut.CodeWindow_DisableEnableBreakpoint,
DebuggerShortcut.CodeWindow_MoveProgramCounter,
DebuggerShortcut.CodeWindow_GoToLocation,
//DebuggerShortcut.CodeWindow_SwitchView,
//DebuggerShortcut.FunctionList_EditLabel,
//DebuggerShortcut.FunctionList_AddBreakpoint,

View file

@ -97,7 +97,7 @@ namespace Mesen.Debugger.ViewModels
Config = ConfigManager.Config.Debug.Debugger;
Options = new DebuggerOptionsViewModel(Config, CpuType);
Disassembly = new DisassemblyViewModel(ConfigManager.Config.Debug, CpuType);
Disassembly = new DisassemblyViewModel(this, ConfigManager.Config.Debug, CpuType);
BreakpointList = new BreakpointListViewModel(CpuType, Disassembly);
LabelList = new LabelListViewModel(CpuType, Disassembly);
CallStack = new CallStackViewModel(CpuType, Disassembly);
@ -225,11 +225,11 @@ namespace Mesen.Debugger.ViewModels
}
}
private void UpdateDisassembly(bool scrollToActiveAddress)
public void UpdateDisassembly(bool scrollToActiveAddress)
{
if(scrollToActiveAddress) {
//Scroll to the active address and highlight it
Disassembly.SetActiveAddress((int)DebugUtilities.GetProgramCounter(CpuType));
Disassembly.SetActiveAddress((int)DebugApi.GetProgramCounter(CpuType, true));
if(!EmuApi.IsPaused()) {
//Clear the highlight if the emulation is still running
Disassembly.SetActiveAddress(null);
@ -432,16 +432,21 @@ namespace Mesen.Debugger.ViewModels
new ContextMenuAction() {
ActionType = ActionType.StepOver,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.StepOver),
IsVisible = () => DebugApi.GetDebuggerFeatures(CpuType).StepOver,
AllowedWhenHidden = true,
OnClick = () => Step(StepType.StepOver, 1)
},
new ContextMenuAction() {
ActionType = ActionType.StepOut,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.StepOut),
IsVisible = () => DebugApi.GetDebuggerFeatures(CpuType).StepOut,
AllowedWhenHidden = true,
OnClick = () => Step(StepType.StepOut, 1)
},
new ContextMenuAction() {
ActionType = ActionType.StepBack,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.StepBack),
IsVisible = () => DebugApi.GetDebuggerFeatures(CpuType).StepBack,
IsEnabled = () => false,
OnClick = () => { } //TODO
},
@ -464,16 +469,23 @@ namespace Mesen.Debugger.ViewModels
OnClick = () => Step(StepType.PpuFrame, 1)
},
new ContextMenuSeparator(),
new ContextMenuSeparator() {
IsVisible = () => {
DebuggerFeatures features = DebugApi.GetDebuggerFeatures(CpuType);
return features.RunToNmi || features.RunToIrq;
}
},
new ContextMenuAction() {
ActionType = ActionType.RunToNmi,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.RunToNmi),
IsVisible = () => DebugApi.GetDebuggerFeatures(CpuType).RunToNmi,
OnClick = () => Step(StepType.RunToNmi)
},
new ContextMenuAction() {
ActionType = ActionType.RunToIrq,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.RunToIrq),
IsVisible = () => DebugApi.GetDebuggerFeatures(CpuType).RunToIrq,
OnClick = () => Step(StepType.RunToIrq)
},

View file

@ -18,6 +18,8 @@ namespace Mesen.Debugger.ViewModels
public class DisassemblyViewModel : ViewModelBase
{
public ICodeDataProvider DataProvider { get; }
public CpuType CpuType { get; }
public DebuggerWindowViewModel Debugger { get; }
[Reactive] public BaseStyleProvider StyleProvider { get; set; }
[Reactive] public int ScrollPosition { get; set; } = 0;
@ -38,11 +40,13 @@ namespace Mesen.Debugger.ViewModels
private int _ignoreScrollUpdates = 0;
[Obsolete("For designer only")]
public DisassemblyViewModel(): this(new DebugConfig(), CpuType.Snes) { }
public DisassemblyViewModel(): this(new DebuggerWindowViewModel(), new DebugConfig(), CpuType.Snes) { }
public DisassemblyViewModel(DebugConfig config, CpuType cpuType)
public DisassemblyViewModel(DebuggerWindowViewModel debugger, DebugConfig config, CpuType cpuType)
{
Config = config;
CpuType = cpuType;
Debugger = debugger;
StyleProvider = new BaseStyleProvider(this);
DataProvider = new CodeDataProvider(cpuType);

View file

@ -11,6 +11,7 @@ using Mesen.Debugger.Utilities;
using Mesen.Debugger.ViewModels;
using Mesen.Debugger.Windows;
using Mesen.Interop;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@ -19,6 +20,8 @@ namespace Mesen.Debugger.Views
public class DisassemblyView : UserControl
{
private DisassemblyViewModel Model => (DisassemblyViewModel)DataContext!;
private CpuType CpuType => Model.CpuType;
private LocationInfo? _mouseOverCodeLocation;
private LocationInfo? _contextMenuLocation;
private ContextMenu _bpMarginContextMenu;
@ -57,7 +60,7 @@ namespace Mesen.Debugger.Views
{
_mainContextMenu = DebugShortcutManager.CreateContextMenu(_viewer, new List<ContextMenuAction> {
MarkSelectionHelper.GetAction(
() => Model.DataProvider.CpuType.ToMemoryType(),
() => CpuType.ToMemoryType(),
() => Model.SelectionStart,
() => Model.SelectionEnd,
() => Model.Refresh()
@ -65,10 +68,10 @@ namespace Mesen.Debugger.Views
new ContextMenuAction() {
ActionType = ActionType.EditSelectedCode,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.CodeWindow_EditSelectedCode),
IsEnabled = () => Model.DataProvider.CpuType.SupportsAssembler() && EmuApi.IsPaused(),
IsEnabled = () => CpuType.SupportsAssembler() && EmuApi.IsPaused(),
OnClick = () => {
string code = Model.GetSelection(false, false, true, false, out int byteCount);
AssemblerWindow.EditCode(Model.DataProvider.CpuType, Model.SelectionStart, code, byteCount);
AssemblerWindow.EditCode(CpuType, Model.SelectionStart, code, byteCount);
}
},
new ContextMenuAction() {
@ -84,9 +87,9 @@ namespace Mesen.Debugger.Views
IsEnabled = () => ActionLocation.RelAddress != null || ActionLocation.AbsAddress != null,
OnClick = () => {
if(ActionLocation.AbsAddress != null) {
BreakpointManager.ToggleBreakpoint(ActionLocation.AbsAddress.Value, Model.DataProvider.CpuType);
BreakpointManager.ToggleBreakpoint(ActionLocation.AbsAddress.Value, CpuType);
} else if(ActionLocation.RelAddress != null) {
BreakpointManager.ToggleBreakpoint(ActionLocation.RelAddress.Value, Model.DataProvider.CpuType);
BreakpointManager.ToggleBreakpoint(ActionLocation.RelAddress.Value, CpuType);
}
}
},
@ -97,9 +100,9 @@ namespace Mesen.Debugger.Views
IsEnabled = () => ActionLocation.Label != null || ActionLocation.RelAddress != null,
OnClick = () => {
if(ActionLocation.Label != null) {
WatchManager.GetWatchManager(Model.DataProvider.CpuType).AddWatch("[" + ActionLocation.Label.Label + "]");
WatchManager.GetWatchManager(CpuType).AddWatch("[" + ActionLocation.Label.Label + "]");
} else if(ActionLocation.RelAddress != null) {
WatchManager.GetWatchManager(Model.DataProvider.CpuType).AddWatch("[$" + ActionLocation.RelAddress.Value.Address.ToString(GetFormatString()) + "]");
WatchManager.GetWatchManager(CpuType).AddWatch("[$" + ActionLocation.RelAddress.Value.Address.ToString(GetFormatString()) + "]");
}
}
},
@ -111,9 +114,9 @@ namespace Mesen.Debugger.Views
OnClick = () => {
CodeLabel? label = ActionLocation.Label ?? (ActionLocation.AbsAddress.HasValue ? LabelManager.GetLabel(ActionLocation.AbsAddress.Value) : null);
if(label != null) {
LabelEditWindow.EditLabel(Model.DataProvider.CpuType, this, label);
LabelEditWindow.EditLabel(CpuType, this, label);
} else if(ActionLocation.AbsAddress != null) {
LabelEditWindow.EditLabel(Model.DataProvider.CpuType, this, new CodeLabel(ActionLocation.AbsAddress.Value));
LabelEditWindow.EditLabel(CpuType, this, new CodeLabel(ActionLocation.AbsAddress.Value));
}
}
},
@ -131,6 +134,21 @@ namespace Mesen.Debugger.Views
}
},
new ContextMenuSeparator(),
new ContextMenuAction() {
ActionType = ActionType.MoveProgramCounter,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.CodeWindow_MoveProgramCounter),
HintText = () => GetHint(ActionLocation),
IsEnabled = () => ActionLocation.RelAddress != null && DebugApi.GetDebuggerFeatures(CpuType).ChangeProgramCounter,
OnClick = () => {
if(ActionLocation.RelAddress != null) {
Model.Debugger.UpdateConsoleState();
DebugApi.SetProgramCounter(CpuType, (uint)ActionLocation.RelAddress.Value.Address);
Model.Debugger.ConsoleStatus?.UpdateUiState();
Model.Debugger.UpdateDisassembly(true);
}
}
},
new ContextMenuSeparator(),
new ContextMenuAction() {
ActionType = ActionType.GoToLocation,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.CodeWindow_GoToLocation),
@ -150,7 +168,7 @@ namespace Mesen.Debugger.Views
{
Breakpoint? GetBreakpoint()
{
return ActionLocation.AbsAddress != null ? BreakpointManager.GetMatchingBreakpoint(ActionLocation.AbsAddress.Value, Model.DataProvider.CpuType) : null;
return ActionLocation.AbsAddress != null ? BreakpointManager.GetMatchingBreakpoint(ActionLocation.AbsAddress.Value, CpuType) : null;
}
_bpMarginContextMenu = DebugShortcutManager.CreateContextMenu(_viewer, new List<ContextMenuAction> {
@ -160,7 +178,7 @@ namespace Mesen.Debugger.Views
IsVisible = () => GetBreakpoint() == null,
OnClick = () => {
if(ActionLocation.AbsAddress != null) {
BreakpointManager.ToggleBreakpoint(ActionLocation.AbsAddress.Value, Model.DataProvider.CpuType);
BreakpointManager.ToggleBreakpoint(ActionLocation.AbsAddress.Value, CpuType);
}
}
},
@ -170,7 +188,7 @@ namespace Mesen.Debugger.Views
IsVisible = () => GetBreakpoint() != null,
OnClick = () => {
if(ActionLocation.AbsAddress != null) {
BreakpointManager.ToggleBreakpoint(ActionLocation.AbsAddress.Value, Model.DataProvider.CpuType);
BreakpointManager.ToggleBreakpoint(ActionLocation.AbsAddress.Value, CpuType);
}
}
},
@ -181,7 +199,7 @@ namespace Mesen.Debugger.Views
IsEnabled = () => GetBreakpoint()?.Enabled == false,
OnClick = () => {
if(ActionLocation.AbsAddress != null) {
BreakpointManager.EnableDisableBreakpoint(ActionLocation.AbsAddress.Value, Model.DataProvider.CpuType);
BreakpointManager.EnableDisableBreakpoint(ActionLocation.AbsAddress.Value, CpuType);
}
}
},
@ -192,7 +210,7 @@ namespace Mesen.Debugger.Views
IsEnabled = () => GetBreakpoint()?.Enabled == true,
OnClick = () => {
if(ActionLocation.AbsAddress != null) {
BreakpointManager.EnableDisableBreakpoint(ActionLocation.AbsAddress.Value, Model.DataProvider.CpuType);
BreakpointManager.EnableDisableBreakpoint(ActionLocation.AbsAddress.Value, CpuType);
}
}
},
@ -211,7 +229,7 @@ namespace Mesen.Debugger.Views
private string GetFormatString()
{
return Model.DataProvider.CpuType.ToMemoryType().GetFormatString();
return CpuType.ToMemoryType().GetFormatString();
}
private string GetHint(LocationInfo? codeLoc)
@ -259,7 +277,7 @@ namespace Mesen.Debugger.Views
private LocationInfo GetSelectedRowLocation()
{
CpuType cpuType = Model.DataProvider.CpuType;
CpuType cpuType = CpuType;
AddressInfo relAddress = new AddressInfo() {
Address = Model.SelectedRowAddress,
Type = cpuType.ToMemoryType()
@ -322,6 +340,8 @@ namespace Mesen.Debugger.Views
ToolTip.SetHorizontalOffset(this, 15);
ToolTip.SetIsOpen(this, true);
}
} else {
_mouseOverCodeLocation = null;
}
if(tooltip == null) {

View file

@ -74,6 +74,21 @@ namespace Mesen.Interop
return Marshal.PtrToStructure<T>((IntPtr)ptr);
}
public static BaseState GetCpuState(CpuType cpuType)
{
return cpuType switch {
CpuType.Snes => GetCpuState<SnesCpuState>(cpuType),
CpuType.Spc => GetCpuState<SpcState>(cpuType),
CpuType.NecDsp => GetCpuState<NecDspState>(cpuType),
CpuType.Sa1 => GetCpuState<SnesCpuState>(cpuType),
CpuType.Gsu => GetCpuState<GsuState>(cpuType),
CpuType.Cx4 => GetCpuState<Cx4State>(cpuType),
CpuType.Gameboy => GetCpuState<GbCpuState>(cpuType),
CpuType.Nes => GetCpuState<NesCpuState>(cpuType),
_ => throw new Exception("Unsupport cpu type")
};
}
[DllImport(DllPath)] private static extern void GetPpuState(IntPtr state, CpuType cpuType);
public unsafe static T GetPpuState<T>(CpuType cpuType) where T : struct, BaseState
{
@ -122,6 +137,9 @@ namespace Mesen.Interop
return Marshal.PtrToStructure<T>((IntPtr)ptr);
}
[DllImport(DllPath)] public static extern void SetProgramCounter(CpuType cpuType, UInt32 address);
[DllImport(DllPath)] public static extern UInt32 GetProgramCounter(CpuType cpuType, [MarshalAs(UnmanagedType.I1)] bool getInstPc);
[DllImport(DllPath)] public static extern void SetScriptTimeout(UInt32 timeout);
[DllImport(DllPath)] public static extern Int32 LoadScript(string name, [MarshalAs(UnmanagedType.LPUTF8Str)]string content, Int32 scriptId = -1);
[DllImport(DllPath)] public static extern void RemoveScript(Int32 scriptId);
@ -130,6 +148,8 @@ namespace Mesen.Interop
[DllImport(DllPath)] public static extern Int32 EvaluateExpression([MarshalAs(UnmanagedType.LPUTF8Str)]string expression, CpuType cpuType, out EvalResultType resultType, [MarshalAs(UnmanagedType.I1)]bool useCache);
[DllImport(DllPath)] public static extern DebuggerFeatures GetDebuggerFeatures(CpuType type);
[DllImport(DllPath)] public static extern Int32 GetMemorySize(MemoryType type);
[DllImport(DllPath)] public static extern Byte GetMemoryValue(MemoryType type, UInt32 address);
[DllImport(DllPath)] public static extern void SetMemoryValue(MemoryType type, UInt32 address, byte value);
@ -377,8 +397,13 @@ namespace Mesen.Interop
{
return cpuType switch {
CpuType.Snes => state is SnesCpuState,
CpuType.Nes => state is NesCpuState,
CpuType.Spc => state is SpcState,
CpuType.NecDsp => state is NecDspState,
CpuType.Sa1 => state is SnesCpuState,
CpuType.Gsu => state is GsuState,
CpuType.Cx4 => state is Cx4State,
CpuType.Gameboy => state is GbCpuState,
CpuType.Nes => state is NesCpuState,
_ => false
};
}
@ -858,6 +883,17 @@ namespace Mesen.Interop
public byte[] Format;
}
public struct DebuggerFeatures
{
[MarshalAs(UnmanagedType.I1)] public bool RunToIrq;
[MarshalAs(UnmanagedType.I1)] public bool RunToNmi;
[MarshalAs(UnmanagedType.I1)] public bool StepOver;
[MarshalAs(UnmanagedType.I1)] public bool StepOut;
[MarshalAs(UnmanagedType.I1)] public bool StepBack;
[MarshalAs(UnmanagedType.I1)] public bool ChangeProgramCounter;
[MarshalAs(UnmanagedType.I1)] public bool CallStack;
}
public enum EvalResultType
{
Numeric = 0,

View file

@ -1914,7 +1914,7 @@ x == [$150] || y == [10]
<Value ID="CodeWindow_ViewInMemoryViewer">Code Window: View in Memory Viewer</Value>
<Value ID="CodeWindow_AddToWatch">Code Window: Add to Watch</Value>
<Value ID="CodeWindow_GoToLocation">Code Window: Go to Location</Value>
<Value ID="CodeWindow_SetNextStatement">Code Window: Set Next Statement</Value>
<Value ID="CodeWindow_MoveProgramCounter">Code Window: Move Program Counter</Value>
<Value ID="CodeWindow_EditSelectedCode">Code Window: Edit Selected Code</Value>
<Value ID="CodeWindow_EditSourceFile">Code Window: Edit Source File (Source View)</Value>
<Value ID="CodeWindow_EditLabel">Code Window: Edit Label</Value>
@ -2171,6 +2171,8 @@ x == [$150] || y == [10]
<Value ID="ResetCdl">Reset CDL data</Value>
<Value ID="LoadCdl">Load CDL data from...</Value>
<Value ID="SaveCdl">Save CDL data as...</Value>
<Value ID="MoveProgramCounter">Move Program Counter</Value>
</Enum>
</Enums>
</Resources>