#include "stdafx.h" #include "Debugger.h" #include "DebugTypes.h" #include "Console.h" #include "Cpu.h" #include "Ppu.h" #include "BaseCartridge.h" #include "MemoryManager.h" #include "SoundMixer.h" #include "NotificationManager.h" #include "CpuTypes.h" #include "DisassemblyInfo.h" #include "TraceLogger.h" #include "MemoryDumper.h" #include "MemoryAccessCounter.h" #include "CodeDataLogger.h" #include "Disassembler.h" #include "BreakpointManager.h" #include "PpuTools.h" #include "EventManager.h" #include "EventType.h" #include "CallstackManager.h" #include "ExpressionEvaluator.h" #include "../Utilities/HexUtilities.h" #include "../Utilities/FolderUtilities.h" Debugger::Debugger(shared_ptr console) { _console = console; _cpu = console->GetCpu(); _ppu = console->GetPpu(); _memoryManager = console->GetMemoryManager(); _watchExpEval.reset(new ExpressionEvaluator(this)); _codeDataLogger.reset(new CodeDataLogger(console->GetCartridge()->DebugGetPrgRomSize(), _memoryManager.get())); _disassembler.reset(new Disassembler(console, _codeDataLogger)); _traceLogger.reset(new TraceLogger(this, _console)); _memoryDumper.reset(new MemoryDumper(_ppu, console->GetSpc(), _memoryManager, console->GetCartridge())); _memoryAccessCounter.reset(new MemoryAccessCounter(this, _memoryManager.get())); _breakpointManager.reset(new BreakpointManager(this)); _ppuTools.reset(new PpuTools(_console.get(), _ppu.get())); _eventManager.reset(new EventManager(this, _cpu.get(), _ppu.get())); _callstackManager.reset(new CallstackManager(this)); _cpuStepCount = -1; _ppuStepCount = -1; _breakAddress = -1; _breakScanline = -1; _executionStopped = false; _breakRequestCount = 0; string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_console->GetCartridge()->GetRomInfo().RomFile.GetFileName(), false) + ".cdl"); _codeDataLogger->LoadCdlFile(cdlFile); //TODO: Thread safety uint32_t prgRomSize = console->GetCartridge()->DebugGetPrgRomSize(); AddressInfo addrInfo; addrInfo.Type = SnesMemoryType::PrgRom; for(uint32_t i = 0; i < prgRomSize; i++) { if(_codeDataLogger->IsCode(i)) { addrInfo.Address = (int32_t)i; i += _disassembler->BuildCache(addrInfo, _codeDataLogger->GetCpuFlags(i)) - 1; } } } Debugger::~Debugger() { Release(); } void Debugger::Release() { string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_console->GetCartridge()->GetRomInfo().RomFile.GetFileName(), false) + ".cdl"); _codeDataLogger->SaveCdlFile(cdlFile); while(_executionStopped) { Run(); } } void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type) { AddressInfo addressInfo = _memoryManager->GetAbsoluteAddress(addr); MemoryOperationInfo operation = { addr, value, type }; CpuState state = _cpu->GetState(); if(type == MemoryOperationType::ExecOpCode) { if(addressInfo.Address >= 0) { if(addressInfo.Type == SnesMemoryType::PrgRom) { uint8_t flags = CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)); if(_prevOpCode == 0x20 || _prevOpCode == 0x5C || _prevOpCode == 0xDC || _prevOpCode == 0xFC) { flags |= CdlFlags::SubEntryPoint; } _codeDataLogger->SetFlags(addressInfo.Address, flags); } _disassembler->BuildCache(addressInfo, state.PS); } DebugState debugState; GetState(debugState); DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo); _traceLogger->Log(debugState, disInfo); uint32_t pc = (state.K << 16) | state.PC; if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) { //JSR, JSL uint8_t opSize = DisassemblyInfo::GetOperandSize(_prevOpCode, 0) + 1; uint32_t returnPc = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + opSize) & 0xFFFF); _callstackManager->Push(_prevProgramCounter, pc, returnPc, StackFrameFlags::None); } else if(_prevOpCode == 0x60 || _prevOpCode == 0x6B || _prevOpCode == 0x40) { //RTS, RTL, RTI _callstackManager->Pop(pc); } ProcessStepConditions(_prevOpCode, pc); _prevOpCode = value; _prevProgramCounter = pc; if(_cpuStepCount > 0) { _cpuStepCount--; } /*if(value == 0x00 || value == 0xDB || value == 0x42) { //Break on BRK/STP/WDM _cpuStepCount = 0; }*/ } 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))); } } else { if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8))); } } _memoryAccessCounter->ProcessMemoryAccess(addressInfo, type, _memoryManager->GetMasterClock()); if(_memoryManager->IsRegister(addr)) { _eventManager->AddEvent(DebugEventType::Register, operation); } ProcessBreakConditions(operation, addressInfo); } void Debugger::ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType type) { AddressInfo addressInfo = _memoryManager->GetAbsoluteAddress(addr); MemoryOperationInfo operation = { addr, value, type }; if(addressInfo.Address >= 0 && (addressInfo.Type == SnesMemoryType::WorkRam || addressInfo.Type == SnesMemoryType::SaveRam)) { _disassembler->InvalidateCache(addressInfo); } if(_memoryManager->IsRegister(addr)) { _eventManager->AddEvent(DebugEventType::Register, operation); } _memoryAccessCounter->ProcessMemoryAccess(addressInfo, type, _memoryManager->GetMasterClock()); ProcessBreakConditions(operation, addressInfo); } void Debugger::ProcessWorkRamRead(uint32_t addr, uint8_t value) { AddressInfo addressInfo(addr, SnesMemoryType::WorkRam); //TODO Make this more flexible/accurate MemoryOperationInfo operation(0x7E0000 | addr, value, MemoryOperationType::Read); ProcessBreakConditions(operation, addressInfo); } void Debugger::ProcessWorkRamWrite(uint32_t addr, uint8_t value) { AddressInfo addressInfo(addr, SnesMemoryType::WorkRam); //TODO Make this more flexible/accurate MemoryOperationInfo operation(0x7E0000 | addr, value, MemoryOperationType::Write); ProcessBreakConditions(operation, addressInfo); } void Debugger::ProcessSpcRead(uint16_t addr, uint8_t value, MemoryOperationType type) { AddressInfo addressInfo(addr, SnesMemoryType::SpcRam); MemoryOperationInfo operation(addr, value, type); ProcessBreakConditions(operation, addressInfo); _memoryAccessCounter->ProcessMemoryAccess(addressInfo, type, _memoryManager->GetMasterClock()); } void Debugger::ProcessSpcWrite(uint16_t addr, uint8_t value, MemoryOperationType type) { AddressInfo addressInfo(addr, SnesMemoryType::SpcRam); MemoryOperationInfo operation(addr, value, type); ProcessBreakConditions(operation, addressInfo); _memoryAccessCounter->ProcessMemoryAccess(addressInfo, type, _memoryManager->GetMasterClock()); } void Debugger::ProcessPpuRead(uint16_t addr, uint8_t value, SnesMemoryType memoryType) { AddressInfo addressInfo(addr, memoryType); MemoryOperationInfo operation(addr, value, MemoryOperationType::Read); ProcessBreakConditions(operation, addressInfo); _memoryAccessCounter->ProcessMemoryAccess(addressInfo, MemoryOperationType::Read, _memoryManager->GetMasterClock()); } void Debugger::ProcessPpuWrite(uint16_t addr, uint8_t value, SnesMemoryType memoryType) { AddressInfo addressInfo(addr, memoryType); MemoryOperationInfo operation(addr, value, MemoryOperationType::Write); ProcessBreakConditions(operation, addressInfo); _memoryAccessCounter->ProcessMemoryAccess(addressInfo, MemoryOperationType::Write, _memoryManager->GetMasterClock()); } void Debugger::ProcessPpuCycle() { uint16_t scanline = _ppu->GetState().Scanline; uint16_t cycle = _ppu->GetState().Cycle; _ppuTools->UpdateViewers(scanline, cycle); if(_ppuStepCount > 0) { _ppuStepCount--; if(_ppuStepCount == 0) { _cpuStepCount = 0; SleepUntilResume(); } } if(cycle == 0 && scanline == _breakScanline) { _cpuStepCount = 0; _breakScanline = -1; SleepUntilResume(); } } void Debugger::SleepUntilResume() { _console->GetSoundMixer()->StopAudio(); _disassembler->Disassemble(); _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak); _executionStopped = true; while(_cpuStepCount == 0 || _breakRequestCount) { std::this_thread::sleep_for(std::chrono::duration(10)); } _executionStopped = false; } void Debugger::ProcessStepConditions(uint8_t opCode, uint32_t currentPc) { if(_breakAddress == (int32_t)currentPc && (opCode == 0x60 || opCode == 0x40 || opCode == 0x6B || opCode == 0x44 || opCode == 0x54)) { //RTS/RTL/RTI found, if we're on the expected return address, break immediately (for step over/step out) _cpuStepCount = 0; } } void Debugger::ProcessBreakConditions(MemoryOperationInfo &operation, AddressInfo &addressInfo) { if(_breakpointManager->CheckBreakpoint(operation, addressInfo)) { _cpuStepCount = 0; } if(_cpuStepCount == 0 || _breakRequestCount) { SleepUntilResume(); } } void Debugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) { _callstackManager->Push(_prevProgramCounter, currentPc, originalPc, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq); _eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq); } void Debugger::ProcessEvent(EventType type) { switch(type) { default: break; case EventType::StartFrame: _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh); _eventManager->ClearFrameEvents(); break; } } int32_t Debugger::EvaluateExpression(string expression, EvalResultType &resultType, bool useCache) { MemoryOperationInfo operationInfo { 0, 0, MemoryOperationType::Read }; DebugState state; GetState(state); if(useCache) { return _watchExpEval->Evaluate(expression, state, resultType, operationInfo); } else { ExpressionEvaluator expEval(this); return expEval.Evaluate(expression, state, resultType, operationInfo); } } void Debugger::Run() { _cpuStepCount = -1; _ppuStepCount = -1; _breakAddress = -1; _breakScanline = -1; } void Debugger::Step(int32_t stepCount, StepType type) { switch(type) { case StepType::CpuStep: _cpuStepCount = stepCount; _breakAddress = -1; _ppuStepCount = -1; _breakScanline = -1; break; case StepType::CpuStepOut: _breakAddress = _callstackManager->GetReturnAddress(); _cpuStepCount = -1; _ppuStepCount = -1; _breakScanline = -1; break; case StepType::CpuStepOver: if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC || _prevOpCode == 0x00 || _prevOpCode == 0x02 || _prevOpCode == 0x44 || _prevOpCode == 0x54) { //JSR, JSL, BRK, COP, MVP, MVN _breakAddress = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + DisassemblyInfo::GetOperandSize(_prevOpCode, 0) + 1) & 0xFFFF); _cpuStepCount = -1; _ppuStepCount = -1; _breakScanline = -1; } else { //For any other instruction, step over is the same as step into _cpuStepCount = 1; _breakAddress = -1; _ppuStepCount = -1; _breakScanline = -1; } break; case StepType::PpuStep: _ppuStepCount = stepCount; _cpuStepCount = -1; _breakAddress = -1; _breakScanline = -1; break; case StepType::SpecificScanline: _breakScanline = stepCount; _ppuStepCount = -1; _cpuStepCount = -1; _breakAddress = -1; break; } } bool Debugger::IsExecutionStopped() { return _executionStopped; } void Debugger::BreakRequest(bool release) { if(release) { _breakRequestCount--; } else { _breakRequestCount++; } } void Debugger::GetState(DebugState &state) { state.MasterClock = _memoryManager->GetMasterClock(); state.Cpu = _cpu->GetState(); state.Ppu = _ppu->GetState(); } shared_ptr Debugger::GetTraceLogger() { return _traceLogger; } shared_ptr Debugger::GetMemoryDumper() { return _memoryDumper; } shared_ptr Debugger::GetMemoryAccessCounter() { return _memoryAccessCounter; } shared_ptr Debugger::GetCodeDataLogger() { return _codeDataLogger; } shared_ptr Debugger::GetDisassembler() { return _disassembler; } shared_ptr Debugger::GetBreakpointManager() { return _breakpointManager; } shared_ptr Debugger::GetPpuTools() { return _ppuTools; } shared_ptr Debugger::GetEventManager() { return _eventManager; } shared_ptr Debugger::GetCallstackManager() { return _callstackManager; } shared_ptr Debugger::GetConsole() { return _console; }