Debugger: SNES - Added coprocessor debuggers

This commit is contained in:
Sour 2022-01-30 20:20:24 -05:00
parent cb2e7ef80e
commit 85482d34f1
31 changed files with 216 additions and 91 deletions

View file

@ -3,6 +3,7 @@
#include "Core/MemoryOperationType.h"
enum class MemoryType;
enum class CpuType : uint8_t;
struct AddressInfo
{
@ -278,6 +279,7 @@ enum class BreakSource
struct BreakEvent
{
BreakSource Source;
CpuType SourceCpu;
MemoryOperationInfo Operation;
int32_t BreakpointId;
};

View file

@ -220,7 +220,7 @@ void Debugger::ProcessPpuCycle()
}
}
void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operation, int breakpointId)
void Debugger::SleepUntilResume(CpuType sourceCpu, BreakSource source, MemoryOperationInfo *operation, int breakpointId)
{
if(_suspendRequestCount) {
return;
@ -234,6 +234,7 @@ void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operati
//Only trigger code break event if the pause was caused by user action
BreakEvent evt = {};
evt.SourceCpu = sourceCpu;
evt.BreakpointId = breakpointId;
evt.Source = source;
if(operation) {
@ -255,14 +256,14 @@ void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operati
_executionStopped = false;
}
void Debugger::ProcessBreakConditions(bool needBreak, BreakpointManager* bpManager, MemoryOperationInfo &operation, AddressInfo &addressInfo, BreakSource source)
void Debugger::ProcessBreakConditions(CpuType sourceCpu, bool needBreak, BreakpointManager* bpManager, MemoryOperationInfo &operation, AddressInfo &addressInfo, BreakSource source)
{
if(needBreak || _breakRequestCount || _waitForBreakResume) {
SleepUntilResume(source);
SleepUntilResume(sourceCpu, source);
} else {
int breakpointId = bpManager->CheckBreakpoint(operation, addressInfo);
if(breakpointId >= 0) {
SleepUntilResume(BreakSource::Breakpoint, &operation, breakpointId);
SleepUntilResume(sourceCpu, BreakSource::Breakpoint, &operation, breakpointId);
}
}
}
@ -393,7 +394,7 @@ void Debugger::SuspendDebugger(bool release)
}
}
void Debugger::BreakImmediately(BreakSource source)
void Debugger::BreakImmediately(CpuType sourceCpu, BreakSource source)
{
//TODO
bool gbDebugger = _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled);
@ -404,7 +405,7 @@ void Debugger::BreakImmediately(BreakSource source)
} else if(source == BreakSource::GbInvalidOamAccess && (!gbDebugger || !_settings->CheckDebuggerFlag(DebuggerFlags::GbBreakOnInvalidOamAccess))) {
return;
}
SleepUntilResume(source);
SleepUntilResume(sourceCpu, source);
}
void Debugger::GetCpuState(BaseState &dstState, CpuType cpuType)

View file

@ -107,10 +107,10 @@ public:
void ResetSuspendCounter();
void SuspendDebugger(bool release);
void BreakImmediately(BreakSource source);
void BreakImmediately(CpuType sourceCpu, BreakSource source);
void ProcessBreakConditions(bool needBreak, BreakpointManager *bpManager, MemoryOperationInfo &operation, AddressInfo &addressInfo, BreakSource source = BreakSource::Unspecified);
void SleepUntilResume(BreakSource source, MemoryOperationInfo* operation = nullptr, int breakpointId = -1);
void ProcessBreakConditions(CpuType sourceCpu, bool needBreak, BreakpointManager *bpManager, MemoryOperationInfo &operation, AddressInfo &addressInfo, BreakSource source = BreakSource::Unspecified);
void SleepUntilResume(CpuType sourceCpu, BreakSource source, MemoryOperationInfo* operation = nullptr, int breakpointId = -1);
void GetCpuState(BaseState& dstState, CpuType cpuType);
void SetCpuState(BaseState& srcState, CpuType cpuType);

View file

@ -134,6 +134,7 @@ vector<DisassemblyResult> Disassembler::Disassemble(CpuType cpuType, uint16_t ba
int32_t bankStart = bank << 16;
int32_t bankEnd = (bank + 1) << 16;
bankEnd = std::min<int32_t>(bankEnd, (int32_t)_memoryDumper->GetMemorySize(relAddress.Type));
AddressInfo addrInfo = {};

View file

@ -70,6 +70,7 @@ uint32_t MemoryDumper::GetMemorySize(MemoryType type)
switch(type) {
case MemoryType::SnesMemory: return 0x1000000;
case MemoryType::SpcMemory: return 0x10000;
case MemoryType::NecDspMemory: return _emu->GetMemory(MemoryType::DspProgramRom).Size;
case MemoryType::Sa1Memory: return 0x1000000;
case MemoryType::GsuMemory: return 0x1000000;
case MemoryType::Cx4Memory: return 0x1000000;
@ -152,6 +153,10 @@ void MemoryDumper::GetMemoryState(MemoryType type, uint8_t *buffer)
break;
}
case MemoryType::NecDspMemory:
GetMemoryState(MemoryType::DspProgramRom, buffer);
break;
default:
uint8_t* src = GetMemoryBuffer(type);
if(src) {
@ -198,6 +203,9 @@ void MemoryDumper::SetMemoryValue(MemoryType memoryType, uint32_t address, uint8
case MemoryType::GameboyMemory: _gameboy->GetMemoryManager()->DebugWrite(address, value); break;
case MemoryType::NesMemory: _nesConsole->DebugWrite(address, value); break;
case MemoryType::NesPpuMemory: _nesConsole->DebugWriteVram(address, value); break;
case MemoryType::NecDspMemory:
SetMemoryValue(MemoryType::DspProgramRom, address, value, disableSideEffects);
return;
default:
uint8_t* src = GetMemoryBuffer(memoryType);
@ -233,7 +241,8 @@ uint8_t MemoryDumper::GetMemoryValue(MemoryType memoryType, uint32_t address, bo
case MemoryType::GameboyMemory: return _gameboy->GetMemoryManager()->DebugRead(address);
case MemoryType::NesMemory: return _nesConsole->DebugRead(address);
case MemoryType::NesPpuMemory: return _nesConsole->DebugReadVram(address);
case MemoryType::NecDspMemory: return GetMemoryValue(MemoryType::DspProgramRom, address);
default:
uint8_t* src = GetMemoryBuffer(memoryType);
return src ? src[address] : 0;

View file

@ -174,14 +174,14 @@ void GbDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType t
}
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo, breakSource);
_debugger->ProcessBreakConditions(CpuType::Gameboy, _step->StepCount == 0, GetBreakpointManager(), operation, addressInfo, breakSource);
}
void GbDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo = _gameboy->GetAbsoluteAddress(addr);
MemoryOperationInfo operation(addr, value, type, MemoryType::GameboyMemory);
_debugger->ProcessBreakConditions(false, GetBreakpointManager(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Gameboy, false, GetBreakpointManager(), operation, addressInfo);
if(addressInfo.Type == MemoryType::GbWorkRam || addressInfo.Type == MemoryType::GbCartRam || addressInfo.Type == MemoryType::GbHighRam) {
_disassembler->InvalidateCache(addressInfo, CpuType::Gameboy);
@ -245,7 +245,7 @@ void GbDebugger::ProcessPpuRead(uint16_t addr, uint8_t value, MemoryType memoryT
{
MemoryOperationInfo operation(addr, value, MemoryOperationType::Read, memoryType);
AddressInfo addressInfo { addr, memoryType };
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Gameboy, false, _breakpointManager.get(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _gameboy->GetMasterClock());
}
@ -253,7 +253,7 @@ void GbDebugger::ProcessPpuWrite(uint16_t addr, uint8_t value, MemoryType memory
{
MemoryOperationInfo operation(addr, value, MemoryOperationType::Write, memoryType);
AddressInfo addressInfo { addr, memoryType };
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Gameboy, false, _breakpointManager.get(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _gameboy->GetMasterClock());
}
@ -265,11 +265,11 @@ void GbDebugger::ProcessPpuCycle()
if(_step->HasRequest) {
if(_step->HasScanlineBreakRequest() && _ppu->GetCycle() == 0 && _ppu->GetScanline() == _step->BreakScanline) {
_debugger->SleepUntilResume(BreakSource::PpuStep);
_debugger->SleepUntilResume(CpuType::Gameboy, BreakSource::PpuStep);
} else if(_step->PpuStepCount > 0) {
_step->PpuStepCount--;
if(_step->PpuStepCount == 0) {
_debugger->SleepUntilResume(BreakSource::PpuStep);
_debugger->SleepUntilResume(CpuType::Gameboy, BreakSource::PpuStep);
}
}
}

View file

@ -721,7 +721,7 @@ void GbPpu::Write(uint16_t addr, uint8_t value)
if(!_state.LcdEnabled) {
//Reset LCD to top of screen when it gets turned off
if(_state.Mode != PpuMode::VBlank) {
_emu->BreakIfDebugging(BreakSource::GbDisableLcdOutsideVblank);
_emu->BreakIfDebugging(CpuType::Gameboy, BreakSource::GbDisableLcdOutsideVblank);
SendFrame();
}
@ -814,7 +814,7 @@ uint8_t GbPpu::ReadVram(uint16_t addr)
_emu->ProcessPpuRead<CpuType::Gameboy>(vramAddr, _vram[vramAddr], MemoryType::GbVideoRam);
return _vram[vramAddr];
} else {
_emu->BreakIfDebugging(BreakSource::GbInvalidVramAccess);
_emu->BreakIfDebugging(CpuType::Gameboy, BreakSource::GbInvalidVramAccess);
return 0xFF;
}
}
@ -831,7 +831,7 @@ void GbPpu::WriteVram(uint16_t addr, uint8_t value)
_emu->ProcessPpuWrite<CpuType::Gameboy>(vramAddr, value, MemoryType::GbVideoRam);
_vram[vramAddr] = value;
} else {
_emu->BreakIfDebugging(BreakSource::GbInvalidVramAccess);
_emu->BreakIfDebugging(CpuType::Gameboy, BreakSource::GbInvalidVramAccess);
}
}
@ -876,7 +876,7 @@ uint8_t GbPpu::ReadOam(uint8_t addr)
_emu->ProcessPpuRead<CpuType::Gameboy>(addr, _oam[addr], MemoryType::GbSpriteRam);
return _oam[addr];
} else {
_emu->BreakIfDebugging(BreakSource::GbInvalidOamAccess);
_emu->BreakIfDebugging(CpuType::Gameboy, BreakSource::GbInvalidOamAccess);
return 0xFF;
}
}
@ -896,7 +896,7 @@ void GbPpu::WriteOam(uint8_t addr, uint8_t value, bool forDma)
_oam[addr] = value;
_emu->ProcessPpuWrite<CpuType::Gameboy>(addr, value, MemoryType::GbSpriteRam);
} else {
_emu->BreakIfDebugging(BreakSource::GbInvalidOamAccess);
_emu->BreakIfDebugging(CpuType::Gameboy, BreakSource::GbInvalidOamAccess);
}
}
}

View file

@ -169,7 +169,7 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
}
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource);
_debugger->ProcessBreakConditions(CpuType::Nes, _step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource);
}
void NesDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
@ -190,7 +190,7 @@ void NesDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _cpu->GetCycleCount());
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Nes, false, _breakpointManager.get(), operation, addressInfo);
}
void NesDebugger::Run()
@ -238,7 +238,7 @@ void NesDebugger::ProcessPpuRead(uint16_t addr, uint8_t value, MemoryType memory
if(DebugUtilities::IsRelativeMemory(memoryType)) {
_mapper->GetPpuAbsoluteAddress(addr, addressInfo);
}
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Nes, false, _breakpointManager.get(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _console->GetMasterClock());
}
@ -249,7 +249,7 @@ void NesDebugger::ProcessPpuWrite(uint16_t addr, uint8_t value, MemoryType memor
if(DebugUtilities::IsRelativeMemory(memoryType)) {
_mapper->GetPpuAbsoluteAddress(addr, addressInfo);
}
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Nes, false, _breakpointManager.get(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _console->GetMasterClock());
}
@ -261,11 +261,11 @@ void NesDebugger::ProcessPpuCycle()
if(_step->HasRequest) {
if(_step->HasScanlineBreakRequest() && _ppu->GetCurrentCycle() == 0 && _ppu->GetCurrentScanline() == _step->BreakScanline) {
_debugger->SleepUntilResume(BreakSource::PpuStep);
_debugger->SleepUntilResume(CpuType::Nes, BreakSource::PpuStep);
} else if(_step->PpuStepCount > 0) {
_step->PpuStepCount--;
if(_step->PpuStepCount == 0) {
_debugger->SleepUntilResume(BreakSource::PpuStep);
_debugger->SleepUntilResume(CpuType::Nes, BreakSource::PpuStep);
}
}
}

View file

@ -294,7 +294,7 @@ uint16_t NesCpu::FetchOperand()
if(_console->GetNesConfig().BreakOnCrash) {
//When "Break on Crash" is enabled, open the debugger and break immediately if a crash occurs
_emu->InitDebugger();
_emu->BreakIfDebugging(BreakSource::BreakOnCpuCrash);
_emu->BreakIfDebugging(CpuType::Nes, BreakSource::BreakOnCpuCrash);
}
if(_console->GetRomFormat() == RomFormat::Nsf) {

View file

@ -1025,7 +1025,7 @@ template<class T> uint8_t NesPpu<T>::ReadSpriteRam(uint8_t addr)
if(_mask.SpritesEnabled) {
if(_settings->CheckDebuggerFlag(DebuggerFlags::NesBreakOnDecayedOamRead)) {
//When debugging with the break on decayed oam read flag turned on, break (only if sprite rendering is enabled to avoid false positives)
_emu->BreakIfDebugging(BreakSource::NesBreakOnDecayedOamRead);
_emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesBreakOnDecayedOamRead);
}
}
//If this 8-byte row hasn't been read/written to in over 3000 cpu cycles (~1.7ms), return 0x10 to simulate decay
@ -1336,12 +1336,12 @@ template<class T> void NesPpu<T>::UpdateState()
if(_cycle == 257) {
_videoRamAddr &= _updateVramAddr;
if(_settings->CheckDebuggerFlag(DebuggerFlags::NesBreakOnPpu2006ScrollGlitch)) {
_emu->BreakIfDebugging(BreakSource::NesBreakOnPpu2006ScrollGlitch);
_emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesBreakOnPpu2006ScrollGlitch);
}
} else if(_cycle > 0 && (_cycle & 0x07) == 0 && (_cycle <= 256 || _cycle > 320)) {
_videoRamAddr = (_updateVramAddr & ~0x41F) | (_videoRamAddr & _updateVramAddr & 0x41F);
if(_settings->CheckDebuggerFlag(DebuggerFlags::NesBreakOnPpu2006ScrollGlitch)) {
_emu->BreakIfDebugging(BreakSource::NesBreakOnPpu2006ScrollGlitch);
_emu->BreakIfDebugging(CpuType::Nes, BreakSource::NesBreakOnPpu2006ScrollGlitch);
}
} else {
_videoRamAddr = _updateVramAddr;

View file

@ -80,14 +80,14 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock());
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Cx4, _step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
}
void Cx4Debugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo = _cx4->GetMemoryMappings()->GetAbsoluteAddress(addr);
MemoryOperationInfo operation(addr, value, type, MemoryType::Cx4Memory);
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Cx4, _step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
@ -126,7 +126,7 @@ BreakpointManager* Cx4Debugger::GetBreakpointManager()
CallstackManager* Cx4Debugger::GetCallstackManager()
{
throw std::runtime_error("Call stack not supported for CX4");
return nullptr;
}
IAssembler* Cx4Debugger::GetAssembler()

View file

@ -83,14 +83,14 @@ void GsuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock());
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Gsu, _step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
}
void GsuDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo = _gsu->GetMemoryMappings()->GetAbsoluteAddress(addr);
MemoryOperationInfo operation(addr, value, type, MemoryType::GsuMemory);
_debugger->ProcessBreakConditions(false, GetBreakpointManager(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Gsu, false, GetBreakpointManager(), operation, addressInfo);
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
@ -129,7 +129,7 @@ BreakpointManager* GsuDebugger::GetBreakpointManager()
CallstackManager* GsuDebugger::GetCallstackManager()
{
throw std::runtime_error("Call stack not supported for GSU");
return nullptr;
}
IAssembler* GsuDebugger::GetAssembler()

View file

@ -54,14 +54,14 @@ void NecDspDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationTy
_step->ProcessCpuExec();
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::NecDsp, _step->StepCount == 0, GetBreakpointManager(), operation, addressInfo);
}
void NecDspDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo { (int32_t)addr, MemoryType::DspDataRam }; //Writes never affect the DSP ROM
MemoryOperationInfo operation(addr, value, type, MemoryType::NecDspMemory);
_debugger->ProcessBreakConditions(false, GetBreakpointManager(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::NecDsp, false, GetBreakpointManager(), operation, addressInfo);
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
@ -95,7 +95,7 @@ void NecDspDebugger::Step(int32_t stepCount, StepType type)
CallstackManager* NecDspDebugger::GetCallstackManager()
{
throw std::runtime_error("Callstack not supported for NEC DSP");
return nullptr;
}
BreakpointManager* NecDspDebugger::GetBreakpointManager()

View file

@ -57,6 +57,9 @@ SnesDebugger::SnesDebugger(Debugger* debugger, CpuType cpuType)
if(cpuType == CpuType::Snes) {
_codeDataLogger.reset(new CodeDataLogger(MemoryType::SnesPrgRom, console->GetCartridge()->DebugGetPrgRomSize(), CpuType::Snes));
_cdl = _codeDataLogger.get();
} else {
_cdl = _debugger->GetCodeDataLogger(CpuType::Snes);
}
_eventManager.reset(new SnesEventManager(debugger, _cpu, console->GetPpu(), _memoryManager, console->GetDmaController()));
@ -105,7 +108,7 @@ void SnesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) {
flags |= CdlFlags::SubEntryPoint;
}
_codeDataLogger->SetFlags(addressInfo.Address, flags);
_cdl->SetFlags(addressInfo.Address, flags);
}
if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(_debuggerEnabledFlag)) {
_disassembler->BuildCache(addressInfo, cpuFlags, _cpuType);
@ -162,7 +165,7 @@ void SnesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
} else if(type == MemoryOperationType::ExecOperand) {
if(addressInfo.Type == MemoryType::SnesPrgRom && addressInfo.Address >= 0) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
_cdl->SetFlags(addressInfo.Address, CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
@ -170,7 +173,7 @@ void SnesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
} else {
if(addressInfo.Type == MemoryType::SnesPrgRom && addressInfo.Address >= 0) {
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
_cdl->SetFlags(addressInfo.Address, CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
}
if(_traceLogger->IsEnabled()) {
_traceLogger->LogNonExec(operation);
@ -191,7 +194,7 @@ void SnesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
}
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource);
_debugger->ProcessBreakConditions(_cpuType, _step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource);
}
void SnesDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
@ -212,7 +215,7 @@ void SnesDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationTyp
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
_debugger->ProcessBreakConditions(_cpuType, false, _breakpointManager.get(), operation, addressInfo);
}
void SnesDebugger::Run()
@ -262,7 +265,7 @@ void SnesDebugger::ProcessPpuRead(uint16_t addr, uint8_t value, MemoryType memor
{
MemoryOperationInfo operation(addr, value, MemoryOperationType::Read, memoryType);
AddressInfo addressInfo { addr, memoryType };
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Snes, false, _breakpointManager.get(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _console->GetMasterClock());
}
@ -270,31 +273,34 @@ void SnesDebugger::ProcessPpuWrite(uint16_t addr, uint8_t value, MemoryType memo
{
MemoryOperationInfo operation(addr, value, MemoryOperationType::Write, memoryType);
AddressInfo addressInfo { addr, memoryType };
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Snes, false, _breakpointManager.get(), operation, addressInfo);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _console->GetMasterClock());
}
void SnesDebugger::ProcessPpuCycle()
{
//Catch up SPC/DSP as needed (if we're tracing or debugging those particular CPUs)
if(_spcTraceLogger->IsEnabled()) {
if(_spcTraceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled)) {
_spc->Run();
}
if(_dspTraceLogger && _dspTraceLogger->IsEnabled()) {
if(_dspTraceLogger && _dspTraceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::NecDspDebuggerEnabled)) {
_cart->RunCoprocessors();
} else if(_settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled)) {
_cart->RunCoprocessors();
}
if(_ppuTools->HasOpenedViewer()) {
_ppuTools->UpdateViewers(_ppu->GetScanline(), _ppu->GetCycle());
}
if(_step->HasRequest) {
if(_step->HasScanlineBreakRequest() && _ppu->GetScanline() == _step->BreakScanline && _memoryManager->GetHClock() == 0) {
_debugger->SleepUntilResume(BreakSource::PpuStep);
_debugger->SleepUntilResume(CpuType::Snes, BreakSource::PpuStep);
} else if(_step->PpuStepCount > 0) {
_step->PpuStepCount--;
if(_step->PpuStepCount == 0) {
_debugger->SleepUntilResume(BreakSource::PpuStep);
_debugger->SleepUntilResume(CpuType::Snes, BreakSource::PpuStep);
}
}
}
@ -341,11 +347,7 @@ BaseEventManager* SnesDebugger::GetEventManager()
CodeDataLogger* SnesDebugger::GetCodeDataLogger()
{
if(_cpuType == CpuType::Sa1) {
return _debugger->GetCodeDataLogger(CpuType::Snes);
} else {
return _codeDataLogger.get();
}
return _cdl;
}
PpuTools* SnesDebugger::GetPpuTools()

View file

@ -41,6 +41,7 @@ class SnesDebugger final : public IDebugger
Spc* _spc;
SnesPpu* _ppu;
MemoryMappings* _memoryMappings;
CodeDataLogger* _cdl;
unique_ptr<CodeDataLogger> _codeDataLogger;
unique_ptr<BaseEventManager> _eventManager;

View file

@ -107,14 +107,14 @@ void SpcDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType
}
}
_debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo, breakSource);
_debugger->ProcessBreakConditions(CpuType::Spc, _step->StepCount == 0, GetBreakpointManager(), operation, addressInfo, breakSource);
}
void SpcDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
{
AddressInfo addressInfo { (int32_t)addr, MemoryType::SpcRam }; //Writes never affect the SPC ROM
MemoryOperationInfo operation(addr, value, type, MemoryType::SpcMemory);
_debugger->ProcessBreakConditions(false, GetBreakpointManager(), operation, addressInfo);
_debugger->ProcessBreakConditions(CpuType::Spc, false, GetBreakpointManager(), operation, addressInfo);
_disassembler->InvalidateCache(addressInfo, CpuType::Spc);

View file

@ -985,10 +985,10 @@ void Emulator::AddDebugEvent(DebugEventType evtType)
}
}
void Emulator::BreakIfDebugging(BreakSource source)
void Emulator::BreakIfDebugging(CpuType sourceCpu, BreakSource source)
{
if(_debugger) {
_debugger->BreakImmediately(source);
_debugger->BreakImmediately(sourceCpu, source);
}
}

View file

@ -273,7 +273,7 @@ public:
template<CpuType type> void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);
void ProcessEvent(EventType type);
template<CpuType cpuType> void AddDebugEvent(DebugEventType evtType);
void BreakIfDebugging(BreakSource source);
void BreakIfDebugging(CpuType sourceCpu, BreakSource source);
};
enum class HashType

View file

@ -80,9 +80,20 @@ extern "C"
DllExport void __stdcall SetBreakpoints(Breakpoint breakpoints[], uint32_t length) { WithDebugger(void, SetBreakpoints(breakpoints, length)); }
DllExport int32_t __stdcall EvaluateExpression(const char* expression, CpuType cpuType, EvalResultType *resultType, bool useCache) { return WithDebugger(int32_t, EvaluateExpression(expression, cpuType, *resultType, useCache)); }
DllExport void __stdcall GetCallstack(CpuType cpuType, StackFrameInfo *callstackArray, uint32_t &callstackSize) { WithDebugger(void, GetCallstackManager(cpuType)->GetCallstack(callstackArray, callstackSize)); }
DllExport void __stdcall GetProfilerData(CpuType cpuType, ProfiledFunction* profilerData, uint32_t& functionCount) { WithDebugger(void, GetCallstackManager(cpuType)->GetProfiler()->GetProfilerData(profilerData, functionCount)); }
DllExport void __stdcall ResetProfiler(CpuType cpuType) { WithDebugger(void, GetCallstackManager(cpuType)->GetProfiler()->Reset()); }
DllExport void __stdcall GetCallstack(CpuType cpuType, StackFrameInfo *callstackArray, uint32_t &callstackSize)
{
callstackSize = 0;
WithToolVoid(GetCallstackManager(cpuType), GetCallstack(callstackArray, callstackSize));
}
DllExport void __stdcall GetProfilerData(CpuType cpuType, ProfiledFunction* profilerData, uint32_t& functionCount)
{
functionCount = 0;
WithToolVoid(GetCallstackManager(cpuType), GetProfiler()->GetProfilerData(profilerData, functionCount));
}
DllExport void __stdcall ResetProfiler(CpuType cpuType) { WithToolVoid(GetCallstackManager(cpuType), GetProfiler()->Reset()); }
DllExport void __stdcall GetConsoleState(BaseState& state, ConsoleType consoleType) { WithDebugger(void, GetConsoleState(state, consoleType)); }
DllExport void __stdcall GetCpuState(BaseState& state, CpuType cpuType) { WithDebugger(void, GetCpuState(state, cpuType)); }

View file

Before

Width:  |  Height:  |  Size: 643 B

After

Width:  |  Height:  |  Size: 643 B

View file

@ -11,6 +11,7 @@ namespace Mesen.Debugger
{
public static uint GetProgramCounter(CpuType cpuType)
{
//TODO move to C++ core
switch(cpuType) {
case CpuType.Snes:
case CpuType.Sa1: {
@ -33,6 +34,21 @@ namespace Mesen.Debugger
return (uint)state.PC;
}
case CpuType.NecDsp: {
NecDspState state = DebugApi.GetCpuState<NecDspState>(cpuType);
return state.PC;
}
case CpuType.Gsu: {
GsuState state = DebugApi.GetCpuState<GsuState>(cpuType);
return 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

@ -356,5 +356,15 @@ namespace Mesen.Debugger.Utilities
[IconFile("SpcDebugger")]
OpenSpcDebugger,
[IconFile("Cx4Debugger")]
OpenCx4Debugger,
[IconFile("NecDspDebugger")]
OpenNecDspDebugger,
[IconFile("GsuDebugger")]
OpenGsuDebugger,
[IconFile("Sa1Debugger")]
OpenSa1Debugger,
[IconFile("GameboyDebugger")]
OpenGameboyDebugger,
}
}

View file

@ -1,4 +1,5 @@
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Dock.Model.Controls;
using Dock.Model.Core;
using Dock.Model.ReactiveUI.Controls;
@ -24,6 +25,10 @@ namespace Mesen.Debugger.ViewModels
{
public class DebuggerWindowViewModel : DisposableViewModel
{
[Reactive] public string Title { get; private set; } = "Debugger";
[Reactive] public WindowIcon? Icon { get; private set; } = null;
[Reactive] public bool IsMainCpuDebugger { get; private set; } = true;
[Reactive] public DebuggerConfig Config { get; private set; }
[Reactive] public DebuggerOptionsViewModel Options { get; private set; }
@ -61,16 +66,24 @@ namespace Mesen.Debugger.ViewModels
if(Design.IsDesignMode) {
CpuType = CpuType.Snes;
} else if(cpuType != null) {
CpuType = cpuType.Value;
} else {
RomInfo romInfo = EmuApi.GetRomInfo();
CpuType = romInfo.ConsoleType.GetMainCpuType();
if(cpuType != null) {
CpuType = cpuType.Value;
if(romInfo.ConsoleType.GetMainCpuType() != CpuType) {
Title = ResourceHelper.GetEnumText(CpuType) + " Debugger";
Icon = new WindowIcon(ImageUtilities.BitmapFromAsset("Assets/" + CpuType.ToString() + "Debugger.png"));
IsMainCpuDebugger = false;
}
} else {
CpuType = romInfo.ConsoleType.GetMainCpuType();
Icon = new WindowIcon(ImageUtilities.BitmapFromAsset("Assets/Debugger.png"));
//TODO temporary - try to load DBG file if it exists
string dbgPath = Path.ChangeExtension(romInfo.RomPath, ".dbg");
if(File.Exists(dbgPath)) {
SourceView = new(DbgImporter.Import(CpuType, romInfo.Format, dbgPath, true, true), CpuType);
//TODO temporary - try to load DBG file if it exists
string dbgPath = Path.ChangeExtension(romInfo.RomPath, ".dbg");
if(File.Exists(dbgPath)) {
SourceView = new(DbgImporter.Import(CpuType, romInfo.Format, dbgPath, true, true), CpuType);
}
}
}
@ -433,7 +446,17 @@ namespace Mesen.Debugger.ViewModels
private void Step(int instructionCount, StepType type)
{
DebugApi.Step(CpuType, instructionCount, type);
switch(type) {
case StepType.PpuStep:
case StepType.PpuScanline:
case StepType.PpuFrame:
DebugApi.Step(CpuType.GetConsoleType().GetMainCpuType(), instructionCount, type);
break;
default:
DebugApi.Step(CpuType, instructionCount, type);
break;
}
}
}
}

View file

@ -17,9 +17,9 @@
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="500"
x:Class="Mesen.Debugger.Windows.DebuggerWindow"
x:DataType="vm:DebuggerWindowViewModel"
Icon="/Assets/Debugger.png"
Title="{CompiledBinding Title}"
Icon="{CompiledBinding Icon}"
Width="400" Height="500"
Title="Debugger"
x:Name="Debugger"
>
<Design.DataContext>

View file

@ -11,6 +11,7 @@ using Mesen.Config;
using System.Runtime.InteropServices;
using Mesen.Debugger.Utilities;
using Mesen.Utilities;
using System.Collections.Generic;
namespace Mesen.Debugger.Windows
{
@ -70,12 +71,16 @@ namespace Mesen.Debugger.Windows
public void ProcessNotification(NotificationEventArgs e)
{
if(_model.Disposed) {
return;
}
switch(e.NotificationType) {
case ConsoleNotificationType.CodeBreak:
BreakEvent evt = Marshal.PtrToStructure<BreakEvent>(e.Parameter);
Dispatcher.UIThread.Post(() => {
_model.UpdateDebugger(true, evt);
if(_model.Config.BringToFrontOnBreak) {
if(_model.Config.BringToFrontOnBreak && evt.SourceCpu == _model.CpuType && evt.Source != BreakSource.PpuStep) {
Activate();
}
});
@ -95,14 +100,26 @@ namespace Mesen.Debugger.Windows
break;
case ConsoleNotificationType.GameLoaded:
if(!EmuApi.GetRomInfo().CpuTypes.Contains(_model.CpuType)) {
_model.Dispose();
_model = new DebuggerWindowViewModel(null);
Dispatcher.UIThread.Post(() => {
_model.InitializeMenu(this);
DataContext = _model;
});
RomInfo romInfo = EmuApi.GetRomInfo();
HashSet<CpuType> cpuTypes = romInfo.CpuTypes;
if(!cpuTypes.Contains(_model.CpuType)) {
if(!_model.IsMainCpuDebugger || _model.CpuType == romInfo.ConsoleType.GetMainCpuType()) {
//Close the debugger window if this is not the main cpu debugger,
//and this cpu is not supported (or it's the main cpu type for this game)
_model.Dispose();
Dispatcher.UIThread.Post(() => {
Dispatcher.UIThread.RunJobs();
Close();
});
} else {
_model.Dispose();
_model = new DebuggerWindowViewModel(null);
Dispatcher.UIThread.Post(() => {
_model.InitializeMenu(this);
DataContext = _model;
});
}
}
if(_model.Config.BreakOnPowerCycleReset) {

View file

@ -937,6 +937,7 @@ namespace Mesen.Interop
public struct BreakEvent
{
public BreakSource Source;
public CpuType SourceCpu;
public MemoryOperationInfo Operation;
public Int32 BreakpointId;
}

View file

@ -273,7 +273,7 @@ namespace Mesen.Interop
[MarshalAs(UnmanagedType.I1)] public bool Sign1;
}
public struct NecDspState
public struct NecDspState : BaseState
{
public UInt16 A;
public NecDspAccFlags FlagsA;
@ -320,7 +320,7 @@ namespace Mesen.Interop
public byte ValidBits;
}
public struct GsuState
public struct GsuState : BaseState
{
public UInt64 CycleCount;
@ -415,7 +415,7 @@ namespace Mesen.Interop
public UInt32 Address;
}
public struct Cx4State
public struct Cx4State : BaseState
{
public UInt64 CycleCount;
public UInt16 PB;

View file

@ -1756,6 +1756,7 @@ x == [$150] || y == [10]
<Value ID="SpcMemory">SPC Memory</Value>
<Value ID="Sa1Memory">SA-1 Memory</Value>
<Value ID="GsuMemory">GSU Memory</Value>
<Value ID="NecDspMemory">DSP-n Memory</Value>
<Value ID="Cx4Memory">CX4 Memory</Value>
<Value ID="SnesPrgRom">PRG ROM</Value>
<Value ID="SnesWorkRam">Work RAM</Value>

View file

@ -66,8 +66,8 @@
<None Remove="Assets\Font.png" />
<None Remove="Assets\Fullscreen.png" />
<None Remove="Assets\Function.png" />
<None Remove="Assets\GameboyDebugger.png" />
<None Remove="Assets\GameboyIcon.png" />
<None Remove="Assets\GbDebugger.png" />
<None Remove="Assets\GsuDebugger.png" />
<None Remove="Assets\HdPack.png" />
<None Remove="Assets\Help.png" />
@ -446,7 +446,7 @@
<AvaloniaResource Include="Assets\Fullscreen.png" />
<AvaloniaResource Include="Assets\Function.png" />
<AvaloniaResource Include="Assets\GameboyIcon.png" />
<AvaloniaResource Include="Assets\GbDebugger.png" />
<AvaloniaResource Include="Assets\GameboyDebugger.png" />
<AvaloniaResource Include="Assets\GsuDebugger.png" />
<AvaloniaResource Include="Assets\HdPack.png" />
<AvaloniaResource Include="Assets\Help.png" />

View file

@ -109,6 +109,36 @@ namespace Mesen.ViewModels
IsVisible = () => MainWindow.RomInfo.CpuTypes.Contains(CpuType.Spc),
OnClick = () => DebugWindowManager.OpenDebugWindow(() => new DebuggerWindow(CpuType.Spc))
},
new ContextMenuAction() {
ActionType = ActionType.OpenCx4Debugger,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.OpenCx4Debugger),
IsVisible = () => MainWindow.RomInfo.CpuTypes.Contains(CpuType.Cx4),
OnClick = () => DebugWindowManager.OpenDebugWindow(() => new DebuggerWindow(CpuType.Cx4))
},
new ContextMenuAction() {
ActionType = ActionType.OpenNecDspDebugger,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.OpenNecDspDebugger),
IsVisible = () => MainWindow.RomInfo.CpuTypes.Contains(CpuType.NecDsp),
OnClick = () => DebugWindowManager.OpenDebugWindow(() => new DebuggerWindow(CpuType.NecDsp))
},
new ContextMenuAction() {
ActionType = ActionType.OpenGsuDebugger,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.OpenGsuDebugger),
IsVisible = () => MainWindow.RomInfo.CpuTypes.Contains(CpuType.Gsu),
OnClick = () => DebugWindowManager.OpenDebugWindow(() => new DebuggerWindow(CpuType.Gsu))
},
new ContextMenuAction() {
ActionType = ActionType.OpenSa1Debugger,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.OpenSa1Debugger),
IsVisible = () => MainWindow.RomInfo.CpuTypes.Contains(CpuType.Sa1),
OnClick = () => DebugWindowManager.OpenDebugWindow(() => new DebuggerWindow(CpuType.Sa1))
},
new ContextMenuAction() {
ActionType = ActionType.OpenGameboyDebugger,
Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.OpenGameboyDebugger),
IsVisible = () => MainWindow.RomInfo.ConsoleType == ConsoleType.Snes && MainWindow.RomInfo.CpuTypes.Contains(CpuType.Gameboy),
OnClick = () => DebugWindowManager.OpenDebugWindow(() => new DebuggerWindow(CpuType.Gameboy))
},
new ContextMenuSeparator(),
new ContextMenuAction() {
ActionType = ActionType.OpenEventViewer,

View file

@ -12,15 +12,15 @@ namespace Mesen.ViewModels
public class DisposableViewModel : ViewModelBase, IDisposable
{
private HashSet<IDisposable> _disposables = new();
private bool _disposed = false;
public bool Disposed { get; private set; } = false;
public void Dispose()
{
if(_disposed) {
if(Disposed) {
return;
}
_disposed = true;
Disposed = true;
foreach(IDisposable obj in _disposables) {
obj.Dispose();