Mesen-SX/Core/Debugger.cpp

1124 lines
30 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "Debugger.h"
2019-02-27 20:33:56 -05:00
#include "DebugTypes.h"
2019-02-15 21:33:13 -05:00
#include "Console.h"
#include "Cpu.h"
#include "Ppu.h"
2019-04-06 17:38:14 -04:00
#include "Spc.h"
#include "Sa1.h"
2019-07-30 22:34:52 -04:00
#include "Gsu.h"
2019-08-03 23:43:51 -04:00
#include "Cx4.h"
#include "NecDsp.h"
#include "Gameboy.h"
#include "CpuDebugger.h"
#include "SpcDebugger.h"
2019-07-30 22:34:52 -04:00
#include "GsuDebugger.h"
2020-02-23 21:50:55 -05:00
#include "NecDspDebugger.h"
2020-02-24 22:00:52 -05:00
#include "Cx4Debugger.h"
#include "GbDebugger.h"
#include "BaseCartridge.h"
#include "MemoryManager.h"
#include "EmuSettings.h"
2019-03-07 20:12:32 -05:00
#include "SoundMixer.h"
#include "MemoryMappings.h"
#include "NotificationManager.h"
#include "CpuTypes.h"
#include "DisassemblyInfo.h"
#include "TraceLogger.h"
2019-02-15 21:33:13 -05:00
#include "MemoryDumper.h"
#include "MemoryAccessCounter.h"
#include "CodeDataLogger.h"
#include "Disassembler.h"
2019-03-01 20:27:49 -05:00
#include "BreakpointManager.h"
2019-03-03 16:34:23 -05:00
#include "PpuTools.h"
2019-03-07 20:12:32 -05:00
#include "EventManager.h"
#include "GbEventManager.h"
2019-03-07 20:12:32 -05:00
#include "EventType.h"
#include "DebugBreakHelper.h"
#include "LabelManager.h"
2019-05-12 21:18:05 -04:00
#include "ScriptManager.h"
2019-03-24 12:05:51 -04:00
#include "CallstackManager.h"
2019-02-27 20:33:56 -05:00
#include "ExpressionEvaluator.h"
2019-10-10 23:54:38 -04:00
#include "InternalRegisters.h"
#include "AluMulDiv.h"
2020-02-11 22:01:06 -05:00
#include "Assembler.h"
#include "Gameboy.h"
#include "GbPpu.h"
2020-06-06 22:27:54 -04:00
#include "GbAssembler.h"
#include "GameboyHeader.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/IpsPatcher.h"
2019-02-15 21:33:13 -05:00
Debugger::Debugger(shared_ptr<Console> console)
{
_console = console;
2019-02-15 21:33:13 -05:00
_cpu = console->GetCpu();
_ppu = console->GetPpu();
2019-04-06 17:38:14 -04:00
_spc = console->GetSpc();
_cart = console->GetCartridge();
_settings = console->GetSettings();
2019-02-15 21:33:13 -05:00
_memoryManager = console->GetMemoryManager();
2019-10-10 23:54:38 -04:00
_dmaController = console->GetDmaController();
_internalRegs = console->GetInternalRegisters();
_gameboy = _cart->GetGameboy();
2019-02-15 21:33:13 -05:00
_labelManager.reset(new LabelManager(this));
_watchExpEval[(int)CpuType::Cpu].reset(new ExpressionEvaluator(this, CpuType::Cpu));
_watchExpEval[(int)CpuType::Spc].reset(new ExpressionEvaluator(this, CpuType::Spc));
_watchExpEval[(int)CpuType::Sa1].reset(new ExpressionEvaluator(this, CpuType::Sa1));
2019-07-30 22:34:52 -04:00
_watchExpEval[(int)CpuType::Gsu].reset(new ExpressionEvaluator(this, CpuType::Gsu));
2020-02-23 21:50:55 -05:00
_watchExpEval[(int)CpuType::NecDsp].reset(new ExpressionEvaluator(this, CpuType::NecDsp));
2020-02-24 22:00:52 -05:00
_watchExpEval[(int)CpuType::Cx4].reset(new ExpressionEvaluator(this, CpuType::Cx4));
_watchExpEval[(int)CpuType::Gameboy].reset(new ExpressionEvaluator(this, CpuType::Gameboy));
2020-06-06 16:13:02 -04:00
_codeDataLogger.reset(new CodeDataLogger(_cart->DebugGetPrgRomSize(), CpuType::Cpu));
2020-02-11 22:01:06 -05:00
_memoryDumper.reset(new MemoryDumper(this));
_disassembler.reset(new Disassembler(console, _codeDataLogger, this));
2019-03-07 20:12:32 -05:00
_traceLogger.reset(new TraceLogger(this, _console));
_memoryAccessCounter.reset(new MemoryAccessCounter(this, console.get()));
2019-03-03 16:34:23 -05:00
_ppuTools.reset(new PpuTools(_console.get(), _ppu.get()));
2019-05-12 21:18:05 -04:00
_scriptManager.reset(new ScriptManager(this));
2020-12-19 23:30:09 +03:00
if (_gameboy)
{
_gbDebugger.reset(new GbDebugger(this));
}
_cpuDebugger.reset(new CpuDebugger(this, CpuType::Cpu));
_spcDebugger.reset(new SpcDebugger(this));
2020-12-19 23:30:09 +03:00
if (_cart->GetSa1())
{
_sa1Debugger.reset(new CpuDebugger(this, CpuType::Sa1));
2020-12-19 23:30:09 +03:00
}
else if (_cart->GetGsu())
{
2019-07-30 22:34:52 -04:00
_gsuDebugger.reset(new GsuDebugger(this));
2020-12-19 23:30:09 +03:00
}
else if (_cart->GetDsp())
{
2020-02-23 21:50:55 -05:00
_necDspDebugger.reset(new NecDspDebugger(this));
2020-12-19 23:30:09 +03:00
}
else if (_cart->GetCx4())
{
2020-02-24 22:00:52 -05:00
_cx4Debugger.reset(new Cx4Debugger(this));
}
2019-05-12 21:18:05 -04:00
_executionStopped = true;
2019-03-07 20:12:32 -05:00
_breakRequestCount = 0;
_suspendRequestCount = 0;
CpuType cpuType = _gbDebugger ? CpuType::Gameboy : CpuType::Cpu;
2020-12-19 23:30:09 +03:00
string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(),
FolderUtilities::GetFilename(
_cart->GetRomInfo().RomFile.GetFileName(), false) + ".cdl");
GetCodeDataLogger(cpuType)->LoadCdlFile(cdlFile, _settings->CheckDebuggerFlag(DebuggerFlags::AutoResetCdl),
_cart->GetCrc32());
RefreshCodeCache();
2020-12-19 23:30:09 +03:00
if (_console->IsPaused())
{
Step(CpuType::Cpu, 1, StepType::Step);
}
_executionStopped = false;
}
Debugger::~Debugger()
{
Release();
}
void Debugger::Release()
{
CpuType cpuType = _gbDebugger ? CpuType::Gameboy : CpuType::Cpu;
2020-12-19 23:30:09 +03:00
string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(),
FolderUtilities::GetFilename(
_cart->GetRomInfo().RomFile.GetFileName(), false) + ".cdl");
GetCodeDataLogger(cpuType)->SaveCdlFile(cdlFile, _cart->GetCrc32());
2020-12-19 23:30:09 +03:00
while (_executionStopped)
{
Run();
}
}
void Debugger::Reset()
{
_memoryAccessCounter->ResetCounts();
_cpuDebugger->Reset();
_spcDebugger->Reset();
2020-12-19 23:30:09 +03:00
if (_sa1Debugger)
{
_sa1Debugger->Reset();
}
}
2020-12-19 23:30:09 +03:00
template <CpuType type>
void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType)
{
2020-12-19 23:30:09 +03:00
switch (type)
{
case CpuType::Cpu: _cpuDebugger->ProcessRead(addr, value, opType);
break;
case CpuType::Spc: _spcDebugger->ProcessRead(addr, value, opType);
break;
case CpuType::NecDsp: _necDspDebugger->ProcessRead(addr, value, opType);
break;
case CpuType::Sa1: _sa1Debugger->ProcessRead(addr, value, opType);
break;
case CpuType::Gsu: _gsuDebugger->ProcessRead(addr, value, opType);
break;
case CpuType::Cx4: _cx4Debugger->ProcessRead(addr, value, opType);
break;
case CpuType::Gameboy: _gbDebugger->ProcessRead(addr, value, opType);
break;
}
if (_scriptManager->HasScript())
{
_scriptManager->ProcessMemoryOperation(addr, value, opType, type);
}
}
2020-12-19 23:30:09 +03:00
template <CpuType type>
void Debugger::ProcessMemoryWrite(uint32_t addr, uint8_t value, MemoryOperationType opType)
{
2020-12-19 23:30:09 +03:00
switch (type)
{
case CpuType::Cpu: _cpuDebugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Spc: _spcDebugger->ProcessWrite(addr, value, opType);
break;
case CpuType::NecDsp: _necDspDebugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Sa1: _sa1Debugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Gsu: _gsuDebugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Cx4: _cx4Debugger->ProcessWrite(addr, value, opType);
break;
case CpuType::Gameboy: _gbDebugger->ProcessWrite(addr, value, opType);
break;
}
if (_scriptManager->HasScript())
{
_scriptManager->ProcessMemoryOperation(addr, value, opType, type);
}
2019-03-01 20:27:49 -05:00
}
void Debugger::ProcessWorkRamRead(uint32_t addr, uint8_t value)
{
2020-12-19 23:30:09 +03:00
AddressInfo addressInfo{(int32_t)addr, SnesMemoryType::WorkRam};
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock());
2020-12-19 23:30:09 +03:00
MemoryOperationInfo operation{0x7E0000 | addr, value, MemoryOperationType::Read};
ProcessBreakConditions(false, _cpuDebugger->GetBreakpointManager(), operation, addressInfo);
2019-03-01 20:27:49 -05:00
}
void Debugger::ProcessWorkRamWrite(uint32_t addr, uint8_t value)
{
2020-12-19 23:30:09 +03:00
AddressInfo addressInfo{(int32_t)addr, SnesMemoryType::WorkRam};
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
2020-12-19 23:30:09 +03:00
MemoryOperationInfo operation{0x7E0000 | addr, value, MemoryOperationType::Write};
ProcessBreakConditions(false, _cpuDebugger->GetBreakpointManager(), operation, addressInfo);
}
2019-03-01 20:27:49 -05:00
void Debugger::ProcessPpuRead(uint16_t addr, uint8_t value, SnesMemoryType memoryType)
{
2020-12-19 23:30:09 +03:00
AddressInfo addressInfo{addr, memoryType};
MemoryOperationInfo operation{addr, value, MemoryOperationType::Read};
BreakpointManager* bpManager = DebugUtilities::ToCpuType(memoryType) == CpuType::Gameboy
? _gbDebugger->GetBreakpointManager()
: _cpuDebugger->GetBreakpointManager();
ProcessBreakConditions(false, bpManager, operation, addressInfo);
_memoryAccessCounter->ProcessMemoryRead(addressInfo, _console->GetMasterClock());
2019-03-01 20:27:49 -05:00
}
void Debugger::ProcessPpuWrite(uint16_t addr, uint8_t value, SnesMemoryType memoryType)
{
2020-12-19 23:30:09 +03:00
AddressInfo addressInfo{addr, memoryType};
MemoryOperationInfo operation{addr, value, MemoryOperationType::Write};
BreakpointManager* bpManager = DebugUtilities::ToCpuType(memoryType) == CpuType::Gameboy
? _gbDebugger->GetBreakpointManager()
: _cpuDebugger->GetBreakpointManager();
ProcessBreakConditions(false, bpManager, operation, addressInfo);
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _console->GetMasterClock());
2019-03-01 20:27:49 -05:00
}
2020-12-19 23:30:09 +03:00
template <CpuType cpuType>
void Debugger::ProcessPpuCycle()
2019-03-03 16:34:23 -05:00
{
uint16_t scanline;
uint16_t cycle;
2020-12-19 23:30:09 +03:00
if (cpuType == CpuType::Gameboy)
{
scanline = _gameboy->GetPpu()->GetScanline();
cycle = _gameboy->GetPpu()->GetCycle();
2020-12-19 23:30:09 +03:00
}
else
{
scanline = _ppu->GetScanline();
cycle = _memoryManager->GetHClock();
}
2019-03-30 22:58:57 -04:00
_ppuTools->UpdateViewers(scanline, cycle, cpuType);
2020-12-19 23:30:09 +03:00
switch (cpuType)
{
case CpuType::Cpu:
//Catch up SPC/DSP as needed (if we're tracing or debugging those particular CPUs)
if (_traceLogger->IsCpuLogged(CpuType::Spc) || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled))
{
_spc->Run();
}
else if (_traceLogger->IsCpuLogged(CpuType::NecDsp))
{
_cart->RunCoprocessors();
}
2020-12-19 23:30:09 +03:00
_cpuDebugger->ProcessPpuCycle(scanline, cycle);
break;
2020-12-19 23:30:09 +03:00
case CpuType::Gameboy:
_gbDebugger->ProcessPpuCycle(scanline, cycle);
break;
2019-03-30 22:58:57 -04:00
}
2020-12-19 23:30:09 +03:00
if (_breakRequestCount > 0)
{
SleepUntilResume(BreakSource::Unspecified);
}
}
2020-12-19 23:30:09 +03:00
void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo* operation, int breakpointId)
{
2020-12-19 23:30:09 +03:00
if (_suspendRequestCount)
{
return;
}
_console->GetSoundMixer()->StopAudio();
_disassembler->Disassemble(CpuType::Cpu);
_disassembler->Disassemble(CpuType::Spc);
2020-12-19 23:30:09 +03:00
if (_cart->GetSa1())
{
_disassembler->Disassemble(CpuType::Sa1);
2020-12-19 23:30:09 +03:00
}
else if (_cart->GetGsu())
{
2019-07-30 22:34:52 -04:00
_disassembler->Disassemble(CpuType::Gsu);
2020-12-19 23:30:09 +03:00
}
else if (_cart->GetDsp())
{
2020-02-23 21:50:55 -05:00
_disassembler->Disassemble(CpuType::NecDsp);
2020-12-19 23:30:09 +03:00
}
else if (_cart->GetCx4())
{
2020-02-24 22:00:52 -05:00
_disassembler->Disassemble(CpuType::Cx4);
2020-12-19 23:30:09 +03:00
}
else if (_cart->GetGameboy())
{
_disassembler->RefreshDisassembly(CpuType::Gameboy);
}
_executionStopped = true;
2020-12-19 23:30:09 +03:00
if (source != BreakSource::Unspecified || _breakRequestCount == 0)
{
//Only trigger code break event if the pause was caused by user action
BreakEvent evt = {};
evt.BreakpointId = breakpointId;
evt.Source = source;
2020-12-19 23:30:09 +03:00
if (operation)
{
evt.Operation = *operation;
}
_waitForBreakResume = true;
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak, &evt);
}
2020-12-19 23:30:09 +03:00
while ((_waitForBreakResume && !_suspendRequestCount) || _breakRequestCount)
{
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(10));
}
_executionStopped = false;
}
2020-12-19 23:30:09 +03:00
void Debugger::ProcessBreakConditions(bool needBreak, BreakpointManager* bpManager, MemoryOperationInfo& operation,
AddressInfo& addressInfo, BreakSource source)
2019-03-01 20:27:49 -05:00
{
2020-12-19 23:30:09 +03:00
if (needBreak || _breakRequestCount || _waitForBreakResume)
{
SleepUntilResume(source);
2020-12-19 23:30:09 +03:00
}
else
{
int breakpointId = bpManager->CheckBreakpoint(operation, addressInfo);
2020-12-19 23:30:09 +03:00
if (breakpointId >= 0)
{
SleepUntilResume(BreakSource::Breakpoint, &operation, breakpointId);
}
2019-03-07 20:12:32 -05:00
}
}
2020-12-19 23:30:09 +03:00
template <CpuType type>
void Debugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
2019-03-24 12:05:51 -04:00
{
2020-12-19 23:30:09 +03:00
switch (type)
{
case CpuType::Cpu: _cpuDebugger->ProcessInterrupt(originalPc, currentPc, forNmi);
break;
case CpuType::Sa1: _sa1Debugger->ProcessInterrupt(originalPc, currentPc, forNmi);
break;
case CpuType::Gameboy: _gbDebugger->ProcessInterrupt(originalPc, currentPc);
break;
}
ProcessEvent(forNmi ? EventType::Nmi : EventType::Irq);
2019-03-24 12:05:51 -04:00
}
2019-03-07 20:12:32 -05:00
void Debugger::ProcessEvent(EventType type)
{
2019-05-12 21:18:05 -04:00
_scriptManager->ProcessEvent(type);
2020-12-19 23:30:09 +03:00
switch (type)
{
default: break;
2020-12-19 23:30:09 +03:00
case EventType::StartFrame:
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh,
(void*)CpuType::Cpu);
GetEventManager(CpuType::Cpu)->ClearFrameEvents();
break;
2020-12-19 23:30:09 +03:00
case EventType::GbStartFrame:
if (_settings->CheckFlag(EmulationFlags::GameboyMode))
{
_scriptManager->ProcessEvent(EventType::StartFrame);
}
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh,
(void*)CpuType::Gameboy);
GetEventManager(CpuType::Gameboy)->ClearFrameEvents();
break;
case EventType::GbEndFrame:
if (_settings->CheckFlag(EmulationFlags::GameboyMode))
{
_scriptManager->ProcessEvent(EventType::EndFrame);
}
break;
2020-12-19 23:30:09 +03:00
case EventType::Reset:
Reset();
break;
2020-12-19 23:30:09 +03:00
case EventType::StateLoaded:
_memoryAccessCounter->ResetCounts();
break;
2019-03-01 20:27:49 -05:00
}
}
2020-12-19 23:30:09 +03:00
int32_t Debugger::EvaluateExpression(string expression, CpuType cpuType, EvalResultType& resultType, bool useCache)
2019-02-27 20:33:56 -05:00
{
2020-12-19 23:30:09 +03:00
MemoryOperationInfo operationInfo{0, 0, MemoryOperationType::Read};
2019-02-27 20:33:56 -05:00
DebugState state;
GetState(state, false);
2020-12-19 23:30:09 +03:00
if (useCache)
{
return _watchExpEval[(int)cpuType]->Evaluate(expression, state, resultType, operationInfo);
2020-12-19 23:30:09 +03:00
}
else
{
ExpressionEvaluator expEval(this, cpuType);
2019-02-27 20:33:56 -05:00
return expEval.Evaluate(expression, state, resultType, operationInfo);
}
}
void Debugger::Run()
{
_cpuDebugger->Run();
_spcDebugger->Run();
2020-12-19 23:30:09 +03:00
if (_sa1Debugger)
{
_sa1Debugger->Run();
}
2020-12-19 23:30:09 +03:00
if (_gsuDebugger)
{
2019-07-30 22:34:52 -04:00
_gsuDebugger->Run();
}
2020-12-19 23:30:09 +03:00
if (_necDspDebugger)
{
2020-02-23 21:50:55 -05:00
_necDspDebugger->Run();
}
2020-12-19 23:30:09 +03:00
if (_cx4Debugger)
{
2020-02-24 22:00:52 -05:00
_cx4Debugger->Run();
2020-12-19 23:30:09 +03:00
}
if (_gbDebugger)
{
_gbDebugger->Run();
2020-02-24 22:00:52 -05:00
}
_waitForBreakResume = false;
}
void Debugger::Step(CpuType cpuType, int32_t stepCount, StepType type)
{
DebugBreakHelper helper(this);
2019-07-18 18:35:35 -04:00
StepRequest step;
2020-12-19 23:30:09 +03:00
IDebugger* debugger = nullptr;
switch (cpuType)
{
case CpuType::Cpu: debugger = _cpuDebugger.get();
break;
case CpuType::Spc: debugger = _spcDebugger.get();
break;
case CpuType::NecDsp: debugger = _necDspDebugger.get();
break;
case CpuType::Sa1: debugger = _sa1Debugger.get();
break;
case CpuType::Gsu: debugger = _gsuDebugger.get();
break;
case CpuType::Cx4: debugger = _cx4Debugger.get();
break;
case CpuType::Gameboy: debugger = _gbDebugger.get();
break;
}
2020-12-19 23:30:09 +03:00
if (debugger)
{
debugger->Step(stepCount, type);
}
2020-12-19 23:30:09 +03:00
if (debugger != _cpuDebugger.get())
{
_cpuDebugger->Run();
}
2020-12-19 23:30:09 +03:00
if (debugger != _spcDebugger.get())
{
_spcDebugger->Run();
}
2020-12-19 23:30:09 +03:00
if (_sa1Debugger && debugger != _sa1Debugger.get())
{
_sa1Debugger->Run();
}
2020-12-19 23:30:09 +03:00
if (_gsuDebugger && debugger != _gsuDebugger.get())
{
_gsuDebugger->Run();
}
2020-12-19 23:30:09 +03:00
if (_necDspDebugger && debugger != _necDspDebugger.get())
{
2020-02-23 21:50:55 -05:00
_necDspDebugger->Run();
}
2020-12-19 23:30:09 +03:00
if (_cx4Debugger && debugger != _cx4Debugger.get())
{
2020-02-24 22:00:52 -05:00
_cx4Debugger->Run();
}
2020-12-19 23:30:09 +03:00
if (_gbDebugger && debugger != _gbDebugger.get())
{
_gbDebugger->Run();
}
_waitForBreakResume = false;
}
bool Debugger::IsExecutionStopped()
{
return _executionStopped || _console->IsThreadPaused();
}
bool Debugger::HasBreakRequest()
{
return _breakRequestCount > 0;
2019-03-07 20:12:32 -05:00
}
void Debugger::BreakRequest(bool release)
{
2020-12-19 23:30:09 +03:00
if (release)
{
2019-03-07 20:12:32 -05:00
_breakRequestCount--;
2020-12-19 23:30:09 +03:00
}
else
{
2019-03-07 20:12:32 -05:00
_breakRequestCount++;
}
}
void Debugger::SuspendDebugger(bool release)
{
2020-12-19 23:30:09 +03:00
if (release)
{
if (_suspendRequestCount > 0)
{
_suspendRequestCount--;
2020-12-19 23:30:09 +03:00
}
else
{
#ifdef _DEBUG
//throw std::runtime_error("unexpected debugger suspend::release call");
2020-12-19 23:30:09 +03:00
#endif
}
2020-12-19 23:30:09 +03:00
}
else
{
_suspendRequestCount++;
}
}
void Debugger::BreakImmediately(BreakSource source)
{
bool gbDebugger = _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled);
2020-12-19 23:30:09 +03:00
if (source == BreakSource::GbDisableLcdOutsideVblank && (!gbDebugger || !_settings->CheckDebuggerFlag(
DebuggerFlags::GbBreakOnDisableLcdOutsideVblank)))
{
return;
2020-12-19 23:30:09 +03:00
}
else if (source == BreakSource::GbInvalidVramAccess && (!gbDebugger || !_settings->CheckDebuggerFlag(
DebuggerFlags::GbBreakOnInvalidVramAccess)))
{
return;
2020-12-19 23:30:09 +03:00
}
else if (source == BreakSource::GbInvalidOamAccess && (!gbDebugger || !_settings->CheckDebuggerFlag(
DebuggerFlags::GbBreakOnInvalidOamAccess)))
{
return;
}
SleepUntilResume(source);
}
2020-12-19 23:30:09 +03:00
void Debugger::GetState(DebugState& state, bool partialPpuState)
{
state.MasterClock = _console->GetMasterClock();
2019-03-01 20:27:49 -05:00
state.Cpu = _cpu->GetState();
_ppu->GetState(state.Ppu, partialPpuState);
2019-04-06 17:38:14 -04:00
state.Spc = _spc->GetState();
state.Dsp = _spc->GetDspState();
2019-10-10 23:54:38 -04:00
2020-12-19 23:30:09 +03:00
if (!partialPpuState)
{
for (int i = 0; i < 8; i++)
{
state.DmaChannels[i] = _dmaController->GetChannelConfig(i);
}
state.InternalRegs = _internalRegs->GetState();
state.Alu = _internalRegs->GetAluState();
2019-10-10 23:54:38 -04:00
}
2020-12-19 23:30:09 +03:00
if (_cart->GetDsp())
{
state.NecDsp = _cart->GetDsp()->GetState();
}
2020-12-19 23:30:09 +03:00
if (_cart->GetSa1())
{
state.Sa1 = _cart->GetSa1()->GetState();
}
2020-12-19 23:30:09 +03:00
if (_cart->GetGsu())
{
2019-07-30 22:34:52 -04:00
state.Gsu = _cart->GetGsu()->GetState();
}
2020-12-19 23:30:09 +03:00
if (_cart->GetCx4())
{
2019-08-03 23:43:51 -04:00
state.Cx4 = _cart->GetCx4()->GetState();
}
2020-12-19 23:30:09 +03:00
if (_cart->GetGameboy())
{
state.Gameboy = _cart->GetGameboy()->GetState();
}
}
bool Debugger::GetCpuProcFlag(ProcFlags::ProcFlags flag)
{
return _cpu->GetCpuProcFlag(flag);
}
2020-10-11 13:57:01 +03:00
void Debugger::SetCpuRegister(CpuRegister reg, uint16_t value)
{
_cpu->SetReg(reg, value);
}
2020-10-12 18:19:46 +03:00
void Debugger::SetCpuProcFlag(ProcFlags::ProcFlags flag, bool set)
{
_cpu->SetCpuProcFlag(flag, set);
}
2020-10-11 13:57:01 +03:00
void Debugger::SetCx4Register(Cx4Register reg, uint32_t value)
{
_cart->GetCx4()->SetReg(reg, value);
}
void Debugger::SetGameboyRegister(GbRegister reg, uint16_t value)
{
_cart->GetGameboy()->SetReg(reg, value);
}
void Debugger::SetGsuRegister(GsuRegister reg, uint16_t value)
{
_cart->GetGsu()->SetReg(reg, value);
}
void Debugger::SetNecDspRegister(NecDspRegister reg, uint16_t value)
{
_cart->GetDsp()->SetReg(reg, value);
}
void Debugger::SetSa1Register(CpuRegister reg, uint16_t value)
{
_cart->GetSa1()->SetReg(reg, value);
}
2020-10-11 14:26:01 -04:00
void Debugger::SetSpcRegister(SpcRegister reg, uint16_t value)
2020-10-11 13:57:01 +03:00
{
_spc->SetReg(reg, value);
}
AddressInfo Debugger::GetAbsoluteAddress(AddressInfo relAddress)
{
2020-12-19 23:30:09 +03:00
if (relAddress.Type == SnesMemoryType::CpuMemory)
{
if (_memoryManager->IsRegister(relAddress.Address))
{
return {relAddress.Address & 0xFFFF, SnesMemoryType::Register};
}
else
{
return _memoryManager->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
}
2020-12-19 23:30:09 +03:00
}
else if (relAddress.Type == SnesMemoryType::SpcMemory)
{
return _spc->GetAbsoluteAddress(relAddress.Address);
2020-12-19 23:30:09 +03:00
}
else if (relAddress.Type == SnesMemoryType::Sa1Memory)
{
return _cart->GetSa1()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
2020-12-19 23:30:09 +03:00
}
else if (relAddress.Type == SnesMemoryType::GsuMemory)
{
2019-07-30 22:34:52 -04:00
return _cart->GetGsu()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
2020-12-19 23:30:09 +03:00
}
else if (relAddress.Type == SnesMemoryType::Cx4Memory)
{
2020-02-24 22:00:52 -05:00
return _cart->GetCx4()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address);
2020-12-19 23:30:09 +03:00
}
else if (relAddress.Type == SnesMemoryType::NecDspMemory)
{
return {relAddress.Address, SnesMemoryType::DspProgramRom};
}
else if (relAddress.Type == SnesMemoryType::GameboyMemory)
{
return _cart->GetGameboy()->GetAbsoluteAddress(relAddress.Address);
}
throw std::runtime_error("Unsupported address type");
}
AddressInfo Debugger::GetRelativeAddress(AddressInfo absAddress, CpuType cpuType)
{
MemoryMappings* mappings = nullptr;
2020-12-19 23:30:09 +03:00
switch (cpuType)
{
case CpuType::Cpu: mappings = _memoryManager->GetMemoryMappings();
break;
case CpuType::Spc: break;
case CpuType::NecDsp: break;
case CpuType::Sa1: mappings = _cart->GetSa1()->GetMemoryMappings();
break;
case CpuType::Gsu: mappings = _cart->GetGsu()->GetMemoryMappings();
break;
case CpuType::Cx4: mappings = _cart->GetCx4()->GetMemoryMappings();
break;
case CpuType::Gameboy: break;
}
switch (absAddress.Type)
{
case SnesMemoryType::PrgRom:
case SnesMemoryType::WorkRam:
case SnesMemoryType::SaveRam:
{
if (!mappings)
{
throw std::runtime_error("Unsupported cpu type");
}
uint8_t startBank = 0;
//Try to find a mirror close to where the PC is
2020-12-19 23:30:09 +03:00
if (cpuType == CpuType::Cpu)
{
if (absAddress.Type == SnesMemoryType::WorkRam)
{
startBank = 0x7E;
2020-12-19 23:30:09 +03:00
}
else
{
startBank = _cpu->GetState().K & 0xC0;
}
2020-12-19 23:30:09 +03:00
}
else if (cpuType == CpuType::Sa1)
{
startBank = (_cart->GetSa1()->GetCpuState().K & 0xC0);
2020-12-19 23:30:09 +03:00
}
else if (cpuType == CpuType::Gsu)
{
startBank = (_cart->GetGsu()->GetState().ProgramBank & 0xC0);
}
2020-12-19 23:30:09 +03:00
return {mappings->GetRelativeAddress(absAddress, startBank), DebugUtilities::GetCpuMemoryType(cpuType)};
}
2020-12-19 23:30:09 +03:00
case SnesMemoryType::SpcRam:
case SnesMemoryType::SpcRom:
return {_spc->GetRelativeAddress(absAddress), SnesMemoryType::SpcMemory};
2020-02-23 21:50:55 -05:00
2020-12-19 23:30:09 +03:00
case SnesMemoryType::GbPrgRom:
case SnesMemoryType::GbWorkRam:
case SnesMemoryType::GbCartRam:
case SnesMemoryType::GbHighRam:
case SnesMemoryType::GbBootRom:
return {_cart->GetGameboy()->GetRelativeAddress(absAddress), SnesMemoryType::GameboyMemory};
2020-02-23 21:50:55 -05:00
2020-12-19 23:30:09 +03:00
case SnesMemoryType::DspProgramRom:
return {absAddress.Address, SnesMemoryType::NecDspMemory};
2020-12-19 23:30:09 +03:00
case SnesMemoryType::Register:
return {absAddress.Address & 0xFFFF, SnesMemoryType::Register};
default:
return {-1, SnesMemoryType::Register};
}
}
2020-12-19 23:30:09 +03:00
void Debugger::SetCdlData(CpuType cpuType, uint8_t* cdlData, uint32_t length)
{
DebugBreakHelper helper(this);
GetCodeDataLogger(cpuType)->SetCdlData(cdlData, length);
RefreshCodeCache();
}
void Debugger::MarkBytesAs(CpuType cpuType, uint32_t start, uint32_t end, uint8_t flags)
{
DebugBreakHelper helper(this);
GetCodeDataLogger(cpuType)->MarkBytesAs(start, end, flags);
RefreshCodeCache();
}
void Debugger::RefreshCodeCache()
{
_disassembler->ResetPrgCache();
RebuildPrgCache(CpuType::Cpu);
RebuildPrgCache(CpuType::Gameboy);
}
void Debugger::RebuildPrgCache(CpuType cpuType)
{
shared_ptr<CodeDataLogger> cdl = GetCodeDataLogger(cpuType);
2020-12-19 23:30:09 +03:00
if (!cdl)
{
return;
}
2020-06-06 16:13:02 -04:00
uint32_t prgRomSize = cdl->GetPrgSize();
AddressInfo addrInfo;
addrInfo.Type = cpuType == CpuType::Gameboy ? SnesMemoryType::GbPrgRom : SnesMemoryType::PrgRom;
2020-12-19 23:30:09 +03:00
for (uint32_t i = 0; i < prgRomSize; i++)
{
if (cdl->IsCode(i))
{
addrInfo.Address = (int32_t)i;
2020-06-06 16:13:02 -04:00
i += _disassembler->BuildCache(addrInfo, cdl->GetCpuFlags(i), cdl->GetCpuType(i)) - 1;
2020-02-25 23:56:55 -05:00
}
}
}
void Debugger::GetCdlData(uint32_t offset, uint32_t length, SnesMemoryType memoryType, uint8_t* cdlData)
{
CpuType cpuType = DebugUtilities::ToCpuType(memoryType);
shared_ptr<CodeDataLogger> cdl = GetCodeDataLogger(cpuType);
2020-12-19 23:30:09 +03:00
if (memoryType == SnesMemoryType::PrgRom || memoryType == SnesMemoryType::GbPrgRom)
{
2020-06-06 16:13:02 -04:00
cdl->GetCdlData(offset, length, cdlData);
2020-12-19 23:30:09 +03:00
}
else
{
2020-06-06 16:13:02 -04:00
SnesMemoryType prgType = _gbDebugger ? SnesMemoryType::GbPrgRom : SnesMemoryType::PrgRom;
2020-02-25 23:56:55 -05:00
2020-06-06 16:13:02 -04:00
AddressInfo relAddress;
relAddress.Type = memoryType;
2020-12-19 23:30:09 +03:00
for (uint32_t i = 0; i < length; i++)
{
2020-06-06 16:13:02 -04:00
relAddress.Address = offset + i;
AddressInfo info = GetAbsoluteAddress(relAddress);
cdlData[i] = info.Type == prgType ? cdl->GetFlags(info.Address) : 0;
}
}
}
void Debugger::SetBreakpoints(Breakpoint breakpoints[], uint32_t length)
{
_cpuDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
_spcDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
2020-12-19 23:30:09 +03:00
if (_gsuDebugger)
{
_gsuDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
2020-12-19 23:30:09 +03:00
if (_sa1Debugger)
{
_sa1Debugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
2020-12-19 23:30:09 +03:00
if (_necDspDebugger)
{
2020-02-23 21:50:55 -05:00
_necDspDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
2020-12-19 23:30:09 +03:00
if (_cx4Debugger)
{
2020-02-24 22:00:52 -05:00
_cx4Debugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
2020-12-19 23:30:09 +03:00
if (_gbDebugger)
{
_gbDebugger->GetBreakpointManager()->SetBreakpoints(breakpoints, length);
}
}
void Debugger::GetBreakpoints(CpuType cpuType, Breakpoint* breakpoints, int& execs, int& reads, int& writes)
{
2020-12-19 23:30:09 +03:00
switch (cpuType)
{
case CpuType::Cpu: return _cpuDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
case CpuType::Spc: return _spcDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
2020-12-19 23:30:09 +03:00
case CpuType::Gsu:
{
if (_gsuDebugger)
{
return _gsuDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
}
2020-12-19 23:30:09 +03:00
break;
case CpuType::Sa1:
{
if (_sa1Debugger)
{
return _sa1Debugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
}
2020-12-19 23:30:09 +03:00
break;
case CpuType::NecDsp:
{
if (_necDspDebugger)
{
return _necDspDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
}
2020-12-19 23:30:09 +03:00
break;
case CpuType::Cx4:
{
if (_cx4Debugger)
{
return _cx4Debugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
}
2020-12-19 23:30:09 +03:00
break;
case CpuType::Gameboy:
{
if (_gbDebugger)
{
return _gbDebugger->GetBreakpointManager()->GetBreakpoints(breakpoints, reads, writes, execs);
}
}
2020-12-19 23:30:09 +03:00
break;
}
}
void Debugger::Log(string message)
{
auto lock = _logLock.AcquireSafe();
2020-12-19 23:30:09 +03:00
if (_debuggerLog.size() >= 1000)
{
_debuggerLog.pop_front();
}
_debuggerLog.push_back(message);
}
string Debugger::GetLog()
{
auto lock = _logLock.AcquireSafe();
stringstream ss;
2020-12-19 23:30:09 +03:00
for (string& msg : _debuggerLog)
{
ss << msg << "\n";
}
return ss.str();
}
void Debugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption)
{
vector<uint8_t> output;
RomInfo romInfo = _cart->GetRomInfo();
Gameboy* gb = _cart->GetGameboy();
vector<uint8_t> rom;
2020-12-19 23:30:09 +03:00
if (gb)
{
uint8_t* prgRom = gb->DebugGetMemory(SnesMemoryType::GbPrgRom);
uint32_t prgRomSize = gb->DebugGetMemorySize(SnesMemoryType::GbPrgRom);
2020-12-19 23:30:09 +03:00
rom = vector<uint8_t>(prgRom, prgRom + prgRomSize);
}
else
{
rom = vector<uint8_t>(_cart->DebugGetPrgRom(), _cart->DebugGetPrgRom() + _cart->DebugGetPrgRomSize());
}
2020-12-19 23:30:09 +03:00
if (saveAsIps)
{
output = IpsPatcher::CreatePatch(_cart->GetOriginalPrgRom(), rom);
2020-12-19 23:30:09 +03:00
}
else
{
if (stripOption != CdlStripOption::StripNone)
{
GetCodeDataLogger(gb ? CpuType::Gameboy : CpuType::Cpu)->StripData(rom.data(), stripOption);
//Preserve rom header regardless of CDL file contents
2020-12-19 23:30:09 +03:00
if (gb)
{
GameboyHeader header = gb->GetHeader();
memcpy(rom.data() + romInfo.HeaderOffset, &header, sizeof(GameboyHeader));
2020-12-19 23:30:09 +03:00
}
else
{
memcpy(rom.data() + romInfo.HeaderOffset, &romInfo.Header, sizeof(SnesCartInformation));
}
}
output = rom;
}
ofstream file(filename, ios::out | ios::binary);
2020-12-19 23:30:09 +03:00
if (file)
{
file.write((char*)output.data(), output.size());
file.close();
}
}
shared_ptr<TraceLogger> Debugger::GetTraceLogger()
{
return _traceLogger;
2019-02-15 21:33:13 -05:00
}
shared_ptr<MemoryDumper> Debugger::GetMemoryDumper()
{
return _memoryDumper;
}
shared_ptr<MemoryAccessCounter> Debugger::GetMemoryAccessCounter()
{
return _memoryAccessCounter;
}
shared_ptr<CodeDataLogger> Debugger::GetCodeDataLogger(CpuType cpuType)
{
2020-12-19 23:30:09 +03:00
if (cpuType == CpuType::Gameboy)
{
return _gbDebugger ? _gbDebugger->GetCodeDataLogger() : nullptr;
2020-12-19 23:30:09 +03:00
}
else
{
2020-06-06 16:13:02 -04:00
return _codeDataLogger;
}
}
shared_ptr<Disassembler> Debugger::GetDisassembler()
{
return _disassembler;
}
2019-03-01 20:27:49 -05:00
2019-03-03 16:34:23 -05:00
shared_ptr<PpuTools> Debugger::GetPpuTools()
{
return _ppuTools;
}
2019-03-07 20:12:32 -05:00
shared_ptr<IEventManager> Debugger::GetEventManager(CpuType cpuType)
2019-03-07 20:12:32 -05:00
{
2020-12-19 23:30:09 +03:00
if (cpuType == CpuType::Gameboy)
{
return std::dynamic_pointer_cast<IEventManager>(_gbDebugger->GetEventManager());
2020-12-19 23:30:09 +03:00
}
else
{
return std::dynamic_pointer_cast<IEventManager>(_cpuDebugger->GetEventManager());
}
2019-03-07 20:12:32 -05:00
}
shared_ptr<LabelManager> Debugger::GetLabelManager()
{
return _labelManager;
}
2019-05-12 21:18:05 -04:00
shared_ptr<ScriptManager> Debugger::GetScriptManager()
{
return _scriptManager;
}
shared_ptr<CallstackManager> Debugger::GetCallstackManager(CpuType cpuType)
2019-03-24 12:05:51 -04:00
{
2020-12-19 23:30:09 +03:00
switch (cpuType)
{
case CpuType::Cpu: return _cpuDebugger->GetCallstackManager();
case CpuType::Spc: return _spcDebugger->GetCallstackManager();
case CpuType::Sa1: return _sa1Debugger->GetCallstackManager();
case CpuType::Gameboy: return _gbDebugger->GetCallstackManager();
case CpuType::Gsu:
case CpuType::NecDsp:
case CpuType::Cx4:
break;
}
throw std::runtime_error("GetCallstackManager() - Unsupported CPU type");
2019-03-24 12:05:51 -04:00
}
2019-03-07 20:12:32 -05:00
shared_ptr<Console> Debugger::GetConsole()
{
return _console;
}
2020-06-06 22:27:54 -04:00
shared_ptr<IAssembler> Debugger::GetAssembler(CpuType cpuType)
2020-02-11 22:01:06 -05:00
{
2020-12-19 23:30:09 +03:00
if (cpuType == CpuType::Gameboy)
{
2020-06-06 22:27:54 -04:00
return std::dynamic_pointer_cast<IAssembler>(_gbDebugger->GetAssembler());
2020-12-19 23:30:09 +03:00
}
else
{
2020-06-06 22:27:54 -04:00
return std::dynamic_pointer_cast<IAssembler>(_cpuDebugger->GetAssembler());
}
2020-02-11 22:01:06 -05:00
}
template void Debugger::ProcessMemoryRead<CpuType::Cpu>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Debugger::ProcessMemoryRead<CpuType::Sa1>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Debugger::ProcessMemoryRead<CpuType::Spc>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2019-07-30 22:34:52 -04:00
template void Debugger::ProcessMemoryRead<CpuType::Gsu>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2020-02-23 21:50:55 -05:00
template void Debugger::ProcessMemoryRead<CpuType::NecDsp>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2020-02-24 22:00:52 -05:00
template void Debugger::ProcessMemoryRead<CpuType::Cx4>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Debugger::ProcessMemoryRead<CpuType::Gameboy>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Cpu>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Sa1>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Spc>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2019-07-30 22:34:52 -04:00
template void Debugger::ProcessMemoryWrite<CpuType::Gsu>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2020-02-23 21:50:55 -05:00
template void Debugger::ProcessMemoryWrite<CpuType::NecDsp>(uint32_t addr, uint8_t value, MemoryOperationType opType);
2020-02-24 22:00:52 -05:00
template void Debugger::ProcessMemoryWrite<CpuType::Cx4>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Debugger::ProcessMemoryWrite<CpuType::Gameboy>(uint32_t addr, uint8_t value, MemoryOperationType opType);
template void Debugger::ProcessInterrupt<CpuType::Cpu>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Debugger::ProcessInterrupt<CpuType::Sa1>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Debugger::ProcessInterrupt<CpuType::Gameboy>(uint32_t originalPc, uint32_t currentPc, bool forNmi);
template void Debugger::ProcessPpuCycle<CpuType::Cpu>();
template void Debugger::ProcessPpuCycle<CpuType::Gameboy>();