2019-07-25 22:22:09 -04:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include "CpuDebugger.h"
|
|
|
|
#include "DebugTypes.h"
|
|
|
|
#include "SettingTypes.h"
|
|
|
|
#include "DisassemblyInfo.h"
|
|
|
|
#include "Disassembler.h"
|
|
|
|
#include "Cpu.h"
|
|
|
|
#include "Sa1.h"
|
|
|
|
#include "TraceLogger.h"
|
|
|
|
#include "CallstackManager.h"
|
2019-10-11 16:07:30 -04:00
|
|
|
#include "BreakpointManager.h"
|
2019-07-25 22:22:09 -04:00
|
|
|
#include "MemoryManager.h"
|
|
|
|
#include "CodeDataLogger.h"
|
|
|
|
#include "EmuSettings.h"
|
|
|
|
#include "EventManager.h"
|
|
|
|
#include "ScriptManager.h"
|
|
|
|
#include "MemoryMappings.h"
|
|
|
|
#include "Debugger.h"
|
|
|
|
#include "BaseCartridge.h"
|
|
|
|
#include "Console.h"
|
|
|
|
#include "MemoryAccessCounter.h"
|
2019-10-11 16:07:30 -04:00
|
|
|
#include "ExpressionEvaluator.h"
|
2020-06-06 22:27:54 -04:00
|
|
|
#include "Assembler.h"
|
2020-06-23 18:34:03 -04:00
|
|
|
#include "../Utilities/HexUtilities.h"
|
2019-07-25 22:22:09 -04:00
|
|
|
|
|
|
|
CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType)
|
|
|
|
{
|
|
|
|
_cpuType = cpuType;
|
|
|
|
|
|
|
|
_debugger = debugger;
|
|
|
|
_traceLogger = debugger->GetTraceLogger().get();
|
|
|
|
_disassembler = debugger->GetDisassembler().get();
|
|
|
|
_memoryAccessCounter = debugger->GetMemoryAccessCounter().get();
|
|
|
|
_cpu = debugger->GetConsole()->GetCpu().get();
|
|
|
|
_sa1 = debugger->GetConsole()->GetCartridge()->GetSa1();
|
2020-06-18 00:58:22 -04:00
|
|
|
_codeDataLogger = debugger->GetCodeDataLogger(CpuType::Cpu).get();
|
2019-07-25 22:22:09 -04:00
|
|
|
_settings = debugger->GetConsole()->GetSettings().get();
|
|
|
|
_memoryManager = debugger->GetConsole()->GetMemoryManager().get();
|
2021-03-10 11:13:28 -05:00
|
|
|
|
|
|
|
_eventManager.reset(new EventManager(debugger, _cpu, _debugger->GetConsole()->GetPpu().get(), _memoryManager, _debugger->GetConsole()->GetDmaController().get()));
|
2019-07-25 22:22:09 -04:00
|
|
|
_callstackManager.reset(new CallstackManager(debugger));
|
2020-05-21 20:57:00 -04:00
|
|
|
_breakpointManager.reset(new BreakpointManager(debugger, cpuType, _eventManager.get()));
|
2019-07-25 22:22:09 -04:00
|
|
|
_step.reset(new StepRequest());
|
2020-06-06 22:27:54 -04:00
|
|
|
_assembler.reset(new Assembler(_debugger->GetLabelManager()));
|
2019-07-25 22:22:09 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(GetState().PC == 0) {
|
2019-07-25 22:22:09 -04:00
|
|
|
//Enable breaking on uninit reads when debugger is opened at power on
|
|
|
|
_enableBreakOnUninitRead = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CpuDebugger::Reset()
|
|
|
|
{
|
|
|
|
_enableBreakOnUninitRead = true;
|
2020-02-08 17:08:33 -05:00
|
|
|
_callstackManager.reset(new CallstackManager(_debugger));
|
2019-07-25 22:22:09 -04:00
|
|
|
_prevOpCode = 0xFF;
|
|
|
|
}
|
|
|
|
|
2019-07-29 16:47:14 -04:00
|
|
|
void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type)
|
2019-07-25 22:22:09 -04:00
|
|
|
{
|
|
|
|
AddressInfo addressInfo = GetMemoryMappings().GetAbsoluteAddress(addr);
|
2021-03-10 11:13:28 -05:00
|
|
|
MemoryOperationInfo operation = { addr, value, type };
|
2019-07-25 22:22:09 -04:00
|
|
|
CpuState state = GetState();
|
|
|
|
BreakSource breakSource = BreakSource::Unspecified;
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(type == MemoryOperationType::ExecOpCode) {
|
|
|
|
bool needDisassemble = _traceLogger->IsCpuLogged(_cpuType) || _settings->CheckDebuggerFlag(_cpuType == CpuType::Cpu ? DebuggerFlags::CpuDebuggerEnabled : DebuggerFlags::Sa1DebuggerEnabled);
|
|
|
|
if(addressInfo.Address >= 0) {
|
|
|
|
if(addressInfo.Type == SnesMemoryType::PrgRom) {
|
2019-07-25 22:22:09 -04:00
|
|
|
uint8_t flags = CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8));
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) {
|
2019-07-25 22:22:09 -04:00
|
|
|
flags |= CdlFlags::SubEntryPoint;
|
|
|
|
}
|
|
|
|
_codeDataLogger->SetFlags(addressInfo.Address, flags);
|
|
|
|
}
|
2021-03-10 11:13:28 -05:00
|
|
|
if(needDisassemble) {
|
|
|
|
_disassembler->BuildCache(addressInfo, state.PS & (ProcFlags::IndexMode8 | ProcFlags::MemoryMode8), _cpuType);
|
2019-10-11 16:07:30 -04:00
|
|
|
}
|
2019-07-25 22:22:09 -04:00
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_traceLogger->IsCpuLogged(_cpuType)) {
|
2020-06-18 00:58:22 -04:00
|
|
|
_debugger->GetState(_debugState, true);
|
2019-07-25 22:22:09 -04:00
|
|
|
|
2020-05-18 16:10:53 -04:00
|
|
|
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, _cpuType);
|
2020-06-18 00:58:22 -04:00
|
|
|
_traceLogger->Log(_cpuType, _debugState, disInfo);
|
2019-10-11 16:07:30 -04:00
|
|
|
}
|
2019-07-25 22:22:09 -04:00
|
|
|
|
|
|
|
uint32_t pc = (state.K << 16) | state.PC;
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) {
|
2019-07-25 22:22:09 -04:00
|
|
|
//JSR, JSL
|
|
|
|
uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, state.PS, _cpuType);
|
|
|
|
uint32_t returnPc = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + opSize) & 0xFFFF);
|
2020-02-08 17:08:33 -05:00
|
|
|
AddressInfo srcAddress = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter);
|
|
|
|
AddressInfo retAddress = GetMemoryMappings().GetAbsoluteAddress(returnPc);
|
2021-03-10 11:13:28 -05:00
|
|
|
_callstackManager->Push(srcAddress, _prevProgramCounter, addressInfo, pc, retAddress, returnPc, StackFrameFlags::None);
|
|
|
|
} else if(_prevOpCode == 0x60 || _prevOpCode == 0x6B || _prevOpCode == 0x40) {
|
2019-07-25 22:22:09 -04:00
|
|
|
//RTS, RTL, RTI
|
2020-02-08 17:08:33 -05:00
|
|
|
_callstackManager->Pop(addressInfo, pc);
|
2019-07-25 22:22:09 -04:00
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_step->BreakAddress == (int32_t)pc && (_prevOpCode == 0x60 || _prevOpCode == 0x40 || _prevOpCode == 0x6B || _prevOpCode == 0x44 || _prevOpCode == 0x54)) {
|
2019-07-25 22:22:09 -04:00
|
|
|
//RTS/RTL/RTI found, if we're on the expected return address, break immediately (for step over/step out)
|
|
|
|
_step->StepCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_prevOpCode = value;
|
|
|
|
_prevProgramCounter = pc;
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_step->StepCount > 0) {
|
2019-07-25 22:22:09 -04:00
|
|
|
_step->StepCount--;
|
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_settings->CheckDebuggerFlag(DebuggerFlags::CpuDebuggerEnabled)) {
|
|
|
|
if(value == 0x00 || value == 0x02 || value == 0x42 || value == 0xDB) {
|
2019-07-25 22:22:09 -04:00
|
|
|
//Break on BRK/STP/WDM/COP
|
2021-03-10 11:13:28 -05:00
|
|
|
if(value == 0x00 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnBrk)) {
|
2019-07-25 22:22:09 -04:00
|
|
|
breakSource = BreakSource::BreakOnBrk;
|
|
|
|
_step->StepCount = 0;
|
2021-03-10 11:13:28 -05:00
|
|
|
} else if(value == 0x02 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnCop)) {
|
2019-07-25 22:22:09 -04:00
|
|
|
breakSource = BreakSource::BreakOnCop;
|
|
|
|
_step->StepCount = 0;
|
2021-03-10 11:13:28 -05:00
|
|
|
} else if(value == 0x42 && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnWdm)) {
|
2019-07-25 22:22:09 -04:00
|
|
|
breakSource = BreakSource::BreakOnWdm;
|
|
|
|
_step->StepCount = 0;
|
2021-03-10 11:13:28 -05:00
|
|
|
} else if(value == 0xDB && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnStp)) {
|
2019-07-25 22:22:09 -04:00
|
|
|
breakSource = BreakSource::BreakOnStp;
|
|
|
|
_step->StepCount = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-12 21:26:50 -05:00
|
|
|
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
|
2021-03-10 11:13:28 -05:00
|
|
|
} else if(type == MemoryOperationType::ExecOperand) {
|
|
|
|
if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) {
|
|
|
|
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
|
2019-07-25 22:22:09 -04:00
|
|
|
}
|
2020-02-12 21:26:50 -05:00
|
|
|
_memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock());
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
|
|
|
if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) {
|
|
|
|
_codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)));
|
2019-07-25 22:22:09 -04:00
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock())) {
|
2020-02-12 21:26:50 -05:00
|
|
|
//Memory access was a read on an uninitialized memory address
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_enableBreakOnUninitRead) {
|
|
|
|
if(_memoryAccessCounter->GetReadCount(addressInfo) == 1) {
|
2020-06-23 18:34:03 -04:00
|
|
|
//Only warn the first time
|
2021-03-10 11:13:28 -05:00
|
|
|
_debugger->Log(string(_cpuType == CpuType::Sa1 ? "[SA1]" : "[CPU]") + " Uninitialized memory read: $" + HexUtilities::ToHex24(addr));
|
2020-06-23 18:34:03 -04:00
|
|
|
}
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_settings->CheckDebuggerFlag(DebuggerFlags::CpuDebuggerEnabled) && _settings->CheckDebuggerFlag(DebuggerFlags::BreakOnUninitRead)) {
|
2020-06-23 18:34:03 -04:00
|
|
|
breakSource = BreakSource::BreakOnUninitMemoryRead;
|
|
|
|
_step->StepCount = 0;
|
|
|
|
}
|
2020-02-12 21:26:50 -05:00
|
|
|
}
|
2019-07-25 22:22:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(IsRegister(addr)) {
|
2019-07-25 22:22:09 -04:00
|
|
|
_eventManager->AddEvent(DebugEventType::Register, operation);
|
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
_debugger->ProcessBreakConditions(_step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource);
|
2019-07-25 22:22:09 -04:00
|
|
|
}
|
|
|
|
|
2019-07-29 16:47:14 -04:00
|
|
|
void CpuDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type)
|
2019-07-25 22:22:09 -04:00
|
|
|
{
|
|
|
|
AddressInfo addressInfo = GetMemoryMappings().GetAbsoluteAddress(addr);
|
2021-03-10 11:13:28 -05:00
|
|
|
MemoryOperationInfo operation = { addr, value, type };
|
|
|
|
if(addressInfo.Address >= 0 && (addressInfo.Type == SnesMemoryType::WorkRam || addressInfo.Type == SnesMemoryType::SaveRam)) {
|
2019-07-25 22:22:09 -04:00
|
|
|
_disassembler->InvalidateCache(addressInfo, _cpuType);
|
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(IsRegister(addr)) {
|
2019-07-25 22:22:09 -04:00
|
|
|
_eventManager->AddEvent(DebugEventType::Register, operation);
|
|
|
|
}
|
|
|
|
|
2020-02-12 21:26:50 -05:00
|
|
|
_memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock());
|
2019-07-25 22:22:09 -04:00
|
|
|
|
2019-10-11 16:07:30 -04:00
|
|
|
_debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo);
|
2019-07-25 22:22:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void CpuDebugger::Run()
|
|
|
|
{
|
|
|
|
_step.reset(new StepRequest());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CpuDebugger::Step(int32_t stepCount, StepType type)
|
|
|
|
{
|
|
|
|
StepRequest step;
|
2021-03-10 11:13:28 -05:00
|
|
|
if((type == StepType::StepOver || type == StepType::StepOut || type == StepType::Step) && GetState().StopState == CpuStopState::Stopped) {
|
2019-07-25 22:22:09 -04:00
|
|
|
//If STP was called, the CPU isn't running anymore - use the PPU to break execution instead (useful for test roms that end with STP)
|
2020-07-08 21:25:23 -04:00
|
|
|
step.PpuStepCount = 1;
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
|
|
|
switch(type) {
|
|
|
|
case StepType::Step: step.StepCount = stepCount; break;
|
|
|
|
case StepType::StepOut: step.BreakAddress = _callstackManager->GetReturnAddress(); break;
|
|
|
|
case StepType::StepOver:
|
|
|
|
if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC || _prevOpCode == 0x00 || _prevOpCode == 0x02 || _prevOpCode == 0x44 || _prevOpCode == 0x54) {
|
|
|
|
//JSR, JSL, BRK, COP, MVP, MVN
|
|
|
|
step.BreakAddress = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + DisassemblyInfo::GetOpSize(_prevOpCode, 0, _cpuType)) & 0xFFFF);
|
|
|
|
} else {
|
|
|
|
//For any other instruction, step over is the same as step into
|
|
|
|
step.StepCount = 1;
|
|
|
|
}
|
|
|
|
break;
|
2019-07-25 22:22:09 -04:00
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
case StepType::PpuStep: step.PpuStepCount = stepCount; break;
|
|
|
|
case StepType::SpecificScanline: step.BreakScanline = stepCount; break;
|
2019-07-25 22:22:09 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_step.reset(new StepRequest(step));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CpuDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi)
|
|
|
|
{
|
2020-02-08 17:08:33 -05:00
|
|
|
AddressInfo src = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter);
|
|
|
|
AddressInfo ret = GetMemoryMappings().GetAbsoluteAddress(originalPc);
|
|
|
|
AddressInfo dest = GetMemoryMappings().GetAbsoluteAddress(currentPc);
|
2021-03-10 11:13:28 -05:00
|
|
|
_callstackManager->Push(src, _prevProgramCounter, dest, currentPc, ret, originalPc, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq);
|
2019-07-25 22:22:09 -04:00
|
|
|
_eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq);
|
|
|
|
}
|
|
|
|
|
2020-06-18 00:58:22 -04:00
|
|
|
void CpuDebugger::ProcessPpuCycle(uint16_t scanline, uint16_t cycle)
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_step->PpuStepCount > 0) {
|
2020-06-18 00:58:22 -04:00
|
|
|
_step->PpuStepCount--;
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_step->PpuStepCount == 0) {
|
2020-06-18 00:58:22 -04:00
|
|
|
_debugger->SleepUntilResume(BreakSource::PpuStep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
if(cycle == 0 && scanline == _step->BreakScanline) {
|
2020-06-18 00:58:22 -04:00
|
|
|
_step->BreakScanline = -1;
|
|
|
|
_debugger->SleepUntilResume(BreakSource::PpuStep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-25 22:22:09 -04:00
|
|
|
MemoryMappings& CpuDebugger::GetMemoryMappings()
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_cpuType == CpuType::Cpu) {
|
2019-07-25 22:22:09 -04:00
|
|
|
return *_memoryManager->GetMemoryMappings();
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
2019-07-25 22:22:09 -04:00
|
|
|
return *_sa1->GetMemoryMappings();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CpuState CpuDebugger::GetState()
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
if(_cpuType == CpuType::Cpu) {
|
2019-07-25 22:22:09 -04:00
|
|
|
return _cpu->GetState();
|
2021-03-10 11:13:28 -05:00
|
|
|
} else {
|
2019-07-25 22:22:09 -04:00
|
|
|
return _sa1->GetCpuState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CpuDebugger::IsRegister(uint32_t addr)
|
|
|
|
{
|
|
|
|
return _cpuType == CpuType::Cpu && _memoryManager->IsRegister(addr);
|
|
|
|
}
|
|
|
|
|
2020-05-21 20:57:00 -04:00
|
|
|
shared_ptr<EventManager> CpuDebugger::GetEventManager()
|
|
|
|
{
|
|
|
|
return _eventManager;
|
|
|
|
}
|
|
|
|
|
2020-06-06 22:27:54 -04:00
|
|
|
shared_ptr<Assembler> CpuDebugger::GetAssembler()
|
|
|
|
{
|
|
|
|
return _assembler;
|
|
|
|
}
|
|
|
|
|
2019-07-25 22:22:09 -04:00
|
|
|
shared_ptr<CallstackManager> CpuDebugger::GetCallstackManager()
|
|
|
|
{
|
|
|
|
return _callstackManager;
|
2019-10-11 16:07:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
BreakpointManager* CpuDebugger::GetBreakpointManager()
|
|
|
|
{
|
|
|
|
return _breakpointManager.get();
|
2021-03-10 11:13:28 -05:00
|
|
|
}
|