diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 2e3b451a..7e2eb306 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -43,14 +43,19 @@ + + + + + @@ -188,6 +193,11 @@ + + + + + @@ -367,17 +377,18 @@ - + + @@ -444,6 +455,11 @@ + + + + + @@ -543,7 +559,6 @@ Create - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index e96252cc..87ac85b8 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -10,9 +10,6 @@ SNES - - Debugger - Debugger @@ -801,14 +798,21 @@ + + + + + + + + + + SNES - - Debugger - Debugger @@ -1238,6 +1242,13 @@ + + + + + + + diff --git a/Core/Debugger/BaseTraceLogger.h b/Core/Debugger/BaseTraceLogger.h new file mode 100644 index 00000000..d8eeefb3 --- /dev/null +++ b/Core/Debugger/BaseTraceLogger.h @@ -0,0 +1,517 @@ +#pragma once +#include "stdafx.h" +#include +#include "Utilities/SimpleLock.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Debugger.h" +#include "Debugger/DebugTypes.h" +#include "Debugger/LabelManager.h" +#include "Debugger/DebugTypes.h" +#include "Debugger/DebugUtilities.h" +#include "Debugger/DebugBreakHelper.h" +#include "Debugger/ITraceLogger.h" +#include "Debugger/ExpressionEvaluator.h" +#include "Utilities/HexUtilities.h" +#include "Shared/Emulator.h" +#include "Shared/EmuSettings.h" + +class IConsole; +class Debugger; +class LabelManager; +class MemoryDumper; +class EmuSettings; + +enum class RowDataType +{ + Text = 0, + ByteCode, + Disassembly, + EffectiveAddress, + MemoryValue, + Align, + PC, + A, + B, + C, + D, + E, + F, + H, + K, + L, + M, + N, + X, + Y, + DB, + SP, + PS, + Cycle, + Scanline, + HClock, + FrameCount, + CycleCount, + + R0, + R1, + R2, + R3, + R4, + R5, + R6, + R7, + R8, + R9, + R10, + R11, + R12, + R13, + R14, + R15, + Src, + Dst, + + MAR, + MDR, + DPR, + ML, + MH, + PB, + P, + + RP, + DP, + DR, + SR, + TR, + TRB, + + FlagsA, + FlagsB +}; + +struct TraceLogPpuState +{ + uint32_t Cycle; + uint32_t HClock; + int32_t Scanline; + uint32_t FrameCount; +}; + +struct RowPart +{ + RowDataType DataType; + string Text; + bool DisplayInHex; + int MinWidth; +}; + +template +class BaseTraceLogger : public ITraceLogger +{ +protected: + static constexpr int ExecutionLogSize = 30000; + + TraceLoggerOptions _options; + IConsole* _console; + EmuSettings* _settings; + LabelManager* _labelManager; + MemoryDumper* _memoryDumper; + Debugger* _debugger; + + CpuType _cpuType = CpuType::Cpu; + SnesMemoryType _cpuMemoryType = SnesMemoryType::CpuMemory; + + vector _rowParts; + + uint32_t _currentPos = 0; + + bool _pendingLog = false; + CpuStateType _lastState = {}; + DisassemblyInfo _lastDisassemblyInfo = {}; + + CpuStateType* _cpuState = nullptr; + DisassemblyInfo *_disassemblyCache = nullptr; + uint64_t* _rowIds = nullptr; + TraceLogPpuState* _ppuState = nullptr; + + unique_ptr _expEvaluator; + ExpressionData _conditionData; + + void WriteByteCode(DisassemblyInfo& info, RowPart& rowPart, string& output) + { + string byteCode; + info.GetByteCode(byteCode); + if(!rowPart.DisplayInHex) { + //Remove $ marks if not in "hex" mode (but still display the bytes as hex) + byteCode.erase(std::remove(byteCode.begin(), byteCode.end(), '$'), byteCode.end()); + } + WriteStringValue(output, byteCode, rowPart); + } + + void WriteDisassembly(DisassemblyInfo& info, RowPart& rowPart, uint8_t sp, uint32_t pc, string& output) + { + int indentLevel = 0; + string code; + + if(_options.IndentCode) { + indentLevel = 0xFF - (sp & 0xFF); + code = std::string(indentLevel, ' '); + } + + LabelManager* labelManager = _options.UseLabels ? _labelManager : nullptr; + info.GetDisassembly(code, pc, labelManager, _settings); + WriteStringValue(output, code, rowPart); + } + + void WriteEffectiveAddress(DisassemblyInfo& info, RowPart& rowPart, void* cpuState, string& output, SnesMemoryType cpuMemoryType, CpuType cpuType) + { + int32_t effectiveAddress = info.GetEffectiveAddress(_debugger, cpuState, cpuType); + if(effectiveAddress >= 0) { + if(_options.UseLabels) { + AddressInfo addr { effectiveAddress, cpuMemoryType }; + string label = _labelManager->GetLabel(addr); + if(!label.empty()) { + WriteStringValue(output, " [" + label + "]", rowPart); + return; + } + } + WriteStringValue(output, " [" + HexUtilities::ToHex24(effectiveAddress) + "]", rowPart); + } + } + + void WriteMemoryValue(DisassemblyInfo& info, RowPart& rowPart, void* cpuState, string& output, SnesMemoryType memType, CpuType cpuType) + { + int32_t address = info.GetEffectiveAddress(_debugger, cpuState, cpuType); + if(address >= 0) { + uint8_t valueSize; + uint16_t value = info.GetMemoryValue(address, _memoryDumper, memType, valueSize); + if(rowPart.DisplayInHex) { + output += "= $"; + if(valueSize == 2) { + WriteIntValue(output, (uint16_t)value, rowPart); + } else { + WriteIntValue(output, (uint8_t)value, rowPart); + } + } else { + output += "= "; + } + } + } + + void GetStatusFlag(const char* activeStatusLetters, const char* inactiveStatusLetters, string& output, uint8_t ps, RowPart& part, int length = 8) + { + if(part.DisplayInHex) { + WriteIntValue(output, ps, part); + } else { + string flags; + for(int i = 0; i < length; i++) { + if(ps & 0x80) { + flags += activeStatusLetters[i]; + } else if(part.MinWidth >= length) { + flags += inactiveStatusLetters[i]; + } + ps <<= 1; + } + WriteStringValue(output, flags, part); + } + } + + void WriteAlign(int originalSize, RowPart& rowPart, string& output) + { + if((int)output.size() - originalSize < rowPart.MinWidth) { + output += std::string(rowPart.MinWidth - (output.size() - originalSize), ' '); + } + } + + void WriteIntValue(string& output, uint32_t value, RowPart& rowPart) + { + string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value); + if(rowPart.MinWidth > (int)str.size()) { + if(rowPart.DisplayInHex) { + str = std::string(rowPart.MinWidth - str.size(), '0') + str; + } else { + str += std::string(rowPart.MinWidth - str.size(), ' '); + } + } + output += str; + } + + void WriteIntValue(string& output, int32_t value, RowPart& rowPart) + { + string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value); + output += str; + if(rowPart.MinWidth > (int)str.size()) { + output += std::string(rowPart.MinWidth - str.size(), ' '); + } + } + + void WriteIntValue(string& output, uint16_t value, RowPart& rowPart) + { + string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value); + output += str; + if(rowPart.MinWidth > (int)str.size()) { + output += std::string(rowPart.MinWidth - str.size(), ' '); + } + } + + void WriteIntValue(string& output, uint8_t value, RowPart& rowPart) + { + string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value); + output += str; + if(rowPart.MinWidth > (int)str.size()) { + output += std::string(rowPart.MinWidth - str.size(), ' '); + } + } + + void WriteStringValue(string& output, string value, RowPart& rowPart) + { + output += value; + if(rowPart.MinWidth > (int)value.size()) { + output += std::string(rowPart.MinWidth - value.size(), ' '); + } + } + + void AddRow(CpuStateType& cpuState, DisassemblyInfo& disassemblyInfo) + { + _disassemblyCache[_currentPos] = disassemblyInfo; + _cpuState[_currentPos] = cpuState; + ((TraceLoggerType*)this)->LogPpuState(); + + _rowIds[_currentPos] = ITraceLogger::NextRowId; + ITraceLogger::NextRowId++; + + _pendingLog = false; + + /*if(_logToFile) { + ((TraceLoggerType*)this)->GetTraceRow(_outputBuffer, _disassemblyCache[_currentPos], cpuState); + if(_outputBuffer.size() > 32768) { + _outputFile << _outputBuffer; + _outputBuffer.clear(); + } + }*/ + + _currentPos = (_currentPos + 1) % ExecutionLogSize; + } + + void ParseFormatString(string format) + { + _rowParts.clear(); + + std::regex formatRegex = std::regex("(\\[\\s*([^[]*?)\\s*(,\\s*([\\d]*)\\s*(h){0,1}){0,1}\\s*\\])|([^[]*)", std::regex_constants::icase); + std::sregex_iterator start = std::sregex_iterator(format.cbegin(), format.cend(), formatRegex); + std::sregex_iterator end = std::sregex_iterator(); + + for(std::sregex_iterator it = start; it != end; it++) { + const std::smatch& match = *it; + + if(match.str(1) == "") { + RowPart part = {}; + part.DataType = RowDataType::Text; + part.Text = match.str(6); + _rowParts.push_back(part); + } else { + RowPart part = {}; + + string tag = match.str(2); + part.DataType = InternalGetFormatTagType(tag); + if(part.DataType == RowDataType::Text) { + part.Text = "[Invalid tag]"; + } + + if(!match.str(4).empty()) { + try { + part.MinWidth = std::stoi(match.str(4)); + } catch(std::exception&) { + } + } + part.DisplayInHex = match.str(5) == "h"; + + _rowParts.push_back(part); + } + } + } + + RowDataType InternalGetFormatTagType(string& tag) + { + if(tag == "ByteCode") { + return RowDataType::ByteCode; + } else if(tag == "Disassembly") { + return RowDataType::Disassembly; + } else if(tag == "EffectiveAddress") { + return RowDataType::EffectiveAddress; + } else if(tag == "MemoryValue") { + return RowDataType::MemoryValue; + } else if(tag == "Align") { + return RowDataType::Align; + } else if(tag == "PC") { + return RowDataType::PC; + } else if(tag == "Cycle") { + return RowDataType::Cycle; + } else if(tag == "HClock") { + return RowDataType::HClock; + } else if(tag == "Scanline") { + return RowDataType::Scanline; + } else if(tag == "FrameCount") { + return RowDataType::FrameCount; + } else if(tag == "CycleCount") { + return RowDataType::CycleCount; + } + + return GetFormatTagType(tag); + } + + virtual RowDataType GetFormatTagType(string& tag) = 0; + + void ProcessSharedTag(RowPart& rowPart, string& output, CpuStateType& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo) + { + switch(rowPart.DataType) { + case RowDataType::Text: output += rowPart.Text; break; + case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break; + case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, ((TraceLoggerType*)this)->GetStackPointer(cpuState), ((TraceLoggerType*)this)->GetProgramCounter(cpuState), output); break; + case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, _cpuMemoryType, _cpuType); break; + case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, _cpuMemoryType, _cpuType); break; + case RowDataType::Align: WriteAlign(0, rowPart, output); break; + + case RowDataType::Cycle: WriteIntValue(output, ppuState.Cycle, rowPart); break; + case RowDataType::Scanline: WriteIntValue(output, ppuState.Scanline, rowPart); break; + case RowDataType::HClock: WriteIntValue(output, ppuState.HClock, rowPart); break; + case RowDataType::FrameCount: WriteIntValue(output, ppuState.FrameCount, rowPart); break; + case RowDataType::CycleCount: + WriteIntValue(output, (uint32_t)(((TraceLoggerType*)this)->GetCycleCount(cpuState) >> 32), rowPart); + WriteIntValue(output, (uint32_t)((TraceLoggerType*)this)->GetCycleCount(cpuState), rowPart); + break; + + case RowDataType::PC: WriteStringValue(output, HexUtilities::ToHex(((TraceLoggerType*)this)->GetProgramCounter(cpuState)), rowPart); break; + } + } + +public: + BaseTraceLogger(Debugger* debugger, CpuType cpuType) + { + _debugger = debugger; + _console = debugger->GetConsole(); + _settings = debugger->GetEmulator()->GetSettings(); + _labelManager = debugger->GetLabelManager().get(); + _memoryDumper = debugger->GetMemoryDumper(); + _options = {}; + _currentPos = 0; + _pendingLog = false; + + _disassemblyCache = new DisassemblyInfo[BaseTraceLogger::ExecutionLogSize]; + _rowIds = new uint64_t[BaseTraceLogger::ExecutionLogSize]; + memset(_disassemblyCache, 0, sizeof(DisassemblyInfo) * BaseTraceLogger::ExecutionLogSize); + memset(_rowIds, 0, sizeof(uint64_t) * BaseTraceLogger::ExecutionLogSize); + + _ppuState = new TraceLogPpuState[BaseTraceLogger::ExecutionLogSize]; + memset(_ppuState, 0, sizeof(TraceLogPpuState) * BaseTraceLogger::ExecutionLogSize); + + _cpuState = new CpuStateType[BaseTraceLogger::ExecutionLogSize]; + memset(_cpuState, 0, sizeof(CpuStateType) * BaseTraceLogger::ExecutionLogSize); + + _cpuType = cpuType; + _cpuMemoryType = DebugUtilities::GetCpuMemoryType(cpuType); + + _expEvaluator.reset(new ExpressionEvaluator(debugger, cpuType)); + } + + virtual ~BaseTraceLogger() + { + delete[] _disassemblyCache; + delete[] _rowIds; + delete[] _ppuState; + delete[] _cpuState; + } + + void Clear() + { + } + + void LogNonExec(MemoryOperationInfo& operation) + { + if(_pendingLog) { + int pos = _currentPos - 1; + if(pos < 0) { + pos = BaseTraceLogger::ExecutionLogSize - 1; + } + + if(ConditionMatches(_lastState, _lastDisassemblyInfo, operation)) { + AddRow(_lastState, _lastDisassemblyInfo); + _pendingLog = false; + } + } + } + + void Log(CpuStateType& cpuState, DisassemblyInfo& disassemblyInfo, MemoryOperationInfo& operation) + { + if(_enabled) { + //For the sake of performance, only log data for the CPUs we're actively displaying/logging + if(ConditionMatches(cpuState, disassemblyInfo, operation)) { + AddRow(cpuState, disassemblyInfo); + } else { + _pendingLog = true; + _lastState = cpuState; + _lastDisassemblyInfo = disassemblyInfo; + } + } + } + + void SetOptions(TraceLoggerOptions options) override + { + DebugBreakHelper helper(_debugger); + _options = options; + + _enabled = options.Enabled; + + string condition = _options.Condition; + string format = _options.Format; + + _conditionData = ExpressionData(); + if(!condition.empty()) { + bool success = false; + ExpressionData rpnList = _expEvaluator->GetRpnList(condition, success); + if(success) { + _conditionData = rpnList; + } + } + + ParseFormatString(format); + } + + int64_t GetRowId(uint32_t offset) override + { + int32_t pos = ((int32_t)_currentPos - (int32_t)offset); + int32_t i = (pos > 0 ? pos : BaseTraceLogger::ExecutionLogSize + pos) - 1; + if(!_disassemblyCache[i].IsInitialized()) { + return -1; + } + return _rowIds[i]; + } + + bool ConditionMatches(BaseState &state, DisassemblyInfo &disassemblyInfo, MemoryOperationInfo &operationInfo) + { + if(!_conditionData.RpnQueue.empty()) { + EvalResultType type; + if(!_expEvaluator->Evaluate(_conditionData, state, type, operationInfo)) { + return false; + } + } + return true; + } + + void GetExecutionTrace(TraceRow& row, uint32_t offset) override + { + int pos = ((int)_currentPos - offset); + int index = (pos > 0 ? pos : BaseTraceLogger::ExecutionLogSize + pos) - 1; + + CpuStateType& state = _cpuState[index]; + string logOutput; + ((TraceLoggerType*)this)->GetTraceRow(logOutput, state, _ppuState[index], _disassemblyCache[index]); + + row.Type = _cpuType; + _disassemblyCache[index].GetByteCode(row.ByteCode); + row.ByteCodeSize = _disassemblyCache[index].GetOpSize(); + row.ProgramCounter = ((TraceLoggerType*)this)->GetProgramCounter(state); + memcpy(row.LogOutput, logOutput.c_str(), logOutput.size()); + } +}; diff --git a/Core/Debugger/CodeDataLogger.cpp b/Core/Debugger/CodeDataLogger.cpp index 6ae1c4e2..c12f7c21 100644 --- a/Core/Debugger/CodeDataLogger.cpp +++ b/Core/Debugger/CodeDataLogger.cpp @@ -100,17 +100,15 @@ void CodeDataLogger::CalculateStats() void CodeDataLogger::SetFlags(int32_t absoluteAddr, uint8_t flags) { - if(absoluteAddr >= 0 && absoluteAddr < (int32_t)_prgSize) { - if((_cdlData[absoluteAddr] & flags) != flags) { - if(flags & CdlFlags::Code) { - _cdlData[absoluteAddr] = flags | (_cdlData[absoluteAddr] & ~(CdlFlags::Data | CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)); - } else if(flags & CdlFlags::Data) { - if(!IsCode(absoluteAddr)) { - _cdlData[absoluteAddr] |= flags; - } - } else { + if((_cdlData[absoluteAddr] & flags) != flags) { + if(flags & CdlFlags::Code) { + _cdlData[absoluteAddr] = flags | (_cdlData[absoluteAddr] & ~(CdlFlags::Data | CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)); + } else if(flags & CdlFlags::Data) { + if(!IsCode(absoluteAddr)) { _cdlData[absoluteAddr] |= flags; } + } else { + _cdlData[absoluteAddr] |= flags; } } } diff --git a/Core/Debugger/DebugTypes.h b/Core/Debugger/DebugTypes.h index fa766bd8..6ac4c436 100644 --- a/Core/Debugger/DebugTypes.h +++ b/Core/Debugger/DebugTypes.h @@ -263,7 +263,7 @@ struct StepRequest int32_t StepCount = -1; int32_t PpuStepCount = -1; int32_t BreakAddress = -1; - int32_t BreakScanline = -1; + int32_t BreakScanline = INT32_MIN; }; enum class CpuType : uint8_t diff --git a/Core/Debugger/DebugUtilities.h b/Core/Debugger/DebugUtilities.h index 142f09e0..c332476f 100644 --- a/Core/Debugger/DebugUtilities.h +++ b/Core/Debugger/DebugUtilities.h @@ -6,7 +6,7 @@ class DebugUtilities { public: - static SnesMemoryType GetCpuMemoryType(CpuType type) + static constexpr SnesMemoryType GetCpuMemoryType(CpuType type) { switch(type) { case CpuType::Cpu: return SnesMemoryType::CpuMemory; @@ -22,7 +22,7 @@ public: throw std::runtime_error("Invalid CPU type"); } - static CpuType ToCpuType(SnesMemoryType type) + static constexpr CpuType ToCpuType(SnesMemoryType type) { switch(type) { case SnesMemoryType::SpcMemory: @@ -81,7 +81,7 @@ public: return SnesMemoryType::NesMemory; } - static bool IsPpuMemory(SnesMemoryType memType) + static constexpr bool IsPpuMemory(SnesMemoryType memType) { switch(memType) { case SnesMemoryType::VideoRam: @@ -102,7 +102,7 @@ public: } } - static bool IsRomMemory(SnesMemoryType memType) + static constexpr bool IsRomMemory(SnesMemoryType memType) { switch(memType) { case SnesMemoryType::PrgRom: diff --git a/Core/Debugger/Debugger.cpp b/Core/Debugger/Debugger.cpp index aa1e704f..8b78fe33 100644 --- a/Core/Debugger/Debugger.cpp +++ b/Core/Debugger/Debugger.cpp @@ -2,7 +2,6 @@ #include "Debugger/Debugger.h" #include "Debugger/DebugTypes.h" #include "Debugger/DisassemblyInfo.h" -#include "Debugger/TraceLogger.h" #include "Debugger/MemoryDumper.h" #include "Debugger/MemoryAccessCounter.h" #include "Debugger/CodeDataLogger.h" @@ -46,6 +45,7 @@ #include "Shared/NotificationManager.h" #include "Shared/BaseState.h" #include "Shared/Emulator.h" +#include "Debugger/ITraceLogger.h" #include "Shared/Interfaces/IConsole.h" #include "Utilities/HexUtilities.h" #include "Utilities/FolderUtilities.h" @@ -53,6 +53,8 @@ #include "MemoryOperationType.h" #include "EventType.h" +uint64_t ITraceLogger::NextRowId = 0; + Debugger::Debugger(Emulator* emu, IConsole* console) { _executionStopped = true; @@ -67,14 +69,13 @@ Debugger::Debugger(Emulator* emu, IConsole* console) _memoryDumper.reset(new MemoryDumper(this)); _disassembler.reset(new Disassembler(console, this)); - _traceLogger.reset(new TraceLogger(this)); _memoryAccessCounter.reset(new MemoryAccessCounter(this)); - _ppuTools.reset(new PpuTools(_emu)); + _ppuTools.reset(new PpuTools(this, _emu)); _scriptManager.reset(new ScriptManager(this)); - vector cpuTypes = _emu->GetCpuTypes(); - _mainCpuType = cpuTypes[0]; - for(CpuType type : cpuTypes) { + _cpuTypes = _emu->GetCpuTypes(); + _mainCpuType = _cpuTypes[0]; + for(CpuType type : _cpuTypes) { unique_ptr &debugger = _debuggers[(int)type].Debugger; switch(type) { case CpuType::Cpu: debugger.reset(new CpuDebugger(this, CpuType::Cpu)); break; @@ -91,6 +92,10 @@ Debugger::Debugger(Emulator* emu, IConsole* console) _debuggers[(int)type].Evaluator.reset(new ExpressionEvaluator(this, type)); } + for(CpuType type : _cpuTypes) { + _debuggers[(int)type].Debugger->Init(); + } + _breakRequestCount = 0; _suspendRequestCount = 0; @@ -134,10 +139,25 @@ void Debugger::Reset() } } +template +DebuggerType* Debugger::GetDebugger() +{ + return (DebuggerType*)_debuggers[(int)type].Debugger.get(); +} + template void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationType opType) { - _debuggers[(int)type].Debugger->ProcessRead(addr, value, opType); + switch(type) { + case CpuType::Cpu: GetDebugger()->ProcessRead(addr, value, opType); break; + case CpuType::Spc: GetDebugger()->ProcessRead(addr, value, opType); break; + case CpuType::NecDsp: GetDebugger()->ProcessRead(addr, value, opType); break; + case CpuType::Sa1: GetDebugger()->ProcessRead(addr, value, opType); break; + case CpuType::Gsu: GetDebugger()->ProcessRead(addr, value, opType); break; + case CpuType::Cx4: GetDebugger()->ProcessRead(addr, value, opType); break; + case CpuType::Gameboy: GetDebugger()->ProcessRead(addr, value, opType); break; + case CpuType::Nes: GetDebugger()->ProcessRead(addr, value, opType); break; + } if(_scriptManager->HasScript()) { _scriptManager->ProcessMemoryOperation(addr, value, opType, type); @@ -147,7 +167,16 @@ void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t value, MemoryOperationTy template void Debugger::ProcessMemoryWrite(uint32_t addr, uint8_t value, MemoryOperationType opType) { - _debuggers[(int)type].Debugger->ProcessWrite(addr, value, opType); + switch(type) { + case CpuType::Cpu: GetDebugger()->ProcessWrite(addr, value, opType); break; + case CpuType::Spc: GetDebugger()->ProcessWrite(addr, value, opType); break; + case CpuType::NecDsp: GetDebugger()->ProcessWrite(addr, value, opType); break; + case CpuType::Sa1: GetDebugger()->ProcessWrite(addr, value, opType); break; + case CpuType::Gsu: GetDebugger()->ProcessWrite(addr, value, opType); break; + case CpuType::Cx4: GetDebugger()->ProcessWrite(addr, value, opType); break; + case CpuType::Gameboy: GetDebugger()->ProcessWrite(addr, value, opType); break; + case CpuType::Nes: GetDebugger()->ProcessWrite(addr, value, opType); break; + } if(_scriptManager->HasScript()) { _scriptManager->ProcessMemoryOperation(addr, value, opType, type); @@ -181,22 +210,17 @@ void Debugger::ProcessPpuWrite(uint16_t addr, uint8_t value, SnesMemoryType memo template void Debugger::ProcessPpuCycle() { - uint16_t scanline = 0; - uint16_t cycle = 0; + int16_t scanline; + uint16_t cycle; switch(type) { - case CpuType::Cpu: - case CpuType::Gameboy: - case CpuType::Nes: - _debuggers[(int)type].Debugger->ProcessPpuCycle(scanline, cycle); - break; + case CpuType::Cpu: GetDebugger()->ProcessPpuCycle(scanline, cycle); break; + case CpuType::Gameboy: GetDebugger()->ProcessPpuCycle(scanline, cycle); break; + case CpuType::Nes: GetDebugger()->ProcessPpuCycle(scanline, cycle); break; + default: throw std::runtime_error("Invalid cpu type"); } _ppuTools->UpdateViewers(scanline, cycle, type); - - if(_breakRequestCount > 0) { - SleepUntilResume(BreakSource::Unspecified); - } } void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operation, int breakpointId) @@ -205,11 +229,11 @@ void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operati return; } - _emu->GetSoundMixer()->StopAudio(); - _executionStopped = true; if(source != BreakSource::Unspecified || _breakRequestCount == 0) { + _emu->GetSoundMixer()->StopAudio(); + //Only trigger code break event if the pause was caused by user action BreakEvent evt = {}; evt.BreakpointId = breakpointId; @@ -222,7 +246,7 @@ void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operati } while((_waitForBreakResume && !_suspendRequestCount) || _breakRequestCount) { - std::this_thread::sleep_for(std::chrono::duration(10)); + std::this_thread::sleep_for(std::chrono::duration(_breakRequestCount ? 1 : 10)); } _executionStopped = false; @@ -363,6 +387,7 @@ void Debugger::SuspendDebugger(bool release) void Debugger::BreakImmediately(BreakSource source) { + //TODO bool gbDebugger = _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled); if(source == BreakSource::GbDisableLcdOutsideVblank && (!gbDebugger || !_settings->CheckDebuggerFlag(DebuggerFlags::GbBreakOnDisableLcdOutsideVblank))) { return; @@ -437,8 +462,8 @@ void Debugger::RebuildPrgCache(CpuType cpuType) uint32_t prgRomSize = cdl->GetPrgSize(); AddressInfo addrInfo; + //TODO addrInfo.Type = cpuType == CpuType::Gameboy ? SnesMemoryType::GbPrgRom : SnesMemoryType::PrgRom; - for(uint32_t i = 0; i < prgRomSize; i++) { if(cdl->IsCode(i)) { addrInfo.Address = (int32_t)i; @@ -535,9 +560,55 @@ void Debugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption str }*/ } -shared_ptr Debugger::GetTraceLogger() +ITraceLogger* Debugger::GetTraceLogger(CpuType cpuType) { - return _traceLogger; + if(_debuggers[(int)cpuType].Debugger) { + return _debuggers[(int)cpuType].Debugger->GetTraceLogger(); + } + return nullptr; +} + +uint32_t Debugger::GetExecutionTrace(TraceRow output[], uint32_t startOffset, uint32_t maxLineCount) +{ + DebugBreakHelper helper(this); + + uint32_t offsetsByCpu[(int)DebugUtilities::GetLastCpuType() + 1] = {}; + + vector cpuTypes = _emu->GetCpuTypes(); + + uint32_t count = 0; + int64_t lastRowId = ITraceLogger::NextRowId; + while(count < maxLineCount) { + bool added = false; + for(CpuType cpuType : cpuTypes) { + ITraceLogger* logger = GetTraceLogger(cpuType); + if(logger) { + uint32_t& offset = offsetsByCpu[(int)cpuType]; + int64_t rowId = logger->GetRowId(offset); + if(rowId == -1 || rowId != lastRowId - 1) { + continue; + } + + lastRowId = rowId; + + if(startOffset > 0) { + //Skip rows until the part the UI wants to display is reached + startOffset--; + } else { + logger->GetExecutionTrace(output[count], offset); + count++; + } + offset++; + added = true; + break; + } + } + if(!added) { + break; + } + } + + return count; } MemoryDumper* Debugger::GetMemoryDumper() diff --git a/Core/Debugger/Debugger.h b/Core/Debugger/Debugger.h index 68650397..26e098fc 100644 --- a/Core/Debugger/Debugger.h +++ b/Core/Debugger/Debugger.h @@ -16,7 +16,6 @@ class InternalRegisters; class DmaController; class EmuSettings; -class TraceLogger; class ExpressionEvaluator; class MemoryDumper; class MemoryAccessCounter; @@ -31,7 +30,9 @@ class Breakpoint; class IEventManager; class IAssembler; class IDebugger; +class ITraceLogger; +struct TraceRow; struct BaseState; enum class EventType; @@ -55,10 +56,10 @@ private: CpuInfo _debuggers[(int)DebugUtilities::GetLastCpuType() + 1]; CpuType _mainCpuType = CpuType::Cpu; + vector _cpuTypes; ConsoleType _consoleType = ConsoleType::Snes; shared_ptr _scriptManager; - shared_ptr _traceLogger; shared_ptr _memoryDumper; shared_ptr _memoryAccessCounter; shared_ptr _codeDataLogger; @@ -77,6 +78,8 @@ private: void Reset(); + template DebuggerType* GetDebugger(); + public: Debugger(Emulator* emu, IConsole* console); ~Debugger(); @@ -126,7 +129,9 @@ public: void SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption); - shared_ptr GetTraceLogger(); + uint32_t GetExecutionTrace(TraceRow output[], uint32_t startOffset, uint32_t maxLineCount); + + ITraceLogger* GetTraceLogger(CpuType cpuType); MemoryDumper* GetMemoryDumper(); shared_ptr GetMemoryAccessCounter(); shared_ptr GetCodeDataLogger(CpuType cpuType); diff --git a/Core/Debugger/Disassembler.cpp b/Core/Debugger/Disassembler.cpp index 5147a3d8..1a55dccb 100644 --- a/Core/Debugger/Disassembler.cpp +++ b/Core/Debugger/Disassembler.cpp @@ -55,7 +55,7 @@ uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuTy int returnSize = 0; int32_t address = addrInfo.Address; - while(address >= 0 && address < (int32_t)src.Cache.size()) { + do { DisassemblyInfo &disInfo = src.Cache[address]; if(!disInfo.IsInitialized() || !disInfo.IsValid(cpuFlags)) { disInfo.Initialize(src.Data+address, cpuFlags, type); @@ -77,7 +77,7 @@ uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags, CpuTy disInfo.UpdateCpuFlags(cpuFlags); address += disInfo.GetOpSize(); - } + } while(address >= 0 && address < (int32_t)src.Cache.size()); return returnSize; } diff --git a/Core/Debugger/ExpressionEvaluator.h b/Core/Debugger/ExpressionEvaluator.h index 65c80dd1..b7f8d04f 100644 --- a/Core/Debugger/ExpressionEvaluator.h +++ b/Core/Debugger/ExpressionEvaluator.h @@ -127,8 +127,8 @@ public: struct ExpressionData { - std::vector RpnQueue; - std::vector Labels; + vector RpnQueue; + vector Labels; }; class ExpressionEvaluator @@ -138,9 +138,9 @@ private: static const vector _binaryPrecedence; static const vector _unaryOperators; static const vector _unaryPrecedence; - static const std::unordered_set _operators; + static const unordered_set _operators; - std::unordered_map _cache; + unordered_map _cache; SimpleLock _cacheLock; Debugger* _debugger; diff --git a/Core/Debugger/IDebugger.h b/Core/Debugger/IDebugger.h index 60102325..ffac1fde 100644 --- a/Core/Debugger/IDebugger.h +++ b/Core/Debugger/IDebugger.h @@ -7,7 +7,9 @@ class CallstackManager; class IAssembler; class IEventManager; class CodeDataLogger; +class ITraceLogger; struct BaseState; +enum class EventType; enum class MemoryOperationType; class IDebugger @@ -18,17 +20,17 @@ public: virtual void Step(int32_t stepCount, StepType type) = 0; virtual void Reset() = 0; virtual void Run() = 0; + + virtual void Init() {} - virtual void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType opType) {} - virtual void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType opType) {} virtual void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) {} - virtual void ProcessPpuCycle(uint16_t& cycle, uint16_t& scanline) {} virtual BreakpointManager* GetBreakpointManager() = 0; virtual shared_ptr GetCallstackManager() = 0; virtual shared_ptr GetAssembler() = 0; virtual shared_ptr GetEventManager() = 0; virtual shared_ptr GetCodeDataLogger() = 0; + virtual ITraceLogger* GetTraceLogger() = 0; virtual BaseState& GetState() = 0; }; \ No newline at end of file diff --git a/Core/Debugger/ITraceLogger.h b/Core/Debugger/ITraceLogger.h new file mode 100644 index 00000000..4fb4fa92 --- /dev/null +++ b/Core/Debugger/ITraceLogger.h @@ -0,0 +1,36 @@ +#pragma once +#include "stdafx.h" +#include "DebugTypes.h" + +struct TraceRow +{ + uint32_t ProgramCounter; + CpuType Type; + uint8_t ByteCode[4]; + uint8_t ByteCodeSize; + char LogOutput[500]; +}; + +struct TraceLoggerOptions +{ + bool Enabled; + bool IndentCode; + bool UseLabels; + char Condition[1000]; + char Format[1000]; +}; + +class ITraceLogger +{ +protected: + bool _enabled = false; + +public: + static uint64_t NextRowId; + + virtual int64_t GetRowId(uint32_t offset) = 0; + virtual void GetExecutionTrace(TraceRow& row, uint32_t offset) = 0; + virtual void SetOptions(TraceLoggerOptions options) = 0; + + __forceinline bool IsEnabled() { return _enabled; } +}; diff --git a/Core/Debugger/MemoryDumper.cpp b/Core/Debugger/MemoryDumper.cpp index 9d338f0e..c83bd113 100644 --- a/Core/Debugger/MemoryDumper.cpp +++ b/Core/Debugger/MemoryDumper.cpp @@ -35,8 +35,11 @@ MemoryDumper::MemoryDumper(Debugger* debugger) _spc = c->GetSpc().get(); _memoryManager = c->GetMemoryManager().get(); _cartridge = c->GetCartridge().get(); + _gameboy = c->GetCartridge()->GetGameboy(); } else if(NesConsole* c = dynamic_cast(console)) { _nesMemoryManager = c->GetMemoryManager(); + } else if(Gameboy* c = dynamic_cast(console)) { + _gameboy = c; } } @@ -122,8 +125,8 @@ void MemoryDumper::GetMemoryState(SnesMemoryType type, uint8_t *buffer) break; case SnesMemoryType::GameboyMemory: { - if(_cartridge->GetGameboy()) { - GbMemoryManager* memManager = _cartridge->GetGameboy()->GetMemoryManager(); + if(_gameboy) { + GbMemoryManager* memManager = _gameboy->GetMemoryManager(); for(int i = 0; i <= 0xFFFF; i++) { buffer[i] = memManager->DebugRead(i); } @@ -183,7 +186,7 @@ void MemoryDumper::SetMemoryValue(SnesMemoryType memoryType, uint32_t address, u case SnesMemoryType::Sa1Memory: _cartridge->GetSa1()->GetMemoryMappings()->DebugWrite(address, value); break; case SnesMemoryType::GsuMemory: _cartridge->GetGsu()->GetMemoryMappings()->DebugWrite(address, value); break; case SnesMemoryType::Cx4Memory: _cartridge->GetCx4()->GetMemoryMappings()->DebugWrite(address, value); break; - case SnesMemoryType::GameboyMemory: _cartridge->GetGameboy()->GetMemoryManager()->DebugWrite(address, value); break; + case SnesMemoryType::GameboyMemory: _gameboy->GetMemoryManager()->DebugWrite(address, value); break; case SnesMemoryType::NesMemory: _nesMemoryManager->DebugWrite(address, value); break; default: @@ -217,7 +220,7 @@ uint8_t MemoryDumper::GetMemoryValue(SnesMemoryType memoryType, uint32_t address case SnesMemoryType::Sa1Memory: return _cartridge->GetSa1()->GetMemoryMappings()->Peek(address); case SnesMemoryType::GsuMemory: return _cartridge->GetGsu()->GetMemoryMappings()->Peek(address); case SnesMemoryType::Cx4Memory: return _cartridge->GetCx4()->GetMemoryMappings()->Peek(address); - case SnesMemoryType::GameboyMemory: return _cartridge->GetGameboy()->GetMemoryManager()->DebugRead(address); + case SnesMemoryType::GameboyMemory: return _gameboy->GetMemoryManager()->DebugRead(address); case SnesMemoryType::NesMemory: return _nesMemoryManager->DebugRead(address); default: diff --git a/Core/Debugger/MemoryDumper.h b/Core/Debugger/MemoryDumper.h index 0a5f5125..e3013185 100644 --- a/Core/Debugger/MemoryDumper.h +++ b/Core/Debugger/MemoryDumper.h @@ -8,6 +8,7 @@ class NesMemoryManager; class BaseCartridge; class Ppu; class Spc; +class Gameboy; class Emulator; class Debugger; class Disassembler; @@ -16,14 +17,15 @@ enum class SnesMemoryType; class MemoryDumper { private: - Emulator* _emu; - Ppu* _ppu; - Spc* _spc; - MemoryManager* _memoryManager; - NesMemoryManager* _nesMemoryManager; - BaseCartridge* _cartridge; - Debugger* _debugger; - Disassembler* _disassembler; + Emulator* _emu = nullptr; + Ppu* _ppu = nullptr; + Spc* _spc = nullptr; + Gameboy* _gameboy = nullptr; + MemoryManager* _memoryManager = nullptr; + NesMemoryManager* _nesMemoryManager = nullptr; + BaseCartridge* _cartridge = nullptr; + Debugger* _debugger = nullptr; + Disassembler* _disassembler = nullptr; public: MemoryDumper(Debugger* debugger); diff --git a/Core/Debugger/PpuTools.cpp b/Core/Debugger/PpuTools.cpp index 70c3f9b6..b18ad84c 100644 --- a/Core/Debugger/PpuTools.cpp +++ b/Core/Debugger/PpuTools.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "Debugger/PpuTools.h" #include "Debugger/DebugTypes.h" +#include "Debugger/DebugBreakHelper.h" #include "Shared/NotificationManager.h" #include "Shared/SettingTypes.h" #include "SNES/SnesDefaultVideoFilter.h" @@ -10,9 +11,10 @@ #include "SNES/MemoryManager.h" #include "Gameboy/GbTypes.h" -PpuTools::PpuTools(Emulator *emu) +PpuTools::PpuTools(Debugger* debugger, Emulator *emu) { _emu = emu; + _debugger = debugger; } uint8_t PpuTools::GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, const uint8_t bpp, const uint32_t pixelStart, const uint8_t shift) @@ -387,7 +389,8 @@ void PpuTools::GetSpritePreview(GetSpritePreviewOptions options, PpuState state, void PpuTools::SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle, CpuType cpuType) { - //TODO Thread safety + DebugBreakHelper helper(_debugger); + ViewerRefreshConfig cfg; cfg.Scanline = scanline; cfg.Cycle = cycle; @@ -397,7 +400,7 @@ void PpuTools::SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint1 void PpuTools::RemoveViewer(uint32_t viewerId) { - //TODO Thread safety + DebugBreakHelper helper(_debugger); _updateTimings.erase(viewerId); } diff --git a/Core/Debugger/PpuTools.h b/Core/Debugger/PpuTools.h index 2633abbf..a701c3b5 100644 --- a/Core/Debugger/PpuTools.h +++ b/Core/Debugger/PpuTools.h @@ -5,6 +5,8 @@ #include "Shared/NotificationManager.h" #include "SNES/PpuTypes.h" +class Debugger; +class Emulator; class Ppu; struct GbPpuState; @@ -18,7 +20,8 @@ struct ViewerRefreshConfig class PpuTools { private: - Emulator *_emu; + Emulator* _emu; + Debugger* _debugger; unordered_map _updateTimings; uint8_t GetTilePixelColor(const uint8_t* ram, const uint32_t ramMask, const uint8_t bpp, const uint32_t pixelStart, const uint8_t shift); @@ -29,7 +32,7 @@ private: uint32_t GetRgbPixelColor(uint32_t* colors, uint8_t colorIndex, uint8_t palette, uint8_t bpp, bool directColorMode, uint16_t basePaletteOffset); public: - PpuTools(Emulator *emu); + PpuTools(Debugger* debugger, Emulator *emu); void GetTileView(GetTileViewOptions options, uint8_t *source, uint32_t srcSize, uint32_t* palette, uint32_t *outBuffer); void GetTilemap(GetTilemapOptions options, PpuState state, uint8_t* vram, uint8_t* cgram, uint32_t *outBuffer); diff --git a/Core/Debugger/TraceLogFileSaver.h b/Core/Debugger/TraceLogFileSaver.h new file mode 100644 index 00000000..b350b33e --- /dev/null +++ b/Core/Debugger/TraceLogFileSaver.h @@ -0,0 +1,42 @@ +#pragma once +#include "stdafx.h" + +class TraceLogFileSaver +{ +private: + bool _logToFile; + string _outputFilepath; + string _outputBuffer; + ofstream _outputFile; + +public: + void StartLogging(string filename) + { + _outputBuffer.clear(); + _outputFile.open(filename, ios::out | ios::binary); + _logToFile = true; + } + + void StopLogging() + { + if(_logToFile) { + _logToFile = false; + if(_outputFile) { + if(!_outputBuffer.empty()) { + _outputFile << _outputBuffer; + } + _outputFile.close(); + } + } + } + + /*void LogExtraInfo(const char* log, uint32_t cycleCount) + { + if(_logToFile) { + //Flush current buffer + _outputFile << _outputBuffer; + _outputBuffer.clear(); + _outputFile << "[" << log << " - Cycle: " << std::to_string(cycleCount) << "]" << (_options.UseWindowsEol ? "\r\n" : "\n"); + } + }*/ +}; \ No newline at end of file diff --git a/Core/Debugger/TraceLogger.cpp b/Core/Debugger/TraceLogger.cpp deleted file mode 100644 index 4e5c4444..00000000 --- a/Core/Debugger/TraceLogger.cpp +++ /dev/null @@ -1,789 +0,0 @@ -#include "stdafx.h" -#include -#include -#include "Debugger/TraceLogger.h" -#include "Debugger/DisassemblyInfo.h" -#include "Debugger/Debugger.h" -#include "Debugger/LabelManager.h" -#include "Debugger/DebugTypes.h" -#include "Debugger/DebugUtilities.h" -#include "Debugger/DebugBreakHelper.h" -#include "SNES/MemoryManager.h" -#include "SNES/Console.h" -#include "SNES/CpuTypes.h" -#include "SNES/SpcTypes.h" -#include "SNES/Coprocessors/DSP/NecDspTypes.h" -#include "SNES/PpuTypes.h" -#include "SNES/Coprocessors/CX4/Cx4Types.h" -#include "SNES/Coprocessors/GSU/GsuTypes.h" -#include "Gameboy/GbTypes.h" -#include "NES/NesTypes.h" -#include "Utilities/HexUtilities.h" -#include "Shared/Emulator.h" -#include "Shared/EmuSettings.h" - -string TraceLogger::_executionTrace = ""; - -TraceLogger::TraceLogger(Debugger* debugger) -{ - _debugger = debugger; - _console = debugger->GetConsole(); - _settings = debugger->GetEmulator()->GetSettings(); - _labelManager = debugger->GetLabelManager().get(); - _memoryDumper = debugger->GetMemoryDumper(); - _options = {}; - _currentPos = 0; - _logCount = 0; - _logToFile = false; - _pendingLog = false; - - _snesCpuState = new CpuState[TraceLogger::ExecutionLogSize]; - _spcState = new SpcState[TraceLogger::ExecutionLogSize]; - _necDspState = new NecDspState[TraceLogger::ExecutionLogSize]; - _gsuState = new GsuState[TraceLogger::ExecutionLogSize]; - _cx4State = new Cx4State[TraceLogger::ExecutionLogSize]; - _gbCpuState = new GbCpuState[TraceLogger::ExecutionLogSize]; - _nesCpuState = new NesCpuState[TraceLogger::ExecutionLogSize]; - - _disassemblyCache = new DisassemblyInfo[TraceLogger::ExecutionLogSize]; - _logCpuType = new CpuType[TraceLogger::ExecutionLogSize]; - - SetOptions({}); - _logCpu[(int)CpuType::Nes] = true; -} - -TraceLogger::~TraceLogger() -{ - StopLogging(); - - delete[] _disassemblyCache; - delete[] _logCpuType; - - delete[] _snesCpuState; - delete[] _spcState; - delete[] _necDspState; - delete[] _gsuState; - delete[] _cx4State; - delete[] _gbCpuState; - delete[] _nesCpuState; -} - -template -void TraceLogger::WriteValue(string &output, T value, RowPart& rowPart) -{ - string str = rowPart.DisplayInHex ? HexUtilities::ToHex(value) : std::to_string(value); - output += str; - if(rowPart.MinWidth > (int)str.size()) { - output += std::string(rowPart.MinWidth - str.size(), ' '); - } -} - -template<> -void TraceLogger::WriteValue(string &output, string value, RowPart& rowPart) -{ - output += value; - if(rowPart.MinWidth > (int)value.size()) { - output += std::string(rowPart.MinWidth - value.size(), ' '); - } -} - -void TraceLogger::SetOptions(TraceLoggerOptions options) -{ - DebugBreakHelper helper(_debugger); - _options = options; - - _logCpu[(int)CpuType::Cpu] = options.LogCpu; - _logCpu[(int)CpuType::Spc] = options.LogSpc; - _logCpu[(int)CpuType::NecDsp] = options.LogNecDsp; - _logCpu[(int)CpuType::Sa1] = options.LogSa1; - _logCpu[(int)CpuType::Gsu] = options.LogGsu; - _logCpu[(int)CpuType::Cx4] = options.LogCx4; - _logCpu[(int)CpuType::Gameboy] = options.LogGameboy; - _logCpu[(int)CpuType::Nes] = options.LogNes; - - string condition = _options.Condition; - string format = _options.Format; - - /*_conditionData = ExpressionData(); - if(!condition.empty()) { - bool success = false; - ExpressionData rpnList = _expEvaluator->GetRpnList(condition, success); - if(success) { - _conditionData = rpnList; - } - }*/ - - ParseFormatString(_rowParts, format); - ParseFormatString(_spcRowParts, "[PC,4h] [ByteCode,11h] [Disassembly][EffectiveAddress] [MemoryValue,h][Align,48] A:[A,2h] X:[X,2h] Y:[Y,2h] S:[SP,2h] P:[P,8] H:[Cycle,3] V:[Scanline,3]"); - ParseFormatString(_dspRowParts, "[PC,4h] [ByteCode,11h] [Disassembly] [Align,65] [A,2h] S:[SP,2h] H:[Cycle,3] V:[Scanline,3]"); - ParseFormatString(_gsuRowParts, "[PC,6h] [ByteCode,11h] [Disassembly] [Align,50] SRC:[X,2] DST:[Y,2] R0:[A,2h] H:[Cycle,3] V:[Scanline,3]"); - ParseFormatString(_cx4RowParts, "[PC,6h] [ByteCode,11h] [Disassembly] [Align,45] [A,2h] H:[Cycle,3] V:[Scanline,3]"); - ParseFormatString(_gbRowParts, "[PC,6h] [ByteCode,11h] [Disassembly] [Align,45] A:[A,2h] B:[B,2h] C:[C,2h] D:[D,2h] E:[E,2h] HL:[H,2h][L,2h] F:[F,2h] SP:[SP,4h] CYC:[Cycle,3] LY:[Scanline,3]"); - ParseFormatString(_nesRowParts, "[PC,4h] [ByteCode,11h] [Disassembly][EffectiveAddress] [MemoryValue,2h][Align,48] A:[A,2h] X:[X,2h] Y:[Y,2h] P:[P,8] SP:[SP,2h] CYC:[Cycle,3] SL:[Scanline,3] FC:[FrameCount] CPU Cycle:[CycleCount]"); -} - -void TraceLogger::ParseFormatString(vector &rowParts, string format) -{ - rowParts.clear(); - - std::regex formatRegex = std::regex("(\\[\\s*([^[]*?)\\s*(,\\s*([\\d]*)\\s*(h){0,1}){0,1}\\s*\\])|([^[]*)", std::regex_constants::icase); - std::sregex_iterator start = std::sregex_iterator(format.cbegin(), format.cend(), formatRegex); - std::sregex_iterator end = std::sregex_iterator(); - - for(std::sregex_iterator it = start; it != end; it++) { - const std::smatch& match = *it; - - if(match.str(1) == "") { - RowPart part = {}; - part.DataType = RowDataType::Text; - part.Text = match.str(6); - rowParts.push_back(part); - } else { - RowPart part = {}; - - string dataType = match.str(2); - if(dataType == "ByteCode") { - part.DataType = RowDataType::ByteCode; - } else if(dataType == "Disassembly") { - part.DataType = RowDataType::Disassembly; - } else if(dataType == "EffectiveAddress") { - part.DataType = RowDataType::EffectiveAddress; - } else if(dataType == "MemoryValue") { - part.DataType = RowDataType::MemoryValue; - } else if(dataType == "Align") { - part.DataType = RowDataType::Align; - } else if(dataType == "PC") { - part.DataType = RowDataType::PC; - } else if(dataType == "A") { - part.DataType = RowDataType::A; - } else if(dataType == "B") { - part.DataType = RowDataType::B; - } else if(dataType == "C") { - part.DataType = RowDataType::C; - } else if(dataType == "D") { - part.DataType = RowDataType::D; - } else if(dataType == "E") { - part.DataType = RowDataType::E; - } else if(dataType == "F") { - part.DataType = RowDataType::F; - } else if(dataType == "H") { - part.DataType = RowDataType::H; - } else if(dataType == "L") { - part.DataType = RowDataType::L; - } else if(dataType == "X") { - part.DataType = RowDataType::X; - } else if(dataType == "Y") { - part.DataType = RowDataType::Y; - } else if(dataType == "D") { - part.DataType = RowDataType::D; - } else if(dataType == "DB") { - part.DataType = RowDataType::DB; - } else if(dataType == "P") { - part.DataType = RowDataType::PS; - } else if(dataType == "SP") { - part.DataType = RowDataType::SP; - } else if(dataType == "Cycle") { - part.DataType = RowDataType::Cycle; - } else if(dataType == "HClock") { - part.DataType = RowDataType::HClock; - } else if(dataType == "Scanline") { - part.DataType = RowDataType::Scanline; - } else if(dataType == "FrameCount") { - part.DataType = RowDataType::FrameCount; - } else if(dataType == "CycleCount") { - part.DataType = RowDataType::CycleCount; - } else { - part.DataType = RowDataType::Text; - part.Text = "[Invalid tag]"; - } - - if(!match.str(4).empty()) { - try { - part.MinWidth = std::stoi(match.str(4)); - } catch(std::exception&) { - } - } - part.DisplayInHex = match.str(5) == "h"; - - rowParts.push_back(part); - } - } -} - -void TraceLogger::StartLogging(string filename) -{ - _outputBuffer.clear(); - _outputFile.open(filename, ios::out | ios::binary); - _logToFile = true; -} - -void TraceLogger::StopLogging() -{ - if(_logToFile) { - _logToFile = false; - if(_outputFile) { - if(!_outputBuffer.empty()) { - _outputFile << _outputBuffer; - } - _outputFile.close(); - } - } -} - -void TraceLogger::LogExtraInfo(const char *log, uint32_t cycleCount) -{ - if(_logToFile && _options.ShowExtraInfo) { - //Flush current buffer - _outputFile << _outputBuffer; - _outputBuffer.clear(); - _outputFile << "[" << log << " - Cycle: " << std::to_string(cycleCount) << "]" << (_options.UseWindowsEol ? "\r\n" : "\n"); - } -} - -template -void TraceLogger::GetStatusFlag(string &output, uint8_t ps, RowPart& part) -{ - constexpr char cpuActiveStatusLetters[8] = { 'N', 'V', 'M', 'X', 'D', 'I', 'Z', 'C' }; - constexpr char cpuInactiveStatusLetters[8] = { 'n', 'v', 'm', 'x', 'd', 'i', 'z', 'c' }; - - constexpr char spcActiveStatusLetters[8] = { 'N', 'V', 'P', 'B', 'H', 'I', 'Z', 'C' }; - constexpr char spcInactiveStatusLetters[8] = { 'n', 'v', 'p', 'b', 'h', 'i', 'z', 'c' }; - - constexpr char nesActiveStatusLetters[8] = { 'N', 'V', '-', '-', 'D', 'I', 'Z', 'C' }; - constexpr char nesInactiveStatusLetters[8] = { 'n', 'v', '-', '-', 'd', 'i', 'z', 'c' }; - - const char *activeStatusLetters = cpuType == CpuType::Cpu ? cpuActiveStatusLetters : (cpuType == CpuType::Spc ? spcActiveStatusLetters : nesActiveStatusLetters); - const char *inactiveStatusLetters = cpuType == CpuType::Cpu ? cpuInactiveStatusLetters : (cpuType == CpuType::Spc ? spcInactiveStatusLetters : nesInactiveStatusLetters); - - if(part.DisplayInHex) { - WriteValue(output, ps, part); - } else { - string flags; - for(int i = 0; i < 8; i++) { - if(ps & 0x80) { - flags += activeStatusLetters[i]; - } else if(part.MinWidth >= 8) { - flags += inactiveStatusLetters[i]; - } - ps <<= 1; - } - WriteValue(output, flags, part); - } -} - -void TraceLogger::WriteByteCode(DisassemblyInfo &info, RowPart &rowPart, string &output) -{ - string byteCode; - info.GetByteCode(byteCode); - if(!rowPart.DisplayInHex) { - //Remove $ marks if not in "hex" mode (but still display the bytes as hex) - byteCode.erase(std::remove(byteCode.begin(), byteCode.end(), '$'), byteCode.end()); - } - WriteValue(output, byteCode, rowPart); -} - -void TraceLogger::WriteDisassembly(DisassemblyInfo &info, RowPart &rowPart, uint8_t sp, uint32_t pc, string &output) -{ - int indentLevel = 0; - string code; - - if(_options.IndentCode) { - indentLevel = 0xFF - (sp & 0xFF); - code = std::string(indentLevel, ' '); - } - - LabelManager* labelManager = _options.UseLabels ? _labelManager : nullptr; - info.GetDisassembly(code, pc, labelManager, _settings); - WriteValue(output, code, rowPart); -} - -void TraceLogger::WriteEffectiveAddress(DisassemblyInfo &info, RowPart &rowPart, void *cpuState, string &output, SnesMemoryType cpuMemoryType, CpuType cpuType) -{ - int32_t effectiveAddress = info.GetEffectiveAddress(_debugger, cpuState, cpuType); - if(effectiveAddress >= 0) { - if(_options.UseLabels) { - AddressInfo addr { effectiveAddress, cpuMemoryType }; - string label = _labelManager->GetLabel(addr); - if(!label.empty()) { - WriteValue(output, " [" + label + "]", rowPart); - return; - } - } - WriteValue(output, " [" + HexUtilities::ToHex24(effectiveAddress) + "]", rowPart); - } -} - -void TraceLogger::WriteMemoryValue(DisassemblyInfo &info, RowPart &rowPart, void *cpuState, string &output, SnesMemoryType memType, CpuType cpuType) -{ - int32_t address = info.GetEffectiveAddress(_debugger, cpuState, cpuType); - if(address >= 0) { - uint8_t valueSize; - uint16_t value = info.GetMemoryValue(address, _memoryDumper, memType, valueSize); - if(rowPart.DisplayInHex) { - output += "= $"; - if(valueSize == 2) { - WriteValue(output, (uint16_t)value, rowPart); - } else { - WriteValue(output, (uint8_t)value, rowPart); - } - } else { - output += "= "; - } - } -} - -void TraceLogger::WriteAlign(int originalSize, RowPart &rowPart, string &output) -{ - if((int)output.size() - originalSize < rowPart.MinWidth) { - output += std::string(rowPart.MinWidth - (output.size() - originalSize), ' '); - } -} - -void TraceLogger::GetTraceRow(string &output, CpuState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo, SnesMemoryType memType, CpuType cpuType) -{ - int originalSize = (int)output.size(); - uint32_t pcAddress = (cpuState.K << 16) | cpuState.PC; - for(RowPart& rowPart : _rowParts) { - switch(rowPart.DataType) { - case RowDataType::Text: output += rowPart.Text; break; - case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break; - case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, (uint8_t)cpuState.SP, pcAddress, output); break; - case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, memType, cpuType); break; - case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, memType, cpuType); break; - case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break; - - case RowDataType::PC: WriteValue(output, HexUtilities::ToHex24(pcAddress), rowPart); break; - case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break; - case RowDataType::X: WriteValue(output, cpuState.X, rowPart); break; - case RowDataType::Y: WriteValue(output, cpuState.Y, rowPart); break; - case RowDataType::D: WriteValue(output, cpuState.D, rowPart); break; - case RowDataType::DB: WriteValue(output, cpuState.DBR, rowPart); break; - case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break; - case RowDataType::PS: GetStatusFlag(output, cpuState.PS, rowPart); break; - case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; - case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; - case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break; - case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; - case RowDataType::CycleCount: - WriteValue(output, (uint32_t)(cpuState.CycleCount >> 32), rowPart); - WriteValue(output, (uint32_t)cpuState.CycleCount, rowPart); - break; - default: break; - } - } - output += _options.UseWindowsEol ? "\r\n" : "\n"; -} - -void TraceLogger::GetTraceRow(string &output, SpcState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo) -{ - int originalSize = (int)output.size(); - uint32_t pcAddress = cpuState.PC; - for(RowPart& rowPart : _spcRowParts) { - switch(rowPart.DataType) { - case RowDataType::Text: output += rowPart.Text; break; - case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break; - case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, cpuState.SP, pcAddress, output); break; - case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::SpcMemory, CpuType::Spc); break; - case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::SpcMemory, CpuType::Spc); break; - case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break; - - case RowDataType::PC: WriteValue(output, HexUtilities::ToHex((uint16_t)pcAddress), rowPart); break; - case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break; - case RowDataType::X: WriteValue(output, cpuState.X, rowPart); break; - case RowDataType::Y: WriteValue(output, cpuState.Y, rowPart); break; - case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break; - case RowDataType::PS: GetStatusFlag(output, cpuState.PS, rowPart); break; - case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; - case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; - case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break; - case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; - - default: break; - } - } - output += _options.UseWindowsEol ? "\r\n" : "\n"; -} - -void TraceLogger::GetTraceRow(string &output, NecDspState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo) -{ - int originalSize = (int)output.size(); - uint32_t pcAddress = cpuState.PC; - for(RowPart& rowPart : _dspRowParts) { - switch(rowPart.DataType) { - case RowDataType::Text: output += rowPart.Text; break; - case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break; - case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, cpuState.SP, pcAddress, output); break; - case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break; - - case RowDataType::PC: WriteValue(output, HexUtilities::ToHex((uint16_t)pcAddress), rowPart); break; - case RowDataType::A: - output += "A:" + HexUtilities::ToHex(cpuState.A); - output += " B:" + HexUtilities::ToHex(cpuState.B); - output += " DR:" + HexUtilities::ToHex(cpuState.DR); - output += " DP:" + HexUtilities::ToHex(cpuState.DP); - output += " SR:" + HexUtilities::ToHex(cpuState.SR); - output += " K:" + HexUtilities::ToHex(cpuState.K); - output += " L:" + HexUtilities::ToHex(cpuState.L); - output += " M:" + HexUtilities::ToHex(cpuState.M); - output += " N:" + HexUtilities::ToHex(cpuState.N); - output += " RP:" + HexUtilities::ToHex(cpuState.RP); - output += " TR:" + HexUtilities::ToHex(cpuState.TR); - output += " TRB:" + HexUtilities::ToHex(cpuState.TRB) + " "; - //output += "FA=" + HexUtilities::ToHex(cpuState.FlagsA); - //output += "FB=" + HexUtilities::ToHex(cpuState.FlagsB); - WriteValue(output, cpuState.A, rowPart); - break; - case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break; - case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; - case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; - case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break; - case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; - default: break; - } - } - output += _options.UseWindowsEol ? "\r\n" : "\n"; -} - -void TraceLogger::GetTraceRow(string &output, GsuState &gsuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo) -{ - int originalSize = (int)output.size(); - uint32_t pcAddress = (gsuState.ProgramBank << 16) | gsuState.R[15]; - for(RowPart& rowPart : _gsuRowParts) { - switch(rowPart.DataType) { - case RowDataType::Text: output += rowPart.Text; break; - case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break; - case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, 0, pcAddress, output); break; - case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break; - - case RowDataType::PC: WriteValue(output, HexUtilities::ToHex24(pcAddress), rowPart); break; - case RowDataType::A: - WriteValue(output, gsuState.R[0], rowPart); - for(int i = 1; i < 16; i++) { - output += " R" + std::to_string(i) + ":" + HexUtilities::ToHex(gsuState.R[i]); - } - break; - case RowDataType::X: WriteValue(output, gsuState.SrcReg, rowPart); break; - case RowDataType::Y: WriteValue(output, gsuState.DestReg, rowPart); break; - - case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; - case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; - case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break; - case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; - default: break; - } - } - output += _options.UseWindowsEol ? "\r\n" : "\n"; -} - - -void TraceLogger::GetTraceRow(string &output, Cx4State &cx4State, PpuState &ppuState, DisassemblyInfo &disassemblyInfo) -{ - int originalSize = (int)output.size(); - uint32_t pcAddress = (cx4State.Cache.Address[cx4State.Cache.Page] + (cx4State.PC * 2)) & 0xFFFFFF; - for(RowPart& rowPart : _cx4RowParts) { - switch(rowPart.DataType) { - case RowDataType::Text: output += rowPart.Text; break; - case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break; - case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, 0, pcAddress, output); break; - case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break; - - case RowDataType::PC: WriteValue(output, HexUtilities::ToHex24(pcAddress), rowPart); break; - case RowDataType::A: - output += " A:" + HexUtilities::ToHex24(cx4State.A); - output += string(" ") + (cx4State.Carry ? "C" : "c") + (cx4State.Zero ? "Z" : "z") + (cx4State.Overflow ? "V" : "v") + (cx4State.Negative ? "N" : "n"); - - output += " PC:" + HexUtilities::ToHex(cx4State.PC); - output += " MAR:" + HexUtilities::ToHex24(cx4State.MemoryAddressReg); - output += " MDR:" + HexUtilities::ToHex24(cx4State.MemoryDataReg); - output += " DPR:" + HexUtilities::ToHex24(cx4State.DataPointerReg); - output += " ML:" + HexUtilities::ToHex24((uint32_t)cx4State.Mult & 0xFFFFFF); - output += " MH:" + HexUtilities::ToHex24((uint32_t)(cx4State.Mult >> 24) & 0xFFFFFF); - for(int i = 0; i < 16; i++) { - output += " R" + std::to_string(i) + ":" + HexUtilities::ToHex24(cx4State.Regs[i]); - } - break; - - case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; - case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; - case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break; - case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; - default: break; - } - } - output += _options.UseWindowsEol ? "\r\n" : "\n"; -} - -void TraceLogger::GetTraceRow(string& output, GbCpuState& cpuState, GbPpuState& ppuState, DisassemblyInfo& disassemblyInfo) -{ - int originalSize = (int)output.size(); - uint32_t pcAddress = cpuState.PC; - for(RowPart& rowPart : _gbRowParts) { - switch(rowPart.DataType) { - case RowDataType::Text: output += rowPart.Text; break; - case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break; - case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, (uint8_t)cpuState.SP, pcAddress, output); break; - case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::GameboyMemory, CpuType::Gameboy); break; - case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::GameboyMemory, CpuType::Gameboy); break; - case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break; - - case RowDataType::PC: WriteValue(output, HexUtilities::ToHex((uint16_t)pcAddress), rowPart); break; - case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break; - case RowDataType::B: WriteValue(output, cpuState.B, rowPart); break; - case RowDataType::C: WriteValue(output, cpuState.C, rowPart); break; - case RowDataType::D: WriteValue(output, cpuState.D, rowPart); break; - case RowDataType::E: WriteValue(output, cpuState.E, rowPart); break; - case RowDataType::F: WriteValue(output, cpuState.Flags, rowPart); break; - case RowDataType::H: WriteValue(output, cpuState.H, rowPart); break; - case RowDataType::L: WriteValue(output, cpuState.L, rowPart); break; - case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break; - case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; - case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; - case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; - default: break; - } - } - output += _options.UseWindowsEol ? "\r\n" : "\n"; -} - -void TraceLogger::GetTraceRow(string &output, NesCpuState &cpuState, NesPpuState &ppuState, DisassemblyInfo &disassemblyInfo) -{ - int originalSize = (int)output.size(); - for(RowPart& rowPart : _nesRowParts) { - switch(rowPart.DataType) { - case RowDataType::Text: output += rowPart.Text; break; - case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break; - case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, cpuState.SP, cpuState.PC, output); break; - case RowDataType::EffectiveAddress: WriteEffectiveAddress(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::NesMemory, CpuType::Nes); break; - case RowDataType::MemoryValue: WriteMemoryValue(disassemblyInfo, rowPart, &cpuState, output, SnesMemoryType::NesMemory, CpuType::Nes); break; - case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break; - - case RowDataType::PC: WriteValue(output, HexUtilities::ToHex((uint16_t)cpuState.PC), rowPart); break; - case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break; - case RowDataType::X: WriteValue(output, cpuState.X, rowPart); break; - case RowDataType::Y: WriteValue(output, cpuState.Y, rowPart); break; - case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break; - case RowDataType::PS: GetStatusFlag(output, cpuState.PS, rowPart); break; - case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; - case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; - case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; - case RowDataType::CycleCount: - WriteValue(output, (uint32_t)(cpuState.CycleCount >> 32), rowPart); - WriteValue(output, (uint32_t)cpuState.CycleCount, rowPart); - break; - - default: break; - } - } - output += _options.UseWindowsEol ? "\r\n" : "\n"; -} - -/* -bool TraceLogger::ConditionMatches(DebugState &state, DisassemblyInfo &disassemblyInfo, OperationInfo &operationInfo) -{ - if(!_conditionData.RpnQueue.empty()) { - EvalResultType type; - if(!_expEvaluator->Evaluate(_conditionData, state, type, operationInfo)) { - if(operationInfo.OperationType == MemoryOperationType::ExecOpCode) { - //Condition did not match, keep state/disassembly info for instruction's subsequent cycles - _lastState = state; - _lastDisassemblyInfo = disassemblyInfo; - _pendingLog = true; - } - return false; - } - } - return true; -} -*/ - -void TraceLogger::GetTraceRow(string &output, CpuType cpuType, DisassemblyInfo &disassemblyInfo, BaseState &state) -{ - PpuState ppu = {}; - GbPpuState gbPpu = {}; - NesPpuState nesPpu = {}; - switch(cpuType) { - case CpuType::Cpu: GetTraceRow(output, (CpuState&)state, ppu, disassemblyInfo, SnesMemoryType::CpuMemory, cpuType); break; - case CpuType::Spc: GetTraceRow(output, (SpcState&)state, ppu, disassemblyInfo); break; - case CpuType::NecDsp: GetTraceRow(output, (NecDspState&)state, ppu, disassemblyInfo); break; - case CpuType::Sa1: GetTraceRow(output, (CpuState&)state, ppu, disassemblyInfo, SnesMemoryType::Sa1Memory, cpuType); break; - case CpuType::Gsu: GetTraceRow(output, (GsuState&)state, ppu, disassemblyInfo); break; - case CpuType::Cx4: GetTraceRow(output, (Cx4State&)state, ppu, disassemblyInfo); break; - case CpuType::Gameboy: GetTraceRow(output, (GbCpuState&)state, gbPpu, disassemblyInfo); break; - case CpuType::Nes: GetTraceRow(output, (NesCpuState&)state, nesPpu, disassemblyInfo); break; - default: throw std::runtime_error("Trace logger - Unsupported CPU type"); - } -} - -template -void TraceLogger::AddRow(CpuType cpuType, T& cpuState, DisassemblyInfo &disassemblyInfo) -{ - _logCpuType[_currentPos] = cpuType; - _disassemblyCache[_currentPos] = disassemblyInfo; - - switch(cpuType) { - case CpuType::Cpu: _snesCpuState[_currentPos] = (CpuState&)cpuState; break; - case CpuType::Spc: _spcState[_currentPos] = (SpcState&)cpuState; break; - case CpuType::NecDsp: _necDspState[_currentPos] = (NecDspState&)cpuState; break; - case CpuType::Sa1: _snesCpuState[_currentPos] = (CpuState&)cpuState; break; - case CpuType::Gsu: _gsuState[_currentPos] = (GsuState&)cpuState; break; - case CpuType::Cx4: _cx4State[_currentPos] = (Cx4State&)cpuState; break; - case CpuType::Gameboy: _gbCpuState[_currentPos] = (GbCpuState&)cpuState; break; - case CpuType::Nes: _nesCpuState[_currentPos] = (NesCpuState&)cpuState; break; - default: throw std::runtime_error("Trace logger - Unsupported CPU type"); - } - - _pendingLog = false; - - if(_logCount < ExecutionLogSize) { - _logCount++; - } - - if(_logToFile) { - GetTraceRow(_outputBuffer, cpuType, _disassemblyCache[_currentPos], cpuState); - if(_outputBuffer.size() > 32768) { - _outputFile << _outputBuffer; - _outputBuffer.clear(); - } - } - - _currentPos = (_currentPos + 1) % ExecutionLogSize; -} -/* -void TraceLogger::LogNonExec(OperationInfo& operationInfo) -{ - if(_pendingLog) { - if(ConditionMatches(_lastState, _lastDisassemblyInfo, operationInfo)) { - AddRow(_lastDisassemblyInfo, _lastState); - } - } -}*/ - -template -void TraceLogger::Log(CpuType cpuType, T& cpuState, DisassemblyInfo &disassemblyInfo) -{ - if(_logCpu[(int)cpuType]) { - //For the sake of performance, only log data for the CPUs we're actively displaying/logging - //if(ConditionMatches(state, disassemblyInfo, operationInfo)) { - AddRow(cpuType, cpuState, disassemblyInfo); - //} - } -} - -void TraceLogger::Clear() -{ - _logCount = 0; -} - -const char* TraceLogger::GetExecutionTrace(uint32_t lineCount) -{ - DebugBreakHelper helper(_debugger); - _executionTrace.clear(); - - int startPos = (_currentPos > 0 ? _currentPos : TraceLogger::ExecutionLogSize) - 1; - - bool enabled = false; - for(int i = 0; i <= (int)DebugUtilities::GetLastCpuType(); i++) { - enabled |= _logCpu[i]; - } - - if(enabled && lineCount > 0) { - for(int i = 0; i < TraceLogger::ExecutionLogSize; i++) { - int index = (startPos - i); - if(index < 0) { - index = TraceLogger::ExecutionLogSize + index; - } - - if((i > 0 && startPos == index) || !_disassemblyCache[index].IsInitialized()) { - //If the entire array was checked, or this element is not initialized, stop - break; - } - - CpuType cpuType = _logCpuType[index]; - if(!_logCpu[(int)cpuType]) { - //This line isn't for a CPU currently being logged - continue; - } - - BaseState* state; - switch(cpuType) { - case CpuType::Cpu: { - state = &_snesCpuState[index]; - CpuState* snesCpu = (CpuState*)state; - _executionTrace += "\x2\x1" + HexUtilities::ToHex24((snesCpu->K << 16) | snesCpu->PC) + "\x1"; - break; - } - - case CpuType::Spc: { - state = &_spcState[index]; - SpcState* spc = (SpcState*)state; - _executionTrace += "\x3\x1" + HexUtilities::ToHex(spc->PC) + "\x1"; - break; - } - - case CpuType::NecDsp: { - state = &_spcState[index]; - NecDspState* necDsp = (NecDspState*)state; - _executionTrace += "\x4\x1" + HexUtilities::ToHex(necDsp->PC) + "\x1"; - break; - } - - case CpuType::Sa1: { - state = &_snesCpuState[index]; - CpuState* sa1 = (CpuState*)state; - _executionTrace += "\x4\x1" + HexUtilities::ToHex24((sa1->K << 16) | sa1->PC) + "\x1"; - break; - } - - case CpuType::Gsu: { - state = &_gsuState[index]; - GsuState* gsu = (GsuState*)state; - _executionTrace += "\x4\x1" + HexUtilities::ToHex24((gsu->ProgramBank << 16) | gsu->R[15]) + "\x1"; - break; - } - - case CpuType::Cx4: { - state = &_cx4State[index]; - Cx4State* cx4 = (Cx4State*)state; - _executionTrace += "\x4\x1" + HexUtilities::ToHex24((cx4->Cache.Address[cx4->Cache.Page] + (cx4->PC * 2)) & 0xFFFFFF) + "\x1"; - break; - } - - case CpuType::Gameboy: { - state = &_gbCpuState[index]; - GbCpuState* gb = (GbCpuState*)state; - _executionTrace += "\x4\x1" + HexUtilities::ToHex(gb->PC) + "\x1"; - break; - } - - case CpuType::Nes: { - state = &_nesCpuState[index]; - NesCpuState* nes = (NesCpuState*)state; - _executionTrace += "\x4\x1" + HexUtilities::ToHex(nes->PC) + "\x1"; - break; - } - - default: - throw std::runtime_error("Trace logger - Unsupported CPU type"); - } - - string byteCode; - _disassemblyCache[index].GetByteCode(byteCode); - _executionTrace += byteCode + "\x1"; - GetTraceRow(_executionTrace, cpuType, _disassemblyCache[index], *state); - - lineCount--; - if(lineCount == 0) { - break; - } - } - } - return _executionTrace.c_str(); -} - -template void TraceLogger::Log(CpuType cpuType, CpuState& cpuState, DisassemblyInfo& disassemblyInfo); -template void TraceLogger::Log(CpuType cpuType, SpcState& cpuState, DisassemblyInfo& disassemblyInfo); -template void TraceLogger::Log(CpuType cpuType, GsuState& cpuState, DisassemblyInfo& disassemblyInfo); -template void TraceLogger::Log(CpuType cpuType, Cx4State& cpuState, DisassemblyInfo& disassemblyInfo); -template void TraceLogger::Log(CpuType cpuType, NecDspState& cpuState, DisassemblyInfo& disassemblyInfo); -template void TraceLogger::Log(CpuType cpuType, GbCpuState& cpuState, DisassemblyInfo& disassemblyInfo); -template void TraceLogger::Log(CpuType cpuType, NesCpuState& cpuState, DisassemblyInfo& disassemblyInfo); diff --git a/Core/Debugger/TraceLogger.h b/Core/Debugger/TraceLogger.h deleted file mode 100644 index 90643ba7..00000000 --- a/Core/Debugger/TraceLogger.h +++ /dev/null @@ -1,172 +0,0 @@ -#pragma once -#include "stdafx.h" -#include "DisassemblyInfo.h" -#include "DebugUtilities.h" -#include "Utilities/SimpleLock.h" - -class IConsole; -class Debugger; -class LabelManager; -class MemoryDumper; -class EmuSettings; - -struct CpuState; -struct NecDspState; -struct GsuState; -struct Cx4State; -struct PpuState; -struct SpcState; -struct GbPpuState; -struct GbCpuState; -struct NesCpuState; -struct NesPpuState; -struct BaseState; - -struct TraceLoggerOptions -{ - bool LogCpu; - bool LogSpc; - bool LogNecDsp; - bool LogSa1; - bool LogGsu; - bool LogCx4; - bool LogGameboy; - bool LogNes; - - bool ShowExtraInfo; - bool IndentCode; - bool UseLabels; - bool UseWindowsEol; - bool ExtendZeroPage; - - char Condition[1000]; - char Format[1000]; -}; - -enum class RowDataType -{ - Text = 0, - ByteCode, - Disassembly, - EffectiveAddress, - MemoryValue, - Align, - PC, - A, - B, - C, - D, - E, - F, - H, - L, - X, - Y, - DB, - SP, - PS, - Cycle, - Scanline, - HClock, - FrameCount, - CycleCount -}; - -struct RowPart -{ - RowDataType DataType; - string Text; - bool DisplayInHex; - int MinWidth; -}; - -class TraceLogger -{ -private: - static constexpr int ExecutionLogSize = 30000; - - //Must be static to be thread-safe when switching game - static string _executionTrace; - - TraceLoggerOptions _options; - string _outputFilepath; - string _outputBuffer; - ofstream _outputFile; - IConsole* _console; - EmuSettings* _settings; - LabelManager* _labelManager; - MemoryDumper* _memoryDumper; - Debugger* _debugger; - - vector _rowParts; - vector _spcRowParts; - vector _dspRowParts; - vector _gsuRowParts; - vector _cx4RowParts; - vector _gbRowParts; - vector _nesRowParts; - - bool _logCpu[(int)DebugUtilities::GetLastCpuType() + 1] = {}; - - bool _pendingLog; - //CpuState _lastState; - //DisassemblyInfo _lastDisassemblyInfo; - - bool _logToFile; - uint32_t _currentPos; - uint32_t _logCount; - - CpuState* _snesCpuState; - SpcState* _spcState; - NecDspState* _necDspState; - GsuState* _gsuState; - Cx4State* _cx4State; - GbCpuState* _gbCpuState; - NesCpuState* _nesCpuState; - - DisassemblyInfo *_disassemblyCache = nullptr; - CpuType* _logCpuType = nullptr; - - template void GetStatusFlag(string &output, uint8_t ps, RowPart& part); - - void WriteByteCode(DisassemblyInfo &info, RowPart &rowPart, string &output); - void WriteDisassembly(DisassemblyInfo &info, RowPart &rowPart, uint8_t sp, uint32_t pc, string &output); - void WriteEffectiveAddress(DisassemblyInfo &info, RowPart &rowPart, void *cpuState, string &output, SnesMemoryType cpuMemoryType, CpuType cpuType); - void WriteMemoryValue(DisassemblyInfo &info, RowPart &rowPart, void *cpuState, string &output, SnesMemoryType memType, CpuType cpuType); - void WriteAlign(int originalSize, RowPart &rowPart, string &output); - - template void AddRow(CpuType cpuType, T& cpuState, DisassemblyInfo& disassemblyInfo); - - //bool ConditionMatches(DebugState &state, DisassemblyInfo &disassemblyInfo, OperationInfo &operationInfo); - - void ParseFormatString(vector &rowParts, string format); - - void GetTraceRow(string &output, CpuType cpuType, DisassemblyInfo &disassemblyInfo, BaseState &state); - void GetTraceRow(string &output, CpuState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo, SnesMemoryType memType, CpuType cpuType); - void GetTraceRow(string &output, SpcState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo); - void GetTraceRow(string &output, NecDspState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo); - void GetTraceRow(string &output, GsuState &gsuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo); - void GetTraceRow(string& output, Cx4State& cx4State, PpuState& ppuState, DisassemblyInfo& disassemblyInfo); - void GetTraceRow(string &output, GbCpuState &gbState, GbPpuState &gbPpuState, DisassemblyInfo &disassemblyInfo); - void GetTraceRow(string& output, NesCpuState& cpuState, NesPpuState& ppuState, DisassemblyInfo& disassemblyInfo); - - template void WriteValue(string &output, T value, RowPart& rowPart); - -public: - TraceLogger(Debugger* debugger); - ~TraceLogger(); - - __forceinline bool IsCpuLogged(CpuType type) { return _logCpu[(int)type]; } - - template void Log(CpuType cpuType, T& cpuState, DisassemblyInfo& disassemblyInfo); - - void Clear(); - //void LogNonExec(OperationInfo& operationInfo); - void SetOptions(TraceLoggerOptions options); - void StartLogging(string filename); - void StopLogging(); - - void LogExtraInfo(const char *log, uint32_t cycleCount); - - const char* GetExecutionTrace(uint32_t lineCount); -}; diff --git a/Core/Gameboy/Debugger/GbDebugger.cpp b/Core/Gameboy/Debugger/GbDebugger.cpp index b90a78b4..52f06e9e 100644 --- a/Core/Gameboy/Debugger/GbDebugger.cpp +++ b/Core/Gameboy/Debugger/GbDebugger.cpp @@ -3,9 +3,9 @@ #include "Gameboy/Debugger/GbDebugger.h" #include "Gameboy/Debugger/GameboyDisUtils.h" #include "Gameboy/Debugger/GbEventManager.h" +#include "Gameboy/Debugger/GbTraceLogger.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/Disassembler.h" -#include "Debugger/TraceLogger.h" #include "Debugger/CallstackManager.h" #include "Debugger/BreakpointManager.h" #include "Debugger/Debugger.h" @@ -28,7 +28,6 @@ GbDebugger::GbDebugger(Debugger* debugger) _debugger = debugger; _emu = debugger->GetEmulator(); - _traceLogger = debugger->GetTraceLogger().get(); _disassembler = debugger->GetDisassembler().get(); _memoryAccessCounter = debugger->GetMemoryAccessCounter().get(); @@ -39,11 +38,13 @@ GbDebugger::GbDebugger(Debugger* debugger) } _cpu = _gameboy->GetCpu(); + _ppu = _gameboy->GetPpu(); _settings = debugger->GetEmulator()->GetSettings(); _codeDataLogger.reset(new CodeDataLogger(SnesMemoryType::GbPrgRom, _gameboy->DebugGetMemorySize(SnesMemoryType::GbPrgRom), CpuType::Gameboy)); + _traceLogger.reset(new GbTraceLogger(debugger, _ppu)); - _eventManager.reset(new GbEventManager(debugger, _gameboy->GetCpu(), _gameboy->GetPpu())); + _eventManager.reset(new GbEventManager(debugger, _gameboy->GetCpu(), _ppu)); _callstackManager.reset(new CallstackManager(debugger)); _breakpointManager.reset(new BreakpointManager(debugger, CpuType::Gameboy, _eventManager.get())); _step.reset(new StepRequest()); @@ -71,11 +72,11 @@ void GbDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType t MemoryOperationInfo operation { addr, value, type }; BreakSource breakSource = BreakSource::Unspecified; - GbCpuState state = _cpu->GetState(); + GbCpuState& state = _cpu->GetState(); uint16_t pc = state.PC; if(type == MemoryOperationType::ExecOpCode) { - if(_traceLogger->IsCpuLogged(CpuType::Gameboy) || _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled)) { + if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled)) { if(addressInfo.Address >= 0) { if(addressInfo.Type == SnesMemoryType::GbPrgRom) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code); @@ -83,9 +84,9 @@ void GbDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType t _disassembler->BuildCache(addressInfo, 0, CpuType::Gameboy); } - if(_traceLogger->IsCpuLogged(CpuType::Gameboy)) { + if(_traceLogger->IsEnabled()) { DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Gameboy); - _traceLogger->Log(CpuType::Gameboy, state, disInfo); + _traceLogger->Log(state, disInfo, operation); } } @@ -137,12 +138,21 @@ void GbDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType t if(addressInfo.Address >= 0 && addressInfo.Type == SnesMemoryType::GbPrgRom) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code); } + + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } + _memoryAccessCounter->ProcessMemoryExec(addressInfo, _emu->GetMasterClock()); } else { if(addressInfo.Address >= 0 && addressInfo.Type == SnesMemoryType::GbPrgRom) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data); } + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } + if(addr < 0xFE00 || addr >= 0xFF80) { if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _emu->GetMasterClock())) { //Memory access was a read on an uninitialized memory address @@ -177,6 +187,10 @@ void GbDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType _disassembler->InvalidateCache(addressInfo, CpuType::Gameboy); } + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } + if(addr == 0xFFFF || (addr >= 0xFE00 && addr < 0xFF80) || (addr >= 0x8000 && addr <= 0x9FFF)) { _eventManager->AddEvent(DebugEventType::Register, operation); } @@ -225,10 +239,10 @@ void GbDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool _eventManager->AddEvent(DebugEventType::Irq); } -void GbDebugger::ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle) +void GbDebugger::ProcessPpuCycle(int16_t &scanline, uint16_t &cycle) { - scanline = _gameboy->GetPpu()->GetScanline(); - cycle = _gameboy->GetPpu()->GetCycle(); + scanline = _ppu->GetScanline(); + cycle = _ppu->GetCycle(); if(_step->PpuStepCount > 0) { _step->PpuStepCount--; @@ -238,7 +252,6 @@ void GbDebugger::ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle) } if(cycle == 0 && scanline == _step->BreakScanline) { - _step->BreakScanline = -1; _debugger->SleepUntilResume(BreakSource::PpuStep); } } @@ -272,3 +285,8 @@ BaseState& GbDebugger::GetState() { return _cpu->GetState(); } + +ITraceLogger* GbDebugger::GetTraceLogger() +{ + return _traceLogger.get(); +} diff --git a/Core/Gameboy/Debugger/GbDebugger.h b/Core/Gameboy/Debugger/GbDebugger.h index c01a8884..d9302a76 100644 --- a/Core/Gameboy/Debugger/GbDebugger.h +++ b/Core/Gameboy/Debugger/GbDebugger.h @@ -5,7 +5,7 @@ class Disassembler; class Debugger; -class TraceLogger; +class GbTraceLogger; class Gameboy; class CallstackManager; class MemoryAccessCounter; @@ -17,6 +17,7 @@ class GbAssembler; class Emulator; class CodeDataLogger; class GbCpu; +class GbPpu; enum class MemoryOperationType; @@ -25,8 +26,8 @@ class GbDebugger final : public IDebugger Debugger* _debugger; Emulator* _emu; GbCpu* _cpu; + GbPpu* _ppu; Disassembler* _disassembler; - TraceLogger* _traceLogger; MemoryAccessCounter* _memoryAccessCounter; Gameboy* _gameboy; EmuSettings* _settings; @@ -37,6 +38,7 @@ class GbDebugger final : public IDebugger unique_ptr _breakpointManager; unique_ptr _step; shared_ptr _assembler; + unique_ptr _traceLogger; uint8_t _prevOpCode = 0xFF; uint32_t _prevProgramCounter = 0; @@ -48,10 +50,10 @@ public: void Reset() override; - void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override; - void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override; + void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type); + void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type); void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) override; - void ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle) override; + void ProcessPpuCycle(int16_t &scanline, uint16_t &cycle); void Run() override; void Step(int32_t stepCount, StepType type) override; @@ -61,6 +63,7 @@ public: shared_ptr GetCallstackManager() override; shared_ptr GetCodeDataLogger() override; BreakpointManager* GetBreakpointManager() override; + ITraceLogger* GetTraceLogger() override; BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/Gameboy/Debugger/GbTraceLogger.cpp b/Core/Gameboy/Debugger/GbTraceLogger.cpp new file mode 100644 index 00000000..d4a9c85b --- /dev/null +++ b/Core/Gameboy/Debugger/GbTraceLogger.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "Gameboy/Debugger/GbTraceLogger.h" +#include "Gameboy/GbPpu.h" +#include "Gameboy/GbTypes.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Debugger.h" +#include "Debugger/DebugTypes.h" +#include "Utilities/HexUtilities.h" + +GbTraceLogger::GbTraceLogger(Debugger* debugger, GbPpu* ppu) : BaseTraceLogger(debugger, CpuType::Gameboy) +{ + _ppu = ppu; + + TraceLoggerOptions options = {}; + SetOptions(options); +} + +RowDataType GbTraceLogger::GetFormatTagType(string& tag) +{ + if(tag == "A") { + return RowDataType::A; + } else if(tag == "B") { + return RowDataType::B; + } else if(tag == "C") { + return RowDataType::C; + } else if(tag == "D") { + return RowDataType::D; + } else if(tag == "E") { + return RowDataType::E; + } else if(tag == "F") { + return RowDataType::F; + } else if(tag == "H") { + return RowDataType::H; + } else if(tag == "L") { + return RowDataType::L; + } else if(tag == "PS") { + return RowDataType::PS; + } else if(tag == "SP") { + return RowDataType::SP; + } else { + return RowDataType::Text; + } +} + +void GbTraceLogger::GetTraceRow(string &output, GbCpuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo) +{ + constexpr char activeStatusLetters[8] = { 'Z', 'N', 'H', 'C' }; + constexpr char inactiveStatusLetters[8] = { 'z', 'n', 'h', 'c' }; + + for(RowPart& rowPart : _rowParts) { + switch(rowPart.DataType) { + case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break; + case RowDataType::B: WriteIntValue(output, cpuState.B, rowPart); break; + case RowDataType::C: WriteIntValue(output, cpuState.C, rowPart); break; + case RowDataType::D: WriteIntValue(output, cpuState.D, rowPart); break; + case RowDataType::E: WriteIntValue(output, cpuState.E, rowPart); break; + case RowDataType::F: WriteIntValue(output, cpuState.Flags, rowPart); break; + case RowDataType::H: WriteIntValue(output, cpuState.H, rowPart); break; + case RowDataType::L: WriteIntValue(output, cpuState.L, rowPart); break; + case RowDataType::SP: WriteIntValue(output, cpuState.SP, rowPart); break; + case RowDataType::PS: GetStatusFlag(activeStatusLetters, inactiveStatusLetters, output, cpuState.Flags, rowPart, 4); break; + default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break; + } + } +} + +void GbTraceLogger::LogPpuState() +{ + _ppuState[_currentPos] = { + _ppu->GetCycle(), + _ppu->GetCycle(), + _ppu->GetScanline(), + _ppu->GetFrameCount() + }; +} \ No newline at end of file diff --git a/Core/Gameboy/Debugger/GbTraceLogger.h b/Core/Gameboy/Debugger/GbTraceLogger.h new file mode 100644 index 00000000..0b2ad6d5 --- /dev/null +++ b/Core/Gameboy/Debugger/GbTraceLogger.h @@ -0,0 +1,27 @@ +#pragma once +#include "stdafx.h" +#include "Debugger/BaseTraceLogger.h" +#include "Gameboy/GbTypes.h" + +class DisassemblyInfo; +class Debugger; +class GbPpu; + +class GbTraceLogger : public BaseTraceLogger +{ +private: + GbPpu* _ppu = nullptr; + +protected: + RowDataType GetFormatTagType(string& tag) override; + +public: + GbTraceLogger(Debugger* debugger, GbPpu* ppu); + + void GetTraceRow(string& output, GbCpuState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo); + void LogPpuState(); + + __forceinline uint32_t GetProgramCounter(GbCpuState& state) { return state.PC; } + __forceinline uint64_t GetCycleCount(GbCpuState& state) { return 0; } //TODO + __forceinline uint8_t GetStackPointer(GbCpuState& state) { return (uint8_t)state.SP; } +}; diff --git a/Core/NES/Debugger/NesDebugger.cpp b/Core/NES/Debugger/NesDebugger.cpp index b3a93e77..64c972a9 100644 --- a/Core/NES/Debugger/NesDebugger.cpp +++ b/Core/NES/Debugger/NesDebugger.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/Disassembler.h" -#include "Debugger/TraceLogger.h" #include "Debugger/CallstackManager.h" #include "Debugger/BreakpointManager.h" #include "Debugger/CodeDataLogger.h" @@ -17,6 +16,7 @@ #include "NES/Debugger/NesDebugger.h" #include "NES/Debugger/NesAssembler.h" #include "NES/Debugger/NesEventManager.h" +#include "NES/Debugger/NesTraceLogger.h" #include "Utilities/HexUtilities.h" #include "Utilities/FolderUtilities.h" #include "Shared/EmuSettings.h" @@ -34,7 +34,7 @@ NesDebugger::NesDebugger(Debugger* debugger) _ppu = console->GetPpu(); _mapper = console->GetMapper(); - _traceLogger = debugger->GetTraceLogger().get(); + _traceLogger.reset(new NesTraceLogger(debugger, _ppu)); _disassembler = debugger->GetDisassembler().get(); _memoryAccessCounter = debugger->GetMemoryAccessCounter().get(); _settings = debugger->GetEmulator()->GetSettings(); @@ -64,13 +64,15 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType { AddressInfo addressInfo = _mapper->GetAbsoluteAddress(addr); MemoryOperationInfo operation = { addr, value, type }; - - NesCpuState state = _cpu->GetState(); - + NesCpuState& state = _cpu->GetState(); BreakSource breakSource = BreakSource::Unspecified; + if(IsRegister(operation)) { + _eventManager->AddEvent(DebugEventType::Register, operation); + } + if(type == MemoryOperationType::ExecOpCode) { - bool needDisassemble = _traceLogger->IsCpuLogged(CpuType::Nes) || _settings->CheckDebuggerFlag(DebuggerFlags::NesDebuggerEnabled); + bool needDisassemble = _traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::NesDebuggerEnabled); if(addressInfo.Address >= 0) { if(addressInfo.Type == SnesMemoryType::NesPrgRom) { uint8_t flags = CdlFlags::Code; @@ -85,9 +87,9 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType } } - if(_traceLogger->IsCpuLogged(CpuType::Nes)) { + if(_traceLogger->IsEnabled()) { DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, CpuType::Nes); - _traceLogger->Log(CpuType::Nes, state, disInfo); + _traceLogger->Log(state, disInfo, operation); } uint32_t pc = state.PC; @@ -127,11 +129,20 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType if(addressInfo.Type == SnesMemoryType::NesPrgRom && addressInfo.Address >= 0) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code); } + + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } + _memoryAccessCounter->ProcessMemoryExec(addressInfo, _cpu->GetCycleCount()); } else { if(addressInfo.Type == SnesMemoryType::NesPrgRom && addressInfo.Address >= 0) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data); } + + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _cpu->GetCycleCount())) { //Memory access was a read on an uninitialized memory address @@ -148,10 +159,6 @@ void NesDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType } } - if(IsRegister(operation)) { - _eventManager->AddEvent(DebugEventType::Register, operation); - } - _debugger->ProcessBreakConditions(_step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource); } @@ -167,6 +174,10 @@ void NesDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType _eventManager->AddEvent(DebugEventType::Register, operation); } + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } + _memoryAccessCounter->ProcessMemoryWrite(addressInfo, _cpu->GetCycleCount()); _debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo); @@ -208,7 +219,7 @@ void NesDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool //_eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq); } -void NesDebugger::ProcessPpuCycle(uint16_t& scanline, uint16_t& cycle) +void NesDebugger::ProcessPpuCycle(int16_t& scanline, uint16_t& cycle) { scanline = _ppu->GetCurrentScanline(); cycle = _ppu->GetCurrentCycle(); @@ -221,7 +232,6 @@ void NesDebugger::ProcessPpuCycle(uint16_t& scanline, uint16_t& cycle) } if(cycle == 0 && scanline == _step->BreakScanline) { - _step->BreakScanline = -1; _debugger->SleepUntilResume(BreakSource::PpuStep); } } @@ -269,3 +279,8 @@ BaseState& NesDebugger::GetState() { return _cpu->GetState(); } + +ITraceLogger* NesDebugger::GetTraceLogger() +{ + return _traceLogger.get(); +} diff --git a/Core/NES/Debugger/NesDebugger.h b/Core/NES/Debugger/NesDebugger.h index a5785a9b..ed649c91 100644 --- a/Core/NES/Debugger/NesDebugger.h +++ b/Core/NES/Debugger/NesDebugger.h @@ -5,7 +5,6 @@ class Disassembler; class Debugger; -class TraceLogger; class CallstackManager; class MemoryAccessCounter; class MemoryManager; @@ -15,6 +14,7 @@ class ScriptManager; class BreakpointManager; class IAssembler; class IEventManager; +class NesTraceLogger; class Emulator; class NesCpu; @@ -28,7 +28,6 @@ class NesDebugger final : public IDebugger Debugger* _debugger; Emulator* _emu; Disassembler* _disassembler; - TraceLogger* _traceLogger; MemoryAccessCounter* _memoryAccessCounter; EmuSettings* _settings; @@ -42,6 +41,7 @@ class NesDebugger final : public IDebugger shared_ptr _assembler; shared_ptr _callstackManager; unique_ptr _breakpointManager; + unique_ptr _traceLogger; unique_ptr _step; bool _enableBreakOnUninitRead = false; @@ -55,15 +55,16 @@ public: void Reset() override; - void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override; - void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override; + void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type); + void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type); void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) override; - void ProcessPpuCycle(uint16_t& scanline, uint16_t& cycle) override; + void ProcessPpuCycle(int16_t& scanline, uint16_t& cycle); void Run() override; void Step(int32_t stepCount, StepType type) override; BreakpointManager* GetBreakpointManager() override; + ITraceLogger* GetTraceLogger() override; shared_ptr GetCallstackManager() override; shared_ptr GetAssembler() override; shared_ptr GetEventManager() override; diff --git a/Core/NES/Debugger/NesTraceLogger.cpp b/Core/NES/Debugger/NesTraceLogger.cpp new file mode 100644 index 00000000..4c0423b0 --- /dev/null +++ b/Core/NES/Debugger/NesTraceLogger.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "NES/Debugger/NesTraceLogger.h" +#include "NES/NesPpu.h" +#include "NES/NesTypes.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Debugger.h" +#include "Debugger/DebugTypes.h" +#include "Utilities/HexUtilities.h" + +NesTraceLogger::NesTraceLogger(Debugger* debugger, BaseNesPpu* ppu) : BaseTraceLogger(debugger, CpuType::Nes) +{ + _ppu = ppu; + + TraceLoggerOptions options = {}; + SetOptions(options); +} + +RowDataType NesTraceLogger::GetFormatTagType(string& tag) +{ + if(tag == "A") { + return RowDataType::A; + } else if(tag == "X") { + return RowDataType::X; + } else if(tag == "Y") { + return RowDataType::Y; + } else if(tag == "P") { + return RowDataType::PS; + } else if(tag == "SP") { + return RowDataType::SP; + } else { + return RowDataType::Text; + } +} + +void NesTraceLogger::GetTraceRow(string &output, NesCpuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo) +{ + constexpr char activeStatusLetters[8] = { 'N', 'V', '-', '-', 'D', 'I', 'Z', 'C' }; + constexpr char inactiveStatusLetters[8] = { 'n', 'v', '-', '-', 'd', 'i', 'z', 'c' }; + + for(RowPart& rowPart : _rowParts) { + switch(rowPart.DataType) { + case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break; + case RowDataType::X: WriteIntValue(output, cpuState.X, rowPart); break; + case RowDataType::Y: WriteIntValue(output, cpuState.Y, rowPart); break; + case RowDataType::SP: WriteIntValue(output, cpuState.SP, rowPart); break; + case RowDataType::PS: GetStatusFlag(activeStatusLetters, inactiveStatusLetters, output, cpuState.PS, rowPart); break; + default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break; + } + } +} + +void NesTraceLogger::LogPpuState() +{ + _ppuState[_currentPos] = { + _ppu->GetCurrentCycle(), + _ppu->GetCurrentCycle(), + _ppu->GetCurrentScanline(), + _ppu->GetFrameCount() + }; +} \ No newline at end of file diff --git a/Core/NES/Debugger/NesTraceLogger.h b/Core/NES/Debugger/NesTraceLogger.h new file mode 100644 index 00000000..c093cc5e --- /dev/null +++ b/Core/NES/Debugger/NesTraceLogger.h @@ -0,0 +1,27 @@ +#pragma once +#include "stdafx.h" +#include "Debugger/BaseTraceLogger.h" +#include "NES/NesTypes.h" + +class DisassemblyInfo; +class Debugger; +class BaseNesPpu; + +class NesTraceLogger : public BaseTraceLogger +{ +private: + BaseNesPpu* _ppu = nullptr; + +protected: + RowDataType GetFormatTagType(string& tag) override; + +public: + NesTraceLogger(Debugger* debugger, BaseNesPpu* ppu); + + void GetTraceRow(string& output, NesCpuState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo); + void LogPpuState(); + + __forceinline uint32_t GetProgramCounter(NesCpuState& state) { return state.PC; } + __forceinline uint64_t GetCycleCount(NesCpuState& state) { return state.CycleCount; } + __forceinline uint8_t GetStackPointer(NesCpuState& state) { return (uint8_t)state.SP; } +}; diff --git a/Core/SNES/BaseCartridge.cpp b/Core/SNES/BaseCartridge.cpp index 99e21152..efe5ce1e 100644 --- a/Core/SNES/BaseCartridge.cpp +++ b/Core/SNES/BaseCartridge.cpp @@ -630,6 +630,7 @@ bool BaseCartridge::LoadGameboy(VirtualFile& romFile) if(_coprocessorType == CoprocessorType::SGB) { _gameboy.reset(new Gameboy(_emu, true)); if(_gameboy->LoadRom(romFile) == LoadRomResult::Success) { + _emu->RegisterMemory(SnesMemoryType::PrgRom, _prgRom, _prgRomSize); return _gameboy->IsSgb(); } } diff --git a/Core/SNES/Console.cpp b/Core/SNES/Console.cpp index b889e3ad..605194f8 100644 --- a/Core/SNES/Console.cpp +++ b/Core/SNES/Console.cpp @@ -222,7 +222,19 @@ PpuFrameInfo Console::GetPpuFrame() vector Console::GetCpuTypes() { - return { CpuType::Cpu, CpuType::Spc }; + vector cpuTypes = { CpuType::Cpu, CpuType::Spc }; + if(_cart->GetGsu()) { + cpuTypes.push_back(CpuType::Gsu); + } else if(_cart->GetDsp()) { + cpuTypes.push_back(CpuType::NecDsp); + } else if(_cart->GetCx4()) { + cpuTypes.push_back(CpuType::Cx4); + } else if(_cart->GetGameboy()) { + cpuTypes.push_back(CpuType::Gameboy); + } else if(_cart->GetSa1()) { + cpuTypes.push_back(CpuType::Sa1); + } + return cpuTypes; } void Console::SaveBattery() diff --git a/Core/SNES/Debugger/CpuDebugger.cpp b/Core/SNES/Debugger/CpuDebugger.cpp index 3ce342f8..d04e4e14 100644 --- a/Core/SNES/Debugger/CpuDebugger.cpp +++ b/Core/SNES/Debugger/CpuDebugger.cpp @@ -10,10 +10,10 @@ #include "SNES/Debugger/SnesAssembler.h" #include "SNES/Debugger/CpuDebugger.h" #include "SNES/Debugger/SnesEventManager.h" +#include "SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h" #include "Debugger/DebugTypes.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/Disassembler.h" -#include "Debugger/TraceLogger.h" #include "Debugger/CallstackManager.h" #include "Debugger/BreakpointManager.h" #include "Debugger/CodeDataLogger.h" @@ -35,7 +35,6 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType) _debugger = debugger; Console* console = (Console*)debugger->GetConsole(); - _traceLogger = debugger->GetTraceLogger().get(); _disassembler = debugger->GetDisassembler().get(); _memoryAccessCounter = debugger->GetMemoryAccessCounter().get(); _cpu = console->GetCpu().get(); @@ -45,7 +44,14 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType) _cart = console->GetCartridge().get(); _spc = console->GetSpc().get(); _ppu = console->GetPpu().get(); + _traceLogger.reset(new SnesCpuTraceLogger(debugger, cpuType, _ppu, _memoryManager)); + if(_cpuType == CpuType::Cpu) { + _memoryMappings = _memoryManager->GetMemoryMappings(); + } else { + _memoryMappings = _sa1->GetMemoryMappings(); + } + if(cpuType == CpuType::Sa1) { _codeDataLogger = _debugger->GetCodeDataLogger(CpuType::Cpu); } else { @@ -62,6 +68,14 @@ CpuDebugger::CpuDebugger(Debugger* debugger, CpuType cpuType) //Enable breaking on uninit reads when debugger is opened at power on _enableBreakOnUninitRead = true; } + + _debuggerEnabledFlag = _cpuType == CpuType::Cpu ? DebuggerFlags::CpuDebuggerEnabled : DebuggerFlags::Sa1DebuggerEnabled; +} + +void CpuDebugger::Init() +{ + _spcTraceLogger = _debugger->GetTraceLogger(CpuType::Spc); + _dspTraceLogger = _debugger->GetTraceLogger(CpuType::NecDsp); } void CpuDebugger::Reset() @@ -73,29 +87,33 @@ void CpuDebugger::Reset() void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) { - AddressInfo addressInfo = GetMemoryMappings().GetAbsoluteAddress(addr); + AddressInfo addressInfo = _memoryMappings->GetAbsoluteAddress(addr); MemoryOperationInfo operation = { addr, value, type }; - CpuState state = GetCpuState(); + CpuState& state = GetCpuState(); BreakSource breakSource = BreakSource::Unspecified; + if(IsRegister(addr)) { + _eventManager->AddEvent(DebugEventType::Register, operation); + } + if(type == MemoryOperationType::ExecOpCode) { - bool needDisassemble = _traceLogger->IsCpuLogged(_cpuType) || _settings->CheckDebuggerFlag(_cpuType == CpuType::Cpu ? DebuggerFlags::CpuDebuggerEnabled : DebuggerFlags::Sa1DebuggerEnabled); if(addressInfo.Address >= 0) { + uint8_t cpuFlags = state.PS & (ProcFlags::IndexMode8 | ProcFlags::MemoryMode8); if(addressInfo.Type == SnesMemoryType::PrgRom) { - uint8_t flags = CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)); + uint8_t flags = CdlFlags::Code | cpuFlags; if(_prevOpCode == 0x20 || _prevOpCode == 0x22 || _prevOpCode == 0xFC) { flags |= CdlFlags::SubEntryPoint; } _codeDataLogger->SetFlags(addressInfo.Address, flags); } - if(needDisassemble) { - _disassembler->BuildCache(addressInfo, state.PS & (ProcFlags::IndexMode8 | ProcFlags::MemoryMode8), _cpuType); + if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(_debuggerEnabledFlag)) { + _disassembler->BuildCache(addressInfo, cpuFlags, _cpuType); } } - if(_traceLogger->IsCpuLogged(_cpuType)) { + if(_traceLogger->IsEnabled()) { DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, state.PS, _cpuType); - _traceLogger->Log(_cpuType, state, disInfo); + _traceLogger->Log(state, disInfo, operation); } uint32_t pc = (state.K << 16) | state.PC; @@ -103,8 +121,8 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType //JSR, JSL uint8_t opSize = DisassemblyInfo::GetOpSize(_prevOpCode, state.PS, _cpuType); uint32_t returnPc = (_prevProgramCounter & 0xFF0000) | (((_prevProgramCounter & 0xFFFF) + opSize) & 0xFFFF); - AddressInfo srcAddress = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter); - AddressInfo retAddress = GetMemoryMappings().GetAbsoluteAddress(returnPc); + AddressInfo srcAddress = _memoryMappings->GetAbsoluteAddress(_prevProgramCounter); + AddressInfo retAddress = _memoryMappings->GetAbsoluteAddress(returnPc); _callstackManager->Push(srcAddress, _prevProgramCounter, addressInfo, pc, retAddress, returnPc, StackFrameFlags::None); } else if(_prevOpCode == 0x60 || _prevOpCode == 0x6B || _prevOpCode == 0x40) { //RTS, RTL, RTI @@ -146,11 +164,17 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8))); } + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } _memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock()); } else { if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8))); } + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } if(_memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock())) { //Memory access was a read on an uninitialized memory address @@ -167,16 +191,12 @@ void CpuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType } } - if(IsRegister(addr)) { - _eventManager->AddEvent(DebugEventType::Register, operation); - } - _debugger->ProcessBreakConditions(_step->StepCount == 0, _breakpointManager.get(), operation, addressInfo, breakSource); } void CpuDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) { - AddressInfo addressInfo = GetMemoryMappings().GetAbsoluteAddress(addr); + AddressInfo addressInfo = _memoryMappings->GetAbsoluteAddress(addr); MemoryOperationInfo operation = { addr, value, type }; if(addressInfo.Address >= 0 && (addressInfo.Type == SnesMemoryType::WorkRam || addressInfo.Type == SnesMemoryType::SaveRam)) { _disassembler->InvalidateCache(addressInfo, _cpuType); @@ -186,6 +206,10 @@ void CpuDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType _eventManager->AddEvent(DebugEventType::Register, operation); } + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } + _memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock()); _debugger->ProcessBreakConditions(false, _breakpointManager.get(), operation, addressInfo); @@ -225,25 +249,20 @@ void CpuDebugger::Step(int32_t stepCount, StepType type) void CpuDebugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) { - AddressInfo src = GetMemoryMappings().GetAbsoluteAddress(_prevProgramCounter); - AddressInfo ret = GetMemoryMappings().GetAbsoluteAddress(originalPc); - AddressInfo dest = GetMemoryMappings().GetAbsoluteAddress(currentPc); + AddressInfo src = _memoryMappings->GetAbsoluteAddress(_prevProgramCounter); + AddressInfo ret = _memoryMappings->GetAbsoluteAddress(originalPc); + AddressInfo dest = _memoryMappings->GetAbsoluteAddress(currentPc); _callstackManager->Push(src, _prevProgramCounter, dest, currentPc, ret, originalPc, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq); _eventManager->AddEvent(forNmi ? DebugEventType::Nmi : DebugEventType::Irq); } -void CpuDebugger::ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle) +void CpuDebugger::ProcessPpuCycle(int16_t &scanline, uint16_t &cycle) { - if(_cpuType == CpuType::Cpu) { - scanline = _ppu->GetScanline(); - cycle = _memoryManager->GetHClock(); - - //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(); - } + //Catch up SPC/DSP as needed (if we're tracing or debugging those particular CPUs) + if(_spcTraceLogger->IsEnabled()) { + _spc->Run(); + } else if(_dspTraceLogger && _dspTraceLogger->IsEnabled()) { + _cart->RunCoprocessors(); } if(_step->PpuStepCount > 0) { @@ -253,18 +272,11 @@ void CpuDebugger::ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle) } } - if(cycle == 0 && scanline == _step->BreakScanline) { - _step->BreakScanline = -1; - _debugger->SleepUntilResume(BreakSource::PpuStep); - } -} + scanline = _ppu->GetScanline(); + cycle = _memoryManager->GetHClock(); -MemoryMappings& CpuDebugger::GetMemoryMappings() -{ - if(_cpuType == CpuType::Cpu) { - return *_memoryManager->GetMemoryMappings(); - } else { - return *_sa1->GetMemoryMappings(); + if(cycle == 0 && scanline == _step->BreakScanline) { + _debugger->SleepUntilResume(BreakSource::PpuStep); } } @@ -287,6 +299,11 @@ shared_ptr CpuDebugger::GetCallstackManager() return _callstackManager; } +ITraceLogger* CpuDebugger::GetTraceLogger() +{ + return _traceLogger.get(); +} + BreakpointManager* CpuDebugger::GetBreakpointManager() { return _breakpointManager.get(); diff --git a/Core/SNES/Debugger/CpuDebugger.h b/Core/SNES/Debugger/CpuDebugger.h index 7c6d8abc..8c18dacc 100644 --- a/Core/SNES/Debugger/CpuDebugger.h +++ b/Core/SNES/Debugger/CpuDebugger.h @@ -6,7 +6,7 @@ class Disassembler; class Debugger; -class TraceLogger; +class SnesCpuTraceLogger; class Cpu; class CallstackManager; class MemoryAccessCounter; @@ -28,7 +28,6 @@ class CpuDebugger final : public IDebugger { Debugger* _debugger; Disassembler* _disassembler; - TraceLogger* _traceLogger; MemoryAccessCounter* _memoryAccessCounter; MemoryManager* _memoryManager; EmuSettings* _settings; @@ -37,37 +36,43 @@ class CpuDebugger final : public IDebugger BaseCartridge* _cart; Spc* _spc; Ppu* _ppu; + MemoryMappings* _memoryMappings; shared_ptr _codeDataLogger; - shared_ptr _eventManager; shared_ptr _assembler; shared_ptr _callstackManager; unique_ptr _breakpointManager; unique_ptr _step; + unique_ptr _traceLogger; + + ITraceLogger* _spcTraceLogger = nullptr; + ITraceLogger* _dspTraceLogger = nullptr; + DebuggerFlags _debuggerEnabledFlag = DebuggerFlags::CpuDebuggerEnabled; CpuType _cpuType; bool _enableBreakOnUninitRead = false; uint8_t _prevOpCode = 0xFF; uint32_t _prevProgramCounter = 0; - MemoryMappings& GetMemoryMappings(); CpuState& GetCpuState(); bool IsRegister(uint32_t addr); public: CpuDebugger(Debugger* debugger, CpuType cpuType); + void Init() override; void Reset() override; - void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override; - void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override; + void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type); + void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type); void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) override; - void ProcessPpuCycle(uint16_t &scanline, uint16_t &cycle) override; + void ProcessPpuCycle(int16_t &scanline, uint16_t &cycle); void Run() override; void Step(int32_t stepCount, StepType type) override; + ITraceLogger* GetTraceLogger() override; BreakpointManager* GetBreakpointManager() override; shared_ptr GetCallstackManager() override; shared_ptr GetAssembler() override; diff --git a/Core/SNES/Debugger/Cx4Debugger.cpp b/Core/SNES/Debugger/Cx4Debugger.cpp index 64744489..aa536014 100644 --- a/Core/SNES/Debugger/Cx4Debugger.cpp +++ b/Core/SNES/Debugger/Cx4Debugger.cpp @@ -2,7 +2,6 @@ #include "Cx4Debugger.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/Disassembler.h" -#include "Debugger/TraceLogger.h" #include "Debugger/CallstackManager.h" #include "Debugger/BreakpointManager.h" #include "Debugger/Debugger.h" @@ -12,6 +11,7 @@ #include "SNES/BaseCartridge.h" #include "SNES/MemoryManager.h" #include "SNES/Console.h" +#include "SNES/Debugger/TraceLogger/Cx4TraceLogger.h" #include "SNES/Coprocessors/CX4/Cx4.h" #include "Shared/Emulator.h" #include "Shared/EmuSettings.h" @@ -20,14 +20,17 @@ Cx4Debugger::Cx4Debugger(Debugger* debugger) { + Console* console = (Console*)debugger->GetConsole(); + _debugger = debugger; _codeDataLogger = debugger->GetCodeDataLogger(CpuType::Cpu).get(); - _traceLogger = debugger->GetTraceLogger().get(); _disassembler = debugger->GetDisassembler().get(); _memoryAccessCounter = debugger->GetMemoryAccessCounter().get(); - _cx4 = ((Console*)debugger->GetConsole())->GetCartridge()->GetCx4(); - _memoryManager = ((Console*)debugger->GetConsole())->GetMemoryManager().get(); + _cx4 = console->GetCartridge()->GetCx4(); + _memoryManager = console->GetMemoryManager().get(); _settings = debugger->GetEmulator()->GetSettings(); + + _traceLogger.reset(new Cx4TraceLogger(debugger, console->GetPpu().get(), _memoryManager)); _breakpointManager.reset(new BreakpointManager(debugger, CpuType::Cx4)); _step.reset(new StepRequest()); @@ -39,7 +42,7 @@ void Cx4Debugger::Reset() void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) { - Cx4State state = _cx4->GetState(); + Cx4State& state = _cx4->GetState(); addr = (state.Cache.Address[state.Cache.Page] + (state.PC * 2)) & 0xFFFFFF; AddressInfo addressInfo = _cx4->GetMemoryMappings()->GetAbsoluteAddress(addr); @@ -52,12 +55,12 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType _codeDataLogger->SetFlags(addressInfo.Address + 1, CdlFlags::Code | CdlFlags::Cx4); } - if(_traceLogger->IsCpuLogged(CpuType::Cx4) || _settings->CheckDebuggerFlag(DebuggerFlags::Cx4DebuggerEnabled)) { + if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::Cx4DebuggerEnabled)) { _disassembler->BuildCache(addressInfo, 0, CpuType::Cx4); - if(_traceLogger->IsCpuLogged(CpuType::Cx4)) { + if(_traceLogger->IsEnabled()) { DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Cx4); - _traceLogger->Log(CpuType::Cx4, state, disInfo); + _traceLogger->Log(state, disInfo, operation); } } @@ -73,6 +76,9 @@ void Cx4Debugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType if(addressInfo.Type == SnesMemoryType::PrgRom) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | CdlFlags::Cx4); } + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } _memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock()); } @@ -85,6 +91,9 @@ void Cx4Debugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType MemoryOperationInfo operation { (uint32_t)addr, value, type }; _debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo); _memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock()); + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } } void Cx4Debugger::Run() @@ -141,3 +150,8 @@ BaseState& Cx4Debugger::GetState() { return _cx4->GetState(); } + +ITraceLogger* Cx4Debugger::GetTraceLogger() +{ + return _traceLogger.get(); +} diff --git a/Core/SNES/Debugger/Cx4Debugger.h b/Core/SNES/Debugger/Cx4Debugger.h index 3a5fe35e..f339e958 100644 --- a/Core/SNES/Debugger/Cx4Debugger.h +++ b/Core/SNES/Debugger/Cx4Debugger.h @@ -5,7 +5,7 @@ class Disassembler; class Debugger; -class TraceLogger; +class Cx4TraceLogger; class CodeDataLogger; class Cx4; class CallstackManager; @@ -20,7 +20,6 @@ class Cx4Debugger final : public IDebugger { Debugger* _debugger; Disassembler* _disassembler; - TraceLogger* _traceLogger; CodeDataLogger* _codeDataLogger; MemoryAccessCounter* _memoryAccessCounter; MemoryManager* _memoryManager; @@ -29,6 +28,7 @@ class Cx4Debugger final : public IDebugger unique_ptr _breakpointManager; unique_ptr _step; + unique_ptr _traceLogger; uint32_t _prevProgramCounter = 0; @@ -37,8 +37,8 @@ public: void Reset() override; - void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override; - void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override; + void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type); + void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type); void Run() override; void Step(int32_t stepCount, StepType type) override; @@ -48,6 +48,7 @@ public: shared_ptr GetAssembler() override; shared_ptr GetEventManager() override; shared_ptr GetCodeDataLogger() override; + ITraceLogger* GetTraceLogger() override; BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/SNES/Debugger/GsuDebugger.cpp b/Core/SNES/Debugger/GsuDebugger.cpp index daa98f60..bfbe12c0 100644 --- a/Core/SNES/Debugger/GsuDebugger.cpp +++ b/Core/SNES/Debugger/GsuDebugger.cpp @@ -4,9 +4,9 @@ #include "SNES/Console.h" #include "SNES/BaseCartridge.h" #include "SNES/Coprocessors/GSU/Gsu.h" +#include "SNES/Debugger/TraceLogger/GsuTraceLogger.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/Disassembler.h" -#include "Debugger/TraceLogger.h" #include "Debugger/CallstackManager.h" #include "Debugger/BreakpointManager.h" #include "Debugger/ExpressionEvaluator.h" @@ -19,14 +19,17 @@ GsuDebugger::GsuDebugger(Debugger* debugger) { + Console* console = (Console*)debugger->GetConsole(); + _debugger = debugger; _codeDataLogger = debugger->GetCodeDataLogger(CpuType::Cpu).get(); - _traceLogger = debugger->GetTraceLogger().get(); _disassembler = debugger->GetDisassembler().get(); _memoryAccessCounter = debugger->GetMemoryAccessCounter().get(); - _gsu = ((Console*)debugger->GetConsole())->GetCartridge()->GetGsu(); - _memoryManager = ((Console*)debugger->GetConsole())->GetMemoryManager().get(); + _gsu = console->GetCartridge()->GetGsu(); + _memoryManager = console->GetMemoryManager().get(); _settings = debugger->GetEmulator()->GetSettings(); + + _traceLogger.reset(new GsuTraceLogger(debugger, console->GetPpu().get(), _memoryManager)); _breakpointManager.reset(new BreakpointManager(debugger, CpuType::Gsu)); _step.reset(new StepRequest()); @@ -52,15 +55,15 @@ void GsuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | CdlFlags::Gsu); } - if(_traceLogger->IsCpuLogged(CpuType::Gsu) || _settings->CheckDebuggerFlag(DebuggerFlags::GsuDebuggerEnabled)) { + if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::GsuDebuggerEnabled)) { GsuState gsuState = _gsu->GetState(); _disassembler->BuildCache(addressInfo, gsuState.SFR.GetFlagsHigh() & 0x13, CpuType::Gsu); - if(_traceLogger->IsCpuLogged(CpuType::Gsu)) { + if(_traceLogger->IsEnabled()) { gsuState.R[15] = addr; DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Gsu); - _traceLogger->Log(CpuType::Gsu, gsuState, disInfo); + _traceLogger->Log(gsuState, disInfo, operation); } } @@ -75,6 +78,9 @@ void GsuDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType if(addressInfo.Type == SnesMemoryType::PrgRom) { _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | CdlFlags::Gsu); } + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } _memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock()); } @@ -87,6 +93,10 @@ void GsuDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType MemoryOperationInfo operation { addr, value, type }; _debugger->ProcessBreakConditions(false, GetBreakpointManager(), operation, addressInfo); + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } + _disassembler->InvalidateCache(addressInfo, CpuType::Gsu); _memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock()); } @@ -142,3 +152,8 @@ BaseState& GsuDebugger::GetState() { return _gsu->GetState(); } + +ITraceLogger* GsuDebugger::GetTraceLogger() +{ + return _traceLogger.get(); +} diff --git a/Core/SNES/Debugger/GsuDebugger.h b/Core/SNES/Debugger/GsuDebugger.h index bd7907f1..63863a55 100644 --- a/Core/SNES/Debugger/GsuDebugger.h +++ b/Core/SNES/Debugger/GsuDebugger.h @@ -5,13 +5,13 @@ class Disassembler; class Debugger; -class TraceLogger; class CodeDataLogger; class Gsu; class MemoryAccessCounter; class MemoryManager; class BreakpointManager; class EmuSettings; +class GsuTraceLogger; enum class MemoryOperationType; @@ -19,7 +19,6 @@ class GsuDebugger final : public IDebugger { Debugger* _debugger; Disassembler* _disassembler; - TraceLogger* _traceLogger; CodeDataLogger* _codeDataLogger; MemoryAccessCounter* _memoryAccessCounter; MemoryManager* _memoryManager; @@ -28,6 +27,7 @@ class GsuDebugger final : public IDebugger unique_ptr _breakpointManager; unique_ptr _step; + unique_ptr _traceLogger; uint8_t _prevOpCode = 0xFF; uint32_t _prevProgramCounter = 0; @@ -37,8 +37,8 @@ public: void Reset() override; - void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override; - void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override; + void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type); + void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type); void Run() override; void Step(int32_t stepCount, StepType type) override; @@ -48,6 +48,7 @@ public: shared_ptr GetAssembler() override; shared_ptr GetEventManager() override; shared_ptr GetCodeDataLogger() override; + ITraceLogger* GetTraceLogger() override; BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/SNES/Debugger/NecDspDebugger.cpp b/Core/SNES/Debugger/NecDspDebugger.cpp index 59c2251f..6d28eab6 100644 --- a/Core/SNES/Debugger/NecDspDebugger.cpp +++ b/Core/SNES/Debugger/NecDspDebugger.cpp @@ -4,9 +4,9 @@ #include "SNES/Console.h" #include "SNES/Coprocessors/DSP/NecDsp.h" #include "SNES/Debugger/NecDspDebugger.h" +#include "SNES/Debugger/TraceLogger/NecDspTraceLogger.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/Disassembler.h" -#include "Debugger/TraceLogger.h" #include "Debugger/CallstackManager.h" #include "Debugger/BreakpointManager.h" #include "Debugger/Debugger.h" @@ -18,11 +18,14 @@ NecDspDebugger::NecDspDebugger(Debugger* debugger) { + Console* console = (Console*)debugger->GetConsole(); + _debugger = debugger; - _traceLogger = debugger->GetTraceLogger().get(); _disassembler = debugger->GetDisassembler().get(); - _dsp = ((Console*)debugger->GetConsole())->GetCartridge()->GetDsp(); + _dsp = console->GetCartridge()->GetDsp(); _settings = debugger->GetEmulator()->GetSettings(); + + _traceLogger.reset(new NecDspTraceLogger(debugger, console->GetPpu().get(), console->GetMemoryManager().get())); _breakpointManager.reset(new BreakpointManager(debugger, CpuType::NecDsp)); _step.reset(new StepRequest()); @@ -38,13 +41,12 @@ void NecDspDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationTy MemoryOperationInfo operation { (uint32_t)addr, value, type }; if(type == MemoryOperationType::ExecOpCode) { - if(_traceLogger->IsCpuLogged(CpuType::NecDsp) || _settings->CheckDebuggerFlag(DebuggerFlags::NecDspDebuggerEnabled)) { + if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::NecDspDebuggerEnabled)) { _disassembler->BuildCache(addressInfo, 0, CpuType::NecDsp); - if(_traceLogger->IsCpuLogged(CpuType::NecDsp)) { + if(_traceLogger->IsEnabled()) { DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::NecDsp); - NecDspState state = _dsp->GetState(); - _traceLogger->Log(CpuType::NecDsp, state, disInfo); + _traceLogger->Log(_dsp->GetState(), disInfo, operation); } } @@ -63,6 +65,10 @@ void NecDspDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationT AddressInfo addressInfo { (int32_t)addr, SnesMemoryType::DspDataRam }; //Writes never affect the DSP ROM MemoryOperationInfo operation { addr, value, type }; _debugger->ProcessBreakConditions(false, GetBreakpointManager(), operation, addressInfo); + + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } } void NecDspDebugger::Run() @@ -119,3 +125,8 @@ BaseState& NecDspDebugger::GetState() { return _dsp->GetState(); } + +ITraceLogger* NecDspDebugger::GetTraceLogger() +{ + return _traceLogger.get(); +} diff --git a/Core/SNES/Debugger/NecDspDebugger.h b/Core/SNES/Debugger/NecDspDebugger.h index f6eb2be9..c923a3e7 100644 --- a/Core/SNES/Debugger/NecDspDebugger.h +++ b/Core/SNES/Debugger/NecDspDebugger.h @@ -5,7 +5,7 @@ class Disassembler; class Debugger; -class TraceLogger; +class NecDspTraceLogger; class NecDsp; class CallstackManager; class MemoryAccessCounter; @@ -19,12 +19,12 @@ class NecDspDebugger final : public IDebugger { Debugger* _debugger; Disassembler* _disassembler; - TraceLogger* _traceLogger; NecDsp* _dsp; EmuSettings* _settings; unique_ptr _breakpointManager; unique_ptr _step; + unique_ptr _traceLogger; uint32_t _prevProgramCounter = 0; @@ -33,8 +33,8 @@ public: void Reset() override; - void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override; - void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override; + void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type); + void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type); void Run() override; void Step(int32_t stepCount, StepType type) override; @@ -44,7 +44,7 @@ public: shared_ptr GetAssembler() override; shared_ptr GetEventManager() override; shared_ptr GetCodeDataLogger() override; + ITraceLogger* GetTraceLogger() override; - // Inherited via IDebugger - virtual BaseState& GetState() override; + BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/SNES/Debugger/SpcDebugger.cpp b/Core/SNES/Debugger/SpcDebugger.cpp index 954d43b7..e94d3b65 100644 --- a/Core/SNES/Debugger/SpcDebugger.cpp +++ b/Core/SNES/Debugger/SpcDebugger.cpp @@ -3,9 +3,9 @@ #include "SNES/MemoryManager.h" #include "SNES/Console.h" #include "SNES/Debugger/SpcDebugger.h" +#include "SNES/Debugger/TraceLogger/SpcTraceLogger.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/Disassembler.h" -#include "Debugger/TraceLogger.h" #include "Debugger/CallstackManager.h" #include "Debugger/BreakpointManager.h" #include "Debugger/Debugger.h" @@ -18,13 +18,16 @@ SpcDebugger::SpcDebugger(Debugger* debugger) { _debugger = debugger; - _traceLogger = debugger->GetTraceLogger().get(); _disassembler = debugger->GetDisassembler().get(); _memoryAccessCounter = debugger->GetMemoryAccessCounter().get(); - _spc = ((Console*)debugger->GetConsole())->GetSpc().get(); - _memoryManager = ((Console*)debugger->GetConsole())->GetMemoryManager().get(); + + Console* console = (Console*)debugger->GetConsole(); + _spc = console->GetSpc().get(); + _memoryManager = console->GetMemoryManager().get(); _settings = debugger->GetEmulator()->GetSettings(); + _traceLogger.reset(new SpcTraceLogger(debugger, console->GetPpu().get(), console->GetMemoryManager().get())); + _callstackManager.reset(new CallstackManager(debugger)); _breakpointManager.reset(new BreakpointManager(debugger, CpuType::Spc)); _step.reset(new StepRequest()); @@ -48,14 +51,14 @@ void SpcDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType BreakSource breakSource = BreakSource::Unspecified; if(type == MemoryOperationType::ExecOpCode) { - SpcState spcState = _spc->GetState(); + SpcState& spcState = _spc->GetState(); - if(_traceLogger->IsCpuLogged(CpuType::Spc) || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled)) { + if(_traceLogger->IsEnabled() || _settings->CheckDebuggerFlag(DebuggerFlags::SpcDebuggerEnabled)) { _disassembler->BuildCache(addressInfo, 0, CpuType::Spc); - if(_traceLogger->IsCpuLogged(CpuType::Spc)) { + if(_traceLogger->IsEnabled()) { DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, addr, 0, CpuType::Spc); - _traceLogger->Log(CpuType::Spc, spcState, disInfo); + _traceLogger->Log(spcState, disInfo, operation); } } @@ -96,8 +99,14 @@ void SpcDebugger::ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType _memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock()); } else if(type == MemoryOperationType::ExecOperand) { _memoryAccessCounter->ProcessMemoryExec(addressInfo, _memoryManager->GetMasterClock()); + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } } else { _memoryAccessCounter->ProcessMemoryRead(addressInfo, _memoryManager->GetMasterClock()); + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } } _debugger->ProcessBreakConditions(_step->StepCount == 0, GetBreakpointManager(), operation, addressInfo, breakSource); @@ -112,6 +121,10 @@ void SpcDebugger::ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType _disassembler->InvalidateCache(addressInfo, CpuType::Spc); _memoryAccessCounter->ProcessMemoryWrite(addressInfo, _memoryManager->GetMasterClock()); + + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation); + } } void SpcDebugger::Run() @@ -173,3 +186,8 @@ BaseState& SpcDebugger::GetState() { return _spc->GetState(); } + +ITraceLogger* SpcDebugger::GetTraceLogger() +{ + return _traceLogger.get(); +} diff --git a/Core/SNES/Debugger/SpcDebugger.h b/Core/SNES/Debugger/SpcDebugger.h index 8bc93b1d..a4eca051 100644 --- a/Core/SNES/Debugger/SpcDebugger.h +++ b/Core/SNES/Debugger/SpcDebugger.h @@ -5,7 +5,7 @@ class Disassembler; class Debugger; -class TraceLogger; +class SpcTraceLogger; class Spc; class CallstackManager; class MemoryAccessCounter; @@ -19,7 +19,6 @@ class SpcDebugger final : public IDebugger { Debugger* _debugger; Disassembler* _disassembler; - TraceLogger* _traceLogger; MemoryAccessCounter* _memoryAccessCounter; MemoryManager* _memoryManager; Spc* _spc; @@ -27,6 +26,7 @@ class SpcDebugger final : public IDebugger shared_ptr _callstackManager; unique_ptr _breakpointManager; + unique_ptr _traceLogger; unique_ptr _step; uint8_t _prevOpCode = 0xFF; @@ -37,8 +37,8 @@ public: void Reset() override; - void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type) override; - void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type) override; + void ProcessRead(uint32_t addr, uint8_t value, MemoryOperationType type); + void ProcessWrite(uint32_t addr, uint8_t value, MemoryOperationType type); void Run() override; void Step(int32_t stepCount, StepType type) override; @@ -48,6 +48,7 @@ public: shared_ptr GetAssembler() override; shared_ptr GetEventManager() override; shared_ptr GetCodeDataLogger() override; + ITraceLogger* GetTraceLogger() override; BaseState& GetState() override; }; \ No newline at end of file diff --git a/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.cpp b/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.cpp new file mode 100644 index 00000000..ae0d2e6d --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "SNES/Debugger/TraceLogger/Cx4TraceLogger.h" +#include "SNES/Ppu.h" +#include "SNES/MemoryManager.h" +#include "SNES/Coprocessors/GSU/GsuTypes.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Debugger.h" +#include "Debugger/DebugTypes.h" +#include "Utilities/HexUtilities.h" + +Cx4TraceLogger::Cx4TraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, CpuType::Gsu) +{ + _ppu = ppu; + _memoryManager = memoryManager; + + TraceLoggerOptions options = {}; + SetOptions(options); +} + +RowDataType Cx4TraceLogger::GetFormatTagType(string& tag) +{ + if(tag == "R0") { + return RowDataType::R0; + } else if(tag == "R1") { + return RowDataType::R1; + } else if(tag == "R2") { + return RowDataType::R2; + } else if(tag == "R3") { + return RowDataType::R3; + } else if(tag == "R4") { + return RowDataType::R4; + } else if(tag == "R5") { + return RowDataType::R5; + } else if(tag == "R6") { + return RowDataType::R6; + } else if(tag == "R7") { + return RowDataType::R7; + } else if(tag == "R8") { + return RowDataType::R8; + } else if(tag == "R9") { + return RowDataType::R9; + } else if(tag == "R10") { + return RowDataType::R10; + } else if(tag == "R11") { + return RowDataType::R11; + } else if(tag == "R12") { + return RowDataType::R12; + } else if(tag == "R13") { + return RowDataType::R13; + } else if(tag == "R14") { + return RowDataType::R14; + } else if(tag == "R15") { + return RowDataType::R15; + } else if(tag == "MAR") { + return RowDataType::MAR; + } else if(tag == "MDR") { + return RowDataType::MDR; + } else if(tag == "DPR") { + return RowDataType::DPR; + } else if(tag == "ML") { + return RowDataType::ML; + } else if(tag == "MH") { + return RowDataType::MH; + } else if(tag == "PB") { + return RowDataType::PB; + } else if(tag == "P") { + return RowDataType::P; + } else if(tag == "PS") { + return RowDataType::PS; + } else if(tag == "A") { + return RowDataType::A; + } else { + return RowDataType::Text; + } +} + +void Cx4TraceLogger::GetTraceRow(string& output, Cx4State& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo) +{ + for(RowPart& rowPart : _rowParts) { + switch(rowPart.DataType) { + case RowDataType::PS: { + string status = string(cpuState.Carry ? "C" : "c") + (cpuState.Zero ? "Z" : "z") + (cpuState.Overflow ? "V" : "v") + (cpuState.Negative ? "N" : "n"); + WriteStringValue(output, status, rowPart); + break; + } + + case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break; + + case RowDataType::R0: WriteIntValue(output, cpuState.Regs[0], rowPart); break; + case RowDataType::R1: WriteIntValue(output, cpuState.Regs[1], rowPart); break; + case RowDataType::R2: WriteIntValue(output, cpuState.Regs[2], rowPart); break; + case RowDataType::R3: WriteIntValue(output, cpuState.Regs[3], rowPart); break; + case RowDataType::R4: WriteIntValue(output, cpuState.Regs[4], rowPart); break; + case RowDataType::R5: WriteIntValue(output, cpuState.Regs[5], rowPart); break; + case RowDataType::R6: WriteIntValue(output, cpuState.Regs[6], rowPart); break; + case RowDataType::R7: WriteIntValue(output, cpuState.Regs[7], rowPart); break; + case RowDataType::R8: WriteIntValue(output, cpuState.Regs[8], rowPart); break; + case RowDataType::R9: WriteIntValue(output, cpuState.Regs[9], rowPart); break; + case RowDataType::R10: WriteIntValue(output, cpuState.Regs[10], rowPart); break; + case RowDataType::R11: WriteIntValue(output, cpuState.Regs[11], rowPart); break; + case RowDataType::R12: WriteIntValue(output, cpuState.Regs[12], rowPart); break; + case RowDataType::R13: WriteIntValue(output, cpuState.Regs[13], rowPart); break; + case RowDataType::R14: WriteIntValue(output, cpuState.Regs[14], rowPart); break; + case RowDataType::R15: WriteIntValue(output, cpuState.Regs[15], rowPart); break; + + case RowDataType::MAR: WriteIntValue(output, cpuState.MemoryAddressReg, rowPart); break; + case RowDataType::MDR: WriteIntValue(output, cpuState.MemoryDataReg, rowPart); break; + case RowDataType::DPR: WriteIntValue(output, cpuState.DataPointerReg, rowPart); break; + case RowDataType::ML: WriteIntValue(output, (uint32_t)(cpuState.Mult & 0xFFFFFF), rowPart); break; + case RowDataType::MH: WriteIntValue(output, (uint32_t)(cpuState.Mult >> 24), rowPart); break; + + case RowDataType::PB: WriteIntValue(output, cpuState.PB, rowPart); break; + case RowDataType::P: WriteIntValue(output, cpuState.P, rowPart); break; + + default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break; + } + } +} + +void Cx4TraceLogger::LogPpuState() +{ + _ppuState[_currentPos] = { + _ppu->GetCycle(), + _memoryManager->GetHClock(), + _ppu->GetScanline(), + _ppu->GetFrameCount() + }; +} \ No newline at end of file diff --git a/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.h b/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.h new file mode 100644 index 00000000..8b6454f4 --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.h @@ -0,0 +1,29 @@ +#pragma once +#include "stdafx.h" +#include "Debugger/BaseTraceLogger.h" +#include "SNES/Coprocessors/CX4/Cx4Types.h" + +class DisassemblyInfo; +class Debugger; +class Ppu; +class MemoryManager; + +class Cx4TraceLogger : public BaseTraceLogger +{ +private: + Ppu* _ppu = nullptr; + MemoryManager* _memoryManager = nullptr; + +protected: + RowDataType GetFormatTagType(string& tag) override; + +public: + Cx4TraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager); + + void GetTraceRow(string& output, Cx4State& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo); + void LogPpuState(); + + __forceinline uint32_t GetProgramCounter(Cx4State& state) { return (state.Cache.Address[state.Cache.Page] + (state.PC * 2)) & 0xFFFFFF; } + __forceinline uint64_t GetCycleCount(Cx4State& state) { return state.CycleCount; } + __forceinline uint8_t GetStackPointer(Cx4State& state) { return state.SP; } +}; diff --git a/Core/SNES/Debugger/TraceLogger/GsuTraceLogger.cpp b/Core/SNES/Debugger/TraceLogger/GsuTraceLogger.cpp new file mode 100644 index 00000000..6497ed7d --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/GsuTraceLogger.cpp @@ -0,0 +1,100 @@ +#include "stdafx.h" +#include "SNES/Debugger/TraceLogger/GsuTraceLogger.h" +#include "SNES/Ppu.h" +#include "SNES/MemoryManager.h" +#include "SNES/Coprocessors/GSU/GsuTypes.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Debugger.h" +#include "Debugger/DebugTypes.h" +#include "Utilities/HexUtilities.h" + +GsuTraceLogger::GsuTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, CpuType::Gsu) +{ + _ppu = ppu; + _memoryManager = memoryManager; + + TraceLoggerOptions options = {}; + SetOptions(options); +} + +RowDataType GsuTraceLogger::GetFormatTagType(string& tag) +{ + if(tag == "R0") { + return RowDataType::R0; + } else if(tag == "R1") { + return RowDataType::R1; + } else if(tag == "R2") { + return RowDataType::R2; + } else if(tag == "R3") { + return RowDataType::R3; + } else if(tag == "R4") { + return RowDataType::R4; + } else if(tag == "R5") { + return RowDataType::R5; + } else if(tag == "R6") { + return RowDataType::R6; + } else if(tag == "R7") { + return RowDataType::R7; + } else if(tag == "R8") { + return RowDataType::R8; + } else if(tag == "R9") { + return RowDataType::R9; + } else if(tag == "R10") { + return RowDataType::R10; + } else if(tag == "R11") { + return RowDataType::R11; + } else if(tag == "R12") { + return RowDataType::R12; + } else if(tag == "R13") { + return RowDataType::R13; + } else if(tag == "R14") { + return RowDataType::R14; + } else if(tag == "R15") { + return RowDataType::R15; + } else if(tag == "SRC") { + return RowDataType::Src; + } else if(tag == "DST") { + return RowDataType::Dst; + } else { + return RowDataType::Text; + } +} + +void GsuTraceLogger::GetTraceRow(string &output, GsuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo) +{ + for(RowPart& rowPart : _rowParts) { + switch(rowPart.DataType) { + case RowDataType::R0: WriteIntValue(output, cpuState.R[0], rowPart); break; + case RowDataType::R1: WriteIntValue(output, cpuState.R[1], rowPart); break; + case RowDataType::R2: WriteIntValue(output, cpuState.R[2], rowPart); break; + case RowDataType::R3: WriteIntValue(output, cpuState.R[3], rowPart); break; + case RowDataType::R4: WriteIntValue(output, cpuState.R[4], rowPart); break; + case RowDataType::R5: WriteIntValue(output, cpuState.R[5], rowPart); break; + case RowDataType::R6: WriteIntValue(output, cpuState.R[6], rowPart); break; + case RowDataType::R7: WriteIntValue(output, cpuState.R[7], rowPart); break; + case RowDataType::R8: WriteIntValue(output, cpuState.R[8], rowPart); break; + case RowDataType::R9: WriteIntValue(output, cpuState.R[9], rowPart); break; + case RowDataType::R10: WriteIntValue(output, cpuState.R[10], rowPart); break; + case RowDataType::R11: WriteIntValue(output, cpuState.R[11], rowPart); break; + case RowDataType::R12: WriteIntValue(output, cpuState.R[12], rowPart); break; + case RowDataType::R13: WriteIntValue(output, cpuState.R[13], rowPart); break; + case RowDataType::R14: WriteIntValue(output, cpuState.R[14], rowPart); break; + case RowDataType::R15: WriteIntValue(output, cpuState.R[15], rowPart); break; + + case RowDataType::Src: WriteIntValue(output, cpuState.SrcReg, rowPart); break; + case RowDataType::Dst: WriteIntValue(output, cpuState.DestReg, rowPart); break; + + default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break; + } + } +} + +void GsuTraceLogger::LogPpuState() +{ + _ppuState[_currentPos] = { + _ppu->GetCycle(), + _memoryManager->GetHClock(), + _ppu->GetScanline(), + _ppu->GetFrameCount() + }; +} \ No newline at end of file diff --git a/Core/SNES/Debugger/TraceLogger/GsuTraceLogger.h b/Core/SNES/Debugger/TraceLogger/GsuTraceLogger.h new file mode 100644 index 00000000..af256c44 --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/GsuTraceLogger.h @@ -0,0 +1,29 @@ +#pragma once +#include "stdafx.h" +#include "Debugger/BaseTraceLogger.h" +#include "SNES/Coprocessors/GSU/GsuTypes.h" + +class DisassemblyInfo; +class Debugger; +class Ppu; +class MemoryManager; + +class GsuTraceLogger : public BaseTraceLogger +{ +private: + Ppu* _ppu = nullptr; + MemoryManager* _memoryManager = nullptr; + +protected: + RowDataType GetFormatTagType(string& tag) override; + +public: + GsuTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager); + + void GetTraceRow(string& output, GsuState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo); + void LogPpuState(); + + __forceinline uint32_t GetProgramCounter(GsuState& state) { return (state.ProgramBank << 16) | state.R[15]; } + __forceinline uint64_t GetCycleCount(GsuState& state) { return state.CycleCount; } + __forceinline uint8_t GetStackPointer(GsuState& state) { return 0; } +}; diff --git a/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.cpp b/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.cpp new file mode 100644 index 00000000..1785b07a --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.cpp @@ -0,0 +1,105 @@ +#include "stdafx.h" +#include "SNES/Debugger/TraceLogger/NecDspTraceLogger.h" +#include "SNES/Ppu.h" +#include "SNES/MemoryManager.h" +#include "SNES/Coprocessors/GSU/GsuTypes.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Debugger.h" +#include "Debugger/DebugTypes.h" +#include "Utilities/HexUtilities.h" + +NecDspTraceLogger::NecDspTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, CpuType::Gsu) +{ + _ppu = ppu; + _memoryManager = memoryManager; + + TraceLoggerOptions options = {}; + SetOptions(options); +} + +RowDataType NecDspTraceLogger::GetFormatTagType(string& tag) +{ + if(tag == "A") { + return RowDataType::A; + } else if(tag == "B") { + return RowDataType::B; + }if(tag == "FlagsA") { + return RowDataType::FlagsA; + } else if(tag == "FlagsB") { + return RowDataType::FlagsB; + } else if(tag == "K") { + return RowDataType::K; + } else if(tag == "L") { + return RowDataType::L; + } else if(tag == "M") { + return RowDataType::M; + } else if(tag == "N") { + return RowDataType::N; + } else if(tag == "RP") { + return RowDataType::RP; + } else if(tag == "DP") { + return RowDataType::DP; + } else if(tag == "DR") { + return RowDataType::DR; + } else if(tag == "SR") { + return RowDataType::SR; + } else if(tag == "TR") { + return RowDataType::TR; + } else if(tag == "TRB") { + return RowDataType::TRB; + } else if(tag == "R12") { + return RowDataType::R12; + } else if(tag == "R13") { + return RowDataType::R13; + } else if(tag == "R14") { + return RowDataType::R14; + } else if(tag == "R15") { + return RowDataType::R15; + } else if(tag == "SRC") { + return RowDataType::Src; + } else if(tag == "DST") { + return RowDataType::Dst; + } else { + return RowDataType::Text; + } +} + +void NecDspTraceLogger::WriteAccFlagsValue(string& output, NecDspAccFlags flags, RowPart& rowPart) +{ + string status = string(flags.Carry ? "C" : "c") + (flags.Zero ? "Z" : "z") + (flags.Overflow0 ? "V" : "v") + (flags.Overflow1 ? "V" : "v") + (flags.Sign0 ? "N" : "n") + (flags.Sign1 ? "N" : "n"); + WriteStringValue(output, status, rowPart); +} + +void NecDspTraceLogger::GetTraceRow(string& output, NecDspState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo) +{ + for(RowPart& rowPart : _rowParts) { + switch(rowPart.DataType) { + case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break; + case RowDataType::FlagsA: WriteAccFlagsValue(output, cpuState.FlagsA, rowPart); break; + case RowDataType::B: WriteIntValue(output, cpuState.B, rowPart); break; + case RowDataType::FlagsB: WriteAccFlagsValue(output, cpuState.FlagsB, rowPart); break; + case RowDataType::K: WriteIntValue(output, cpuState.K, rowPart); break; + case RowDataType::L: WriteIntValue(output, cpuState.L, rowPart); break; + case RowDataType::M: WriteIntValue(output, cpuState.M, rowPart); break; + case RowDataType::N: WriteIntValue(output, cpuState.N, rowPart); break; + case RowDataType::RP: WriteIntValue(output, cpuState.RP, rowPart); break; + case RowDataType::DP: WriteIntValue(output, cpuState.DP, rowPart); break; + case RowDataType::DR: WriteIntValue(output, cpuState.DR, rowPart); break; + case RowDataType::SR: WriteIntValue(output, cpuState.SR, rowPart); break; + case RowDataType::TR: WriteIntValue(output, cpuState.TR, rowPart); break; + case RowDataType::TRB: WriteIntValue(output, cpuState.TRB, rowPart); break; + + default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break; + } + } +} + +void NecDspTraceLogger::LogPpuState() +{ + _ppuState[_currentPos] = { + _ppu->GetCycle(), + _memoryManager->GetHClock(), + _ppu->GetScanline(), + _ppu->GetFrameCount() + }; +} \ No newline at end of file diff --git a/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.h b/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.h new file mode 100644 index 00000000..176e7692 --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.h @@ -0,0 +1,31 @@ +#pragma once +#include "stdafx.h" +#include "Debugger/BaseTraceLogger.h" +#include "SNES/Coprocessors/DSP/NecDspTypes.h" + +class DisassemblyInfo; +class Debugger; +class Ppu; +class MemoryManager; + +class NecDspTraceLogger : public BaseTraceLogger +{ +private: + Ppu* _ppu = nullptr; + MemoryManager* _memoryManager = nullptr; + +protected: + RowDataType GetFormatTagType(string& tag) override; + + void WriteAccFlagsValue(string& output, NecDspAccFlags flags, RowPart& rowPart); + +public: + NecDspTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager); + + void GetTraceRow(string& output, NecDspState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo); + void LogPpuState(); + + __forceinline uint32_t GetProgramCounter(NecDspState& state) { return state.PC; } + __forceinline uint64_t GetCycleCount(NecDspState& state) { return 0; } //TODO + __forceinline uint8_t GetStackPointer(NecDspState& state) { return state.SP; } +}; diff --git a/Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.cpp b/Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.cpp new file mode 100644 index 00000000..20c8e00e --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h" +#include "SNES/CpuTypes.h" +#include "SNES/Ppu.h" +#include "SNES/MemoryManager.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Debugger.h" +#include "Debugger/DebugTypes.h" + +SnesCpuTraceLogger::SnesCpuTraceLogger(Debugger* debugger, CpuType cpuType, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, cpuType) +{ + _ppu = ppu; + _memoryManager = memoryManager; + + TraceLoggerOptions options = {}; + SetOptions(options); +} + +RowDataType SnesCpuTraceLogger::GetFormatTagType(string& tag) +{ + if(tag == "A") { + return RowDataType::A; + } else if(tag == "X") { + return RowDataType::X; + } else if(tag == "Y") { + return RowDataType::Y; + } else if(tag == "D") { + return RowDataType::D; + } else if(tag == "DB") { + return RowDataType::DB; + } else if(tag == "P") { + return RowDataType::PS; + } else if(tag == "SP") { + return RowDataType::SP; + } else { + return RowDataType::Text; + } +} + +void SnesCpuTraceLogger::GetTraceRow(string &output, CpuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo) +{ + constexpr char activeStatusLetters[8] = { 'N', 'V', 'M', 'X', 'D', 'I', 'Z', 'C' }; + constexpr char inactiveStatusLetters[8] = { 'n', 'v', 'm', 'x', 'd', 'i', 'z', 'c' }; + + for(RowPart& rowPart : _rowParts) { + switch(rowPart.DataType) { + case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break; + case RowDataType::X: WriteIntValue(output, cpuState.X, rowPart); break; + case RowDataType::Y: WriteIntValue(output, cpuState.Y, rowPart); break; + case RowDataType::D: WriteIntValue(output, cpuState.D, rowPart); break; + case RowDataType::DB: WriteIntValue(output, cpuState.DBR, rowPart); break; + case RowDataType::SP: WriteIntValue(output, cpuState.SP, rowPart); break; + case RowDataType::PS: GetStatusFlag(activeStatusLetters, inactiveStatusLetters, output, cpuState.PS, rowPart); break; + default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break; + } + } +} + +void SnesCpuTraceLogger::LogPpuState() +{ + _ppuState[_currentPos] = { + _ppu->GetCycle(), + _memoryManager->GetHClock(), + _ppu->GetScanline(), + _ppu->GetFrameCount() + }; +} \ No newline at end of file diff --git a/Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h b/Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h new file mode 100644 index 00000000..ebad008b --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/SnesCpuTraceLogger.h @@ -0,0 +1,29 @@ +#pragma once +#include "stdafx.h" +#include "Debugger/BaseTraceLogger.h" +#include "SNES/CpuTypes.h" + +class DisassemblyInfo; +class Debugger; +class Ppu; +class MemoryManager; + +class SnesCpuTraceLogger : public BaseTraceLogger +{ +private: + Ppu* _ppu = nullptr; + MemoryManager* _memoryManager = nullptr; + +protected: + RowDataType GetFormatTagType(string& tag) override; + +public: + SnesCpuTraceLogger(Debugger* debugger, CpuType cpuType, Ppu* ppu, MemoryManager* memoryManager); + + void GetTraceRow(string &output, CpuState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo); + void LogPpuState(); + + __forceinline uint32_t GetProgramCounter(CpuState& state) { return (state.K << 16) | state.PC; } + __forceinline uint64_t GetCycleCount(CpuState& state) { return state.CycleCount; } + __forceinline uint8_t GetStackPointer(CpuState& state) { return (uint8_t)state.SP; } +}; diff --git a/Core/SNES/Debugger/TraceLogger/SpcTraceLogger.cpp b/Core/SNES/Debugger/TraceLogger/SpcTraceLogger.cpp new file mode 100644 index 00000000..470b0510 --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/SpcTraceLogger.cpp @@ -0,0 +1,61 @@ +#include "stdafx.h" +#include "SNES/Debugger/TraceLogger/SpcTraceLogger.h" +#include "SNES/CpuTypes.h" +#include "SNES/Ppu.h" +#include "SNES/MemoryManager.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Debugger.h" +#include "Debugger/DebugTypes.h" + +SpcTraceLogger::SpcTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager) : BaseTraceLogger(debugger, CpuType::Spc) +{ + _ppu = ppu; + _memoryManager = memoryManager; + + TraceLoggerOptions options = {}; + SetOptions(options); +} + +RowDataType SpcTraceLogger::GetFormatTagType(string& tag) +{ + if(tag == "A") { + return RowDataType::A; + } else if(tag == "X") { + return RowDataType::X; + } else if(tag == "Y") { + return RowDataType::Y; + } else if(tag == "P") { + return RowDataType::PS; + } else if(tag == "SP") { + return RowDataType::SP; + } else { + return RowDataType::Text; + } +} + +void SpcTraceLogger::GetTraceRow(string &output, SpcState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo) +{ + constexpr char activeStatusLetters[8] = { 'N', 'V', 'P', 'B', 'H', 'I', 'Z', 'C' }; + constexpr char inactiveStatusLetters[8] = { 'n', 'v', 'p', 'b', 'h', 'i', 'z', 'c' }; + + for(RowPart& rowPart : _rowParts) { + switch(rowPart.DataType) { + case RowDataType::A: WriteIntValue(output, cpuState.A, rowPart); break; + case RowDataType::X: WriteIntValue(output, cpuState.X, rowPart); break; + case RowDataType::Y: WriteIntValue(output, cpuState.Y, rowPart); break; + case RowDataType::SP: WriteIntValue(output, cpuState.SP, rowPart); break; + case RowDataType::PS: GetStatusFlag(activeStatusLetters, inactiveStatusLetters, output, cpuState.PS, rowPart); break; + default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break; + } + } +} + +void SpcTraceLogger::LogPpuState() +{ + _ppuState[_currentPos] = { + _ppu->GetCycle(), + _memoryManager->GetHClock(), + _ppu->GetScanline(), + _ppu->GetFrameCount() + }; +} \ No newline at end of file diff --git a/Core/SNES/Debugger/TraceLogger/SpcTraceLogger.h b/Core/SNES/Debugger/TraceLogger/SpcTraceLogger.h new file mode 100644 index 00000000..cc06529d --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/SpcTraceLogger.h @@ -0,0 +1,29 @@ +#pragma once +#include "stdafx.h" +#include "Debugger/BaseTraceLogger.h" +#include "SNES/SpcTypes.h" + +class DisassemblyInfo; +class Debugger; +class Ppu; +class MemoryManager; + +class SpcTraceLogger : public BaseTraceLogger +{ +private: + Ppu* _ppu = nullptr; + MemoryManager* _memoryManager = nullptr; + +protected: + RowDataType GetFormatTagType(string& tag) override; + +public: + SpcTraceLogger(Debugger* debugger, Ppu* ppu, MemoryManager* memoryManager); + + void GetTraceRow(string &output, SpcState &cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo); + void LogPpuState(); + + __forceinline uint32_t GetProgramCounter(SpcState& state) { return state.PC; } + __forceinline uint64_t GetCycleCount(SpcState& state) { return state.Cycle; } + __forceinline uint8_t GetStackPointer(SpcState& state) { return state.SP; } +}; diff --git a/Core/SNES/Spc.cpp b/Core/SNES/Spc.cpp index d4adbd64..7c1df43d 100644 --- a/Core/SNES/Spc.cpp +++ b/Core/SNES/Spc.cpp @@ -337,7 +337,8 @@ uint8_t Spc::DspReadRam(uint16_t addr) { uint8_t value = _ram[addr]; #ifndef DUMMYSPC - _emu->ProcessMemoryRead(addr, value, MemoryOperationType::Read); + //TODO + //_emu->ProcessMemoryRead(addr, value, MemoryOperationType::Read); #endif return value; } @@ -345,7 +346,8 @@ uint8_t Spc::DspReadRam(uint16_t addr) void Spc::DspWriteRam(uint16_t addr, uint8_t value) { #ifndef DUMMYSPC - _emu->ProcessMemoryWrite(addr, value, MemoryOperationType::Write); + //TODO + //_emu->ProcessMemoryWrite(addr, value, MemoryOperationType::Write); #endif _ram[addr] = value; } diff --git a/InteropDLL/DebugApiWrapper.cpp b/InteropDLL/DebugApiWrapper.cpp index 6a048c9d..a879fea7 100644 --- a/InteropDLL/DebugApiWrapper.cpp +++ b/InteropDLL/DebugApiWrapper.cpp @@ -1,7 +1,6 @@ #include "stdafx.h" #include "Core/Shared/Emulator.h" #include "Core/Debugger/Debugger.h" -#include "Core/Debugger/TraceLogger.h" #include "Core/Debugger/MemoryDumper.h" #include "Core/Debugger/MemoryAccessCounter.h" #include "Core/Debugger/Disassembler.h" @@ -16,6 +15,7 @@ #include "Core/Debugger/Profiler.h" #include "Core/Debugger/IAssembler.h" #include "Core/Debugger/IEventManager.h" +#include "Core/Debugger/ITraceLogger.h" #include "Core/Gameboy/GbTypes.h" extern shared_ptr _emu; @@ -51,11 +51,13 @@ extern "C" DllExport uint32_t __stdcall GetDisassemblyOutput(CpuType type, uint32_t lineIndex, CodeLineData output[], uint32_t rowCount) { return GetDebugger()->GetDisassembler()->GetDisassemblyOutput(type, lineIndex, output, rowCount); } DllExport int32_t __stdcall SearchDisassembly(CpuType type, const char* searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards) { return GetDebugger()->GetDisassembler()->SearchDisassembly(type, searchString, startPosition, endPosition, searchBackwards); } - DllExport void __stdcall SetTraceOptions(TraceLoggerOptions options) { GetDebugger()->GetTraceLogger()->SetOptions(options); } - DllExport void __stdcall StartTraceLogger(char* filename) { GetDebugger()->GetTraceLogger()->StartLogging(filename); } - DllExport void __stdcall StopTraceLogger() { GetDebugger()->GetTraceLogger()->StopLogging(); } - DllExport void __stdcall ClearTraceLog() { GetDebugger()->GetTraceLogger()->Clear(); } - DllExport const char* GetExecutionTrace(uint32_t lineCount) { return GetDebugger()->GetTraceLogger()->GetExecutionTrace(lineCount); } + //DllExport void __stdcall ClearTraceLog() { GetDebugger()->GetTraceLogger()->Clear(); } + + DllExport void __stdcall SetTraceOptions(CpuType type, TraceLoggerOptions options) { GetDebugger()->GetTraceLogger(type)->SetOptions(options); } + DllExport uint32_t __stdcall GetExecutionTrace(TraceRow output[], uint32_t startOffset, uint32_t lineCount) { return GetDebugger()->GetExecutionTrace(output, startOffset, lineCount); } + + //DllExport void __stdcall StartLogTraceToFile(char* filename) { GetDebugger()->GetTraceLogger()->StartLogging(filename); } + //DllExport void __stdcall StopLogTraceToFile() { GetDebugger()->GetTraceLogger()->StopLogging(); } DllExport void __stdcall SetBreakpoints(Breakpoint breakpoints[], uint32_t length) { GetDebugger()->SetBreakpoints(breakpoints, length); } DllExport int32_t __stdcall EvaluateExpression(char* expression, CpuType cpuType, EvalResultType *resultType, bool useCache) { return GetDebugger()->EvaluateExpression(expression, cpuType, *resultType, useCache); } diff --git a/NewUI/Config/Debugger/DebugConfig.cs b/NewUI/Config/Debugger/DebugConfig.cs index 9a96f9a9..de1697c2 100644 --- a/NewUI/Config/Debugger/DebugConfig.cs +++ b/NewUI/Config/Debugger/DebugConfig.cs @@ -13,7 +13,7 @@ namespace Mesen.Config public class DebugConfig { //public DebuggerShortcutsConfig Shortcuts = new DebuggerShortcutsConfig(); - public TraceLoggerInfo TraceLogger = new TraceLoggerInfo(); + public TraceLoggerConfig TraceLogger = new TraceLoggerConfig(); public HexEditorConfig HexEditor = new HexEditorConfig(); public EventViewerConfig EventViewer = new EventViewerConfig(); public DebuggerConfig Debugger = new DebuggerConfig(); diff --git a/NewUI/Config/Debugger/TraceLoggerConfig.cs b/NewUI/Config/Debugger/TraceLoggerConfig.cs new file mode 100644 index 00000000..2a6a8c6d --- /dev/null +++ b/NewUI/Config/Debugger/TraceLoggerConfig.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Serialization; +using Avalonia; +using Avalonia.Media; +using Mesen.Debugger; +using Mesen.ViewModels; + +namespace Mesen.Config +{ + public class TraceLoggerConfig : ViewModelBase + { + public bool AutoRefresh { get; set; } = true; + + public Size WindowSize { get; set; } = new Size(0, 0); + public Point WindowLocation { get; set; } = new Point(0, 0); + public string FontFamily { get; set; } = DebuggerConfig.MonospaceFontFamily; + public FontStyle FontStyle { get; set; } = FontStyle.Normal; + public float FontSize { get; set; } = DebuggerConfig.DefaultFontSize; + public int TextZoom { get; set; } = 100; + + public TraceLoggerCpuConfig SnesCpu { get; set; } + public TraceLoggerCpuConfig SpcCpu { get; set; } + public TraceLoggerCpuConfig Sa1Cpu { get; set; } + public TraceLoggerCpuConfig GsuCpu { get; set; } + public TraceLoggerCpuConfig Cx4Cpu { get; set; } + public TraceLoggerCpuConfig NecDspCpu { get; set; } + + public TraceLoggerCpuConfig NesCpu { get; set; } + public TraceLoggerCpuConfig GameboyCpu { get; set; } + + public TraceLoggerConfig() + { + SnesCpu = new TraceLoggerCpuConfig() { + Enabled = true, + Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,4h] X:[X,4h] Y:[Y,4h] S:[SP,4h] D:[D,4h] DB:[DB,2h] P:[P,h] H:[HClock,3] V:[Scanline,3]" + }; + + SpcCpu = new TraceLoggerCpuConfig() { + Enabled = true, + Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,2h] X:[X,2h] Y:[Y,2h] S:[SP,2h] P:[P,8] H:[HClock,3] V:[Scanline,3]" + }; + + Sa1Cpu = new TraceLoggerCpuConfig() { + Enabled = true, + Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,4h] X:[X,4h] Y:[Y,4h] S:[SP,4h] D:[D,4h] DB:[DB,2h] P:[P,h] H:[HClock,3] V:[Scanline,3]" + }; + + GsuCpu = new TraceLoggerCpuConfig() { + Enabled = true, + Format = "[Disassembly][Align,28] SRC:[SRC,2h] DST:[DST,2h] R0:[R0,4h] R1:[R1,4h] R2:[R2,4h] R3:[R3,4h] R4:[R4,4h] R5:[R5,4h] R6:[R6,4h] R7:[R7,4h] R8:[R8,4h] R9:[R9,4h] R10:[R10,4h] R11:[R11,4h] R12:[R12,4h] R13:[R13,4h] R14:[R14,4h] R15:[R15,4h] H:[Cycle,3] V:[Scanline,3]" + }; + + Cx4Cpu = new TraceLoggerCpuConfig() { + Enabled = true, + Format = "[Disassembly][Align,28] A:[A,6h] MAR:[MAR,6h] MDR:[MDR,6h] DPR:[DPR,6h] ML:[ML,6h] MH:[MH,6h] P:[P,4h] PB:[PB,4h] PS:[PS] R0:[R0,6h] R1:[R1,6h] R2:[R2,6h] R3:[R3,6h] R4:[R4,6h] R5:[R5,6h] R6:[R6,6h] R7:[R7,6h] R8:[R8,6h] R9:[R9,6h] R10:[R10,6h] R11:[R11,6h] R12:[R12,6h] R13:[R13,6h] R14:[R14,6h] R15:[R15,6h] H:[Cycle,3] V:[Scanline,3]" + }; + + NecDspCpu = new TraceLoggerCpuConfig() { + Enabled = true, + Format = "[Disassembly][Align,28] A:[A,4h] [FlagsA] B:[A,4h] [FlagsB] K:[K,4h] L:[L,4h] M:[M,4h] N:[N,4h] RP:[RP,4h] DP:[DP,4h] DR:[DR,4h] SR:[SR,4h] TR:[TR,4h] TRB:[TRB,4h] H:[Cycle,3] V:[Scanline,3]" + }; + + NesCpu = new TraceLoggerCpuConfig() { + Enabled = true, + Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,2h] X:[X,2h] Y:[Y,2h] S:[SP,2h] P:[P,h] V:[Scanline,3] H:[Cycle,3]" + }; + + GameboyCpu = new TraceLoggerCpuConfig() { + Enabled = true, + Format = "[Disassembly][EffectiveAddress] [MemoryValue,h][Align,28] A:[A,2h] B:[B,2h] C:[C,2h] D:[D,2h] E:[E,2h] F:[PS,4] HL:[H,2h][L,2h] V:[Scanline,3] H:[Cycle,3]" + }; + } + } +} diff --git a/NewUI/Config/Debugger/TraceLoggerCpuConfig.cs b/NewUI/Config/Debugger/TraceLoggerCpuConfig.cs new file mode 100644 index 00000000..6a2ad315 --- /dev/null +++ b/NewUI/Config/Debugger/TraceLoggerCpuConfig.cs @@ -0,0 +1,35 @@ +using ReactiveUI.Fody.Helpers; + +namespace Mesen.Config +{ + public class TraceLoggerCpuConfig : BaseConfig + { + [Reactive] public bool Enabled { get; set; } + + [Reactive] public bool ShowByteCode { get; set; } + [Reactive] public bool ShowRegisters { get; set; } + [Reactive] public bool ShowCpuCycles { get; set; } + + [Reactive] public bool ShowPpuCycles { get; set; } + [Reactive] public bool ShowPpuScanline { get; set; } + [Reactive] public bool ShowPpuFrames { get; set; } + + [Reactive] public bool IndentCode { get; set; } + [Reactive] public bool ShowEffectiveAddresses { get; set; } + [Reactive] public bool ShowMemoryValues { get; set; } + [Reactive] public bool UseLabels { get; set; } + + [Reactive] public StatusFlagFormat StatusFormat { get; set; } + + [Reactive] public bool OverrideFormat { get; set; } + [Reactive] public string Format { get; set; } = ""; + [Reactive] public string Condition { get; set; } = ""; + } + + public enum StatusFlagFormat + { + Hexadecimal = 0, + Text = 1, + CompactText = 2 + } +} diff --git a/NewUI/Config/Debugger/TraceLoggerInfo.cs b/NewUI/Config/Debugger/TraceLoggerInfo.cs deleted file mode 100644 index 34b96498..00000000 --- a/NewUI/Config/Debugger/TraceLoggerInfo.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; -using System.Xml.Serialization; -using Avalonia; -using Avalonia.Media; -using Mesen.Debugger; - -namespace Mesen.Config -{ - public class TraceLoggerInfo - { - public TraceLoggerOptions LogOptions; - public bool AutoRefresh = true; - public int LineCount = 1000; - - public Size WindowSize = new Size(0, 0); - public Point WindowLocation; - public string FontFamily = DebuggerConfig.MonospaceFontFamily; - public FontStyle FontStyle = FontStyle.Normal; - public float FontSize = DebuggerConfig.DefaultFontSize; - public int TextZoom = 100; - - public TraceLoggerInfo() - { - LogOptions = new TraceLoggerOptions() { - LogCpu = true, - ShowByteCode = true, - ShowEffectiveAddresses = true, - ShowPpuFrames = false, - ShowPpuCycles = true, - ShowPpuScanline = true, - ShowRegisters = true, - UseLabels = false, - StatusFormat = StatusFlagFormat.Text - }; - } - } - - public class TraceLoggerOptions - { - public bool LogCpu; - public bool LogSpc; - public bool LogNecDsp; - public bool LogSa1; - public bool LogGsu; - public bool LogCx4; - public bool LogGameboy; - - public bool ShowByteCode; - public bool ShowRegisters; - public bool ShowCpuCycles; - public bool ShowPpuCycles; - public bool ShowPpuScanline; - public bool ShowPpuFrames; - public bool ShowExtraInfo; - public bool IndentCode; - public bool ShowEffectiveAddresses; - public bool ShowMemoryValues; - public bool UseLabels; - public bool ExtendZeroPage; - public bool UseWindowsEol = true; //TODO - - public StatusFlagFormat StatusFormat; - - public bool OverrideFormat; - public string Format = ""; - } - - public enum StatusFlagFormat - { - Hexadecimal = 0, - Text = 1, - CompactText = 2 - } -} diff --git a/NewUI/Debugger/Controls/DisassemblyViewer.cs b/NewUI/Debugger/Controls/DisassemblyViewer.cs index b67462de..5c09604e 100644 --- a/NewUI/Debugger/Controls/DisassemblyViewer.cs +++ b/NewUI/Debugger/Controls/DisassemblyViewer.cs @@ -42,11 +42,17 @@ namespace Mesen.Debugger.Controls private double RowHeight => this.LetterSize.Height; private int VisibleRows => (int)(Bounds.Height / RowHeight) - 1; + private bool _scrollByAddress = false; private bool _updatingScroll = false; static DisassemblyViewer() { - AffectsRender(DataProviderProperty, ScrollPositionProperty, StyleProviderProperty); + //AffectsRender(DataProviderProperty, ScrollPositionProperty, StyleProviderProperty); + DataProviderProperty.Changed.AddClassHandler((x, e) => { + x.Refresh(); + x.InvalidateVisual(); + }); + ScrollPositionProperty.Changed.AddClassHandler((x, e) => { if(x._updatingScroll) { return; @@ -54,20 +60,28 @@ namespace Mesen.Debugger.Controls x.Refresh(); - x._updatingScroll = true; - CodeLineData[] lines = x._lines; - if(e.OldValue is int oldValue && e.NewValue is int newValue && oldValue < newValue) { - foreach(CodeLineData line in lines) { - if(line.Address >= 0 && newValue < line.Address) { - x.ScrollPosition = line.Address; - break; + if(x._scrollByAddress) { + x._updatingScroll = true; + CodeLineData[] lines = x._lines; + if(e.OldValue is int oldValue && e.NewValue is int newValue && oldValue < newValue) { + foreach(CodeLineData line in lines) { + if(line.Address >= 0 && newValue < line.Address) { + x.ScrollPosition = line.Address; + break; + } } } + x._updatingScroll = false; } - x._updatingScroll = false; + x.InvalidateVisual(); }); } + public DisassemblyViewer() + { + ClipToBounds = true; + } + protected override void OnPointerPressed(PointerPressedEventArgs e) { base.OnPointerPressed(e); @@ -76,13 +90,14 @@ namespace Mesen.Debugger.Controls int rowNumber = (int)(p.Position.Y / LetterSize.Height); CodeLineData[] lines = _lines; if(rowNumber < lines.Length) { + CpuType cpuType = lines[rowNumber].CpuType; AddressInfo relAddress = new AddressInfo() { Address = lines[rowNumber].Address, - Type = DataProvider.CpuType.ToMemoryType() + Type = cpuType.ToMemoryType() }; AddressInfo absAddress = DebugApi.GetAbsoluteAddress(relAddress); - BreakpointManager.ToggleBreakpoint(absAddress.Address < 0 ? relAddress : absAddress, DataProvider.CpuType); + BreakpointManager.ToggleBreakpoint(absAddress.Address < 0 ? relAddress : absAddress, cpuType); } } @@ -111,14 +126,16 @@ namespace Mesen.Debugger.Controls InitFontAndLetterSize(); _lines = dp?.GetCodeLines(scrollPosition, VisibleRows + 3) ?? new CodeLineData[0]; - foreach(CodeLineData line in _lines) { - if(line.Address >= 0) { - if(ScrollPosition != line.Address) { - _updatingScroll = true; - ScrollPosition = line.Address; - _updatingScroll = false; + if(_scrollByAddress) { + foreach(CodeLineData line in _lines) { + if(line.Address >= 0) { + if(ScrollPosition != line.Address) { + _updatingScroll = true; + ScrollPosition = line.Address; + _updatingScroll = false; + } + break; } - break; } } } @@ -141,7 +158,7 @@ namespace Mesen.Debugger.Controls string addrFormat = "X" + DataProvider.CpuType.GetAddressSize(); double symbolMargin = 20; double addressMargin = Math.Floor(LetterSize.Width * DataProvider.CpuType.GetAddressSize() + symbolMargin) + 0.5; - double byteCodeMargin = Math.Floor(LetterSize.Width * (4 * DataProvider.CpuType.GetByteCodeSize())); + double byteCodeMargin = Math.Floor(LetterSize.Width * (3 * DataProvider.CpuType.GetByteCodeSize())); double codeIndent = Math.Floor(LetterSize.Width * 2) + 0.5; //Draw margin (address) @@ -261,7 +278,6 @@ namespace Mesen.Debugger.Controls public interface ILineStyleProvider { LineProperties GetLineStyle(CodeLineData lineData, int lineIndex); - string? GetLineComment(int lineIndex); List GetCodeColors(CodeLineData lineData, bool highlightCode, string addressFormat, Color? textColor, bool showMemoryValues); } diff --git a/NewUI/Debugger/Disassembly/BaseStyleProvider.cs b/NewUI/Debugger/Disassembly/BaseStyleProvider.cs index aeb25aa7..5f773f1b 100644 --- a/NewUI/Debugger/Disassembly/BaseStyleProvider.cs +++ b/NewUI/Debugger/Disassembly/BaseStyleProvider.cs @@ -15,8 +15,6 @@ namespace Mesen.Debugger.Disassembly { public int? ActiveAddress { get; set; } - public string? GetLineComment(int lineIndex) => null; - public static void ConfigureActiveStatement(LineProperties props) { props.FgColor = Colors.Black; diff --git a/NewUI/Debugger/Disassembly/CodeLineData.cs b/NewUI/Debugger/Disassembly/CodeLineData.cs index 9c429f67..a692ad95 100644 --- a/NewUI/Debugger/Disassembly/CodeLineData.cs +++ b/NewUI/Debugger/Disassembly/CodeLineData.cs @@ -15,20 +15,20 @@ namespace Mesen.Debugger public string Text = ""; - public Int32 Address; - public Int32 AbsoluteAddress; + public Int32 Address = -1; + public Int32 AbsoluteAddress = -1; public LineFlags Flags; public int? CustomIndent = null; - public byte OpSize; + public byte OpSize = 0; public string ByteCode = ""; public string Comment = ""; - public Int32 EffectiveAddress; - public UInt16 Value; - public byte ValueSize; + public Int32 EffectiveAddress = -1; + public UInt16 Value = 0; + public byte ValueSize = 0; public string GetEffectiveAddressString(string format) { @@ -90,7 +90,7 @@ namespace Mesen.Debugger this.OpSize = data.OpSize; this.ByteCode = ""; for(int i = 0; i < this.OpSize; i++) { - this.ByteCode += "$" + data.ByteCode[i].ToString("X2") + " "; + this.ByteCode += data.ByteCode[i].ToString("X2") + " "; } this.Address = data.Address; diff --git a/NewUI/Debugger/ViewModels/TraceLoggerViewModel.cs b/NewUI/Debugger/ViewModels/TraceLoggerViewModel.cs new file mode 100644 index 00000000..0af49e9a --- /dev/null +++ b/NewUI/Debugger/ViewModels/TraceLoggerViewModel.cs @@ -0,0 +1,178 @@ +using Avalonia.Controls; +using Avalonia.Media; +using Mesen.Config; +using Mesen.Debugger.Controls; +using Mesen.Debugger.Disassembly; +using Mesen.Interop; +using Mesen.Localization; +using Mesen.ViewModels; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Mesen.Debugger.ViewModels +{ + public class TraceLoggerViewModel : ViewModelBase + { + [Reactive] public TraceLoggerCodeDataProvider DataProvider { get; set; } = new TraceLoggerCodeDataProvider(); + [Reactive] public TraceLoggerStyleProvider StyleProvider { get; set; } = new TraceLoggerStyleProvider(); + [Reactive] public int ScrollPosition { get; set; } = 0; + [Reactive] public int MaxScrollPosition { get; set; } = 0; + + [Reactive] public List Tabs { get; set; } = new List(); + + public TraceLoggerViewModel() + { + if(Design.IsDesignMode) { + return; + } + + UpdateAvailableTabs(); + } + + public void UpdateAvailableTabs() + { + List tabs = new(); + RomInfo romInfo = EmuApi.GetRomInfo(); + StyleProvider.SetConsoleType(romInfo.ConsoleType); + foreach(CpuType type in romInfo.CpuTypes) { + tabs.Add(new TraceLoggerOptionTab() { + TabName = ResourceHelper.GetEnumText(type), + CpuType = type, + Options = type switch { + CpuType.Cpu => ConfigManager.Config.Debug.TraceLogger.SnesCpu.Clone(), + CpuType.Spc => ConfigManager.Config.Debug.TraceLogger.SpcCpu.Clone(), + CpuType.Gameboy => ConfigManager.Config.Debug.TraceLogger.GameboyCpu.Clone(), + CpuType.Nes => ConfigManager.Config.Debug.TraceLogger.NesCpu.Clone(), + CpuType.Sa1 => ConfigManager.Config.Debug.TraceLogger.Sa1Cpu.Clone(), + CpuType.Gsu => ConfigManager.Config.Debug.TraceLogger.GsuCpu.Clone(), + CpuType.Cx4 => ConfigManager.Config.Debug.TraceLogger.Cx4Cpu.Clone(), + CpuType.NecDsp => ConfigManager.Config.Debug.TraceLogger.NecDspCpu.Clone(), + _ => throw new Exception("Unsupported cpu type") + } + }); + } + + Tabs = tabs; + } + + public void UpdateLog() + { + foreach(TraceLoggerOptionTab tab in Tabs) { + InteropTraceLoggerOptions options = new InteropTraceLoggerOptions() { + Enabled = tab.Options.Enabled, + UseLabels = true, + Format = Encoding.UTF8.GetBytes(tab.Options.Format), + Condition = Encoding.UTF8.GetBytes(tab.Options.Condition) + }; + + Array.Resize(ref options.Condition, 1000); + Array.Resize(ref options.Format, 1000); + + DebugApi.SetTraceOptions(tab.CpuType, options); + } + + DataProvider = new TraceLoggerCodeDataProvider(); + MaxScrollPosition = DataProvider.GetLineCount(); + ScrollPosition = MaxScrollPosition - 0x20; + } + } + + public class TraceLoggerOptionTab + { + [Reactive] public string TabName { get; set; } = ""; + [Reactive] public CpuType CpuType { get; set; } = CpuType.Cpu; + + public TraceLoggerCpuConfig Options { get; set; } = new TraceLoggerCpuConfig(); + } + + public class TraceLoggerCodeDataProvider : ICodeDataProvider + { + public TraceLoggerCodeDataProvider() + { + } + + public bool UseOptimizedSearch { get { return false; } } + + public CpuType CpuType => CpuType.Cpu; + + public int GetLineCount() + { + return 30000; + } + + public int GetNextResult(string searchString, int startPosition, int endPosition, bool searchBackwards) + { + throw new NotImplementedException(); + } + + public CodeLineData[] GetCodeLines(int startIndex, int rowCount) + { + TraceRow[] rows = DebugApi.GetExecutionTrace((uint)(30000 - startIndex), (uint)rowCount); + + List lines = new(rowCount); + for(int i = 0; i < rows.Length; i++) { + lines.Insert(0, new CodeLineData(rows[i].Type) { + Address = (int)rows[i].ProgramCounter, + Text = rows[i].GetOutput(), + ByteCode = rows[i].GetByteCode(), + EffectiveAddress = -1 + }); + } + + while(lines.Count < rowCount) { + lines.Insert(0, new CodeLineData(CpuType.Cpu)); + } + + return lines.ToArray(); + } + } + + public class TraceLoggerStyleProvider : ILineStyleProvider + { + private LineProperties _defaultLine = new LineProperties() { AddressColor = null, LineBgColor = null }; + private LineProperties _spcLine = new LineProperties() { AddressColor = Color.FromRgb(30, 145, 30), LineBgColor = Color.FromRgb(230, 245, 230) }; + private LineProperties _coprocLine = new LineProperties() { AddressColor = Color.FromRgb(30, 30, 145), LineBgColor = Color.FromRgb(230, 230, 245) }; + private ConsoleType _consoleType = ConsoleType.Snes; + + public TraceLoggerStyleProvider() + { + } + + public List GetCodeColors(CodeLineData lineData, bool highlightCode, string addressFormat, Color? textColor, bool showMemoryValues) + { + return CodeHighlighting.GetCpuHighlights(lineData, highlightCode, addressFormat, textColor, showMemoryValues); + } + + public LineProperties GetLineStyle(CodeLineData lineData, int lineIndex) + { + switch(lineData.CpuType) { + case CpuType.Spc: return _spcLine; + + case CpuType.NecDsp: + case CpuType.Sa1: + case CpuType.Gsu: + case CpuType.Cx4: + return _coprocLine; + + case CpuType.Gameboy: + if(_consoleType == ConsoleType.Snes) { + return _coprocLine; + } + break; + + default: break; + } + + return _defaultLine; + } + + internal void SetConsoleType(ConsoleType consoleType) + { + _consoleType = consoleType; + } + } + +} diff --git a/NewUI/Debugger/Windows/TraceLoggerWindow.axaml b/NewUI/Debugger/Windows/TraceLoggerWindow.axaml new file mode 100644 index 00000000..46dbace8 --- /dev/null +++ b/NewUI/Debugger/Windows/TraceLoggerWindow.axaml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NewUI/Debugger/Windows/TraceLoggerWindow.axaml.cs b/NewUI/Debugger/Windows/TraceLoggerWindow.axaml.cs new file mode 100644 index 00000000..f03cac0f --- /dev/null +++ b/NewUI/Debugger/Windows/TraceLoggerWindow.axaml.cs @@ -0,0 +1,87 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.LogicalTree; +using Avalonia.Markup.Xaml; +using Avalonia.Media; +using Avalonia.Threading; +using AvaloniaEdit; +using AvaloniaEdit.Highlighting; +using AvaloniaEdit.Highlighting.Xshd; +using Mesen.Debugger.Controls; +using Mesen.Debugger.ViewModels; +using Mesen.Interop; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using System.Xml; + +namespace Mesen.Debugger.Windows +{ + public class TraceLoggerWindow : Window + { + private NotificationListener? _listener = null; + private TraceLoggerViewModel Model => ((TraceLoggerViewModel)DataContext!); + private int _refreshCounter = 0; + + public TraceLoggerWindow() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + } + + protected override void OnOpened(EventArgs e) + { + base.OnOpened(e); + + if(Design.IsDesignMode) { + return; + } + + _listener = new NotificationListener(); + _listener.OnNotification += listener_OnNotification; + } + + protected override void OnClosing(CancelEventArgs e) + { + base.OnClosing(e); + _listener?.Dispose(); + } + + private void listener_OnNotification(NotificationEventArgs e) + { + switch(e.NotificationType) { + case ConsoleNotificationType.GameLoaded: + Model.UpdateAvailableTabs(); + break; + + case ConsoleNotificationType.CodeBreak: { + Dispatcher.UIThread.Post(() => { + Model.UpdateLog(); + }); + break; + } + + case ConsoleNotificationType.PpuFrameDone: { + _refreshCounter++; + if(_refreshCounter == 9) { + //Refresh every 9 frames, ~7fps + Dispatcher.UIThread.Post(() => { + Model.UpdateLog(); + }); + _refreshCounter = 0; + } + break; + } + } + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/NewUI/Interop/DebugApi.cs b/NewUI/Interop/DebugApi.cs index beb35d18..31f8eb1b 100644 --- a/NewUI/Interop/DebugApi.cs +++ b/NewUI/Interop/DebugApi.cs @@ -20,10 +20,26 @@ namespace Mesen.Interop [DllImport(DllPath)] public static extern void ResumeExecution(); [DllImport(DllPath)] public static extern void Step(CpuType cpuType, Int32 instructionCount, StepType type = StepType.Step); - [DllImport(DllPath)] public static extern void StartTraceLogger([MarshalAs(UnmanagedType.LPUTF8Str)]string filename); + /*[DllImport(DllPath)] public static extern void StartTraceLogger([MarshalAs(UnmanagedType.LPUTF8Str)]string filename); [DllImport(DllPath)] public static extern void StopTraceLogger(); - [DllImport(DllPath)] public static extern void SetTraceOptions(InteropTraceLoggerOptions options); - [DllImport(DllPath)] public static extern void ClearTraceLog(); + [DllImport(DllPath)] public static extern void ClearTraceLog();*/ + + [DllImport(DllPath)] public static extern void SetTraceOptions(CpuType cpuType, InteropTraceLoggerOptions options); + + [DllImport(DllPath, EntryPoint = "GetExecutionTrace")] private static extern UInt32 GetExecutionTraceWrapper(IntPtr output, UInt32 startOffset, UInt32 maxRowCount); + public static TraceRow[] GetExecutionTrace(UInt32 startOffset, UInt32 maxRowCount) + { + TraceRow[] rows = new TraceRow[maxRowCount]; + + GCHandle handle = GCHandle.Alloc(rows, GCHandleType.Pinned); + IntPtr ptr = handle.AddrOfPinnedObject(); + UInt32 rowCount = DebugApi.GetExecutionTraceWrapper(ptr, startOffset, maxRowCount); + handle.Free(); + + Array.Resize(ref rows, (int)rowCount); + + return rows; + } [DllImport(DllPath, EntryPoint = "GetDebuggerLog")] private static extern IntPtr GetDebuggerLogWrapper(); public static string GetLog() { return Utf8Utilities.PtrToStringUtf8(DebugApi.GetDebuggerLogWrapper()).Replace("\n", Environment.NewLine); } @@ -49,9 +65,6 @@ namespace Mesen.Interop [DllImport(DllPath)] public static extern int SearchDisassembly(CpuType type, [MarshalAs(UnmanagedType.LPUTF8Str)]string searchString, int startPosition, int endPosition, [MarshalAs(UnmanagedType.I1)]bool searchBackwards); - [DllImport(DllPath, EntryPoint = "GetExecutionTrace")] private static extern IntPtr GetExecutionTraceWrapper(UInt32 lineCount); - public static string GetExecutionTrace(UInt32 lineCount) { return Utf8Utilities.PtrToStringUtf8(DebugApi.GetExecutionTraceWrapper(lineCount)); } - [DllImport(DllPath, EntryPoint = "GetState")] private static extern void GetState(IntPtr state, CpuType cpuType); public static T GetState(CpuType cpuType) where T : struct, BaseState @@ -654,19 +667,9 @@ namespace Mesen.Interop [Serializable] public struct InteropTraceLoggerOptions { - [MarshalAs(UnmanagedType.I1)] public bool LogCpu; - [MarshalAs(UnmanagedType.I1)] public bool LogSpc; - [MarshalAs(UnmanagedType.I1)] public bool LogNecDsp; - [MarshalAs(UnmanagedType.I1)] public bool LogSa1; - [MarshalAs(UnmanagedType.I1)] public bool LogGsu; - [MarshalAs(UnmanagedType.I1)] public bool LogCx4; - [MarshalAs(UnmanagedType.I1)] public bool LogGameboy; - - [MarshalAs(UnmanagedType.I1)] public bool ShowExtraInfo; + [MarshalAs(UnmanagedType.I1)] public bool Enabled; [MarshalAs(UnmanagedType.I1)] public bool IndentCode; [MarshalAs(UnmanagedType.I1)] public bool UseLabels; - [MarshalAs(UnmanagedType.I1)] public bool UseWindowsEol; - [MarshalAs(UnmanagedType.I1)] public bool ExtendZeroPage; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)] public byte[] Condition; @@ -843,4 +846,41 @@ namespace Mesen.Interop public UInt64 MaxCycles; public AddressInfo Address; } + + public unsafe struct TraceRow + { + public UInt32 ProgramCounter; + public CpuType Type; + + public fixed byte ByteCode[4]; + public byte ByteCodeSize; + + public fixed byte LogOutput[500]; + + public unsafe string GetOutput() + { + fixed(byte* output = LogOutput) { + int i; + for(i = 0; i < 500; i++) { + if(output[i] == 0) { + break; + } + } + + return UTF8Encoding.UTF8.GetString(output, i); + } + } + + public unsafe string GetByteCode() + { + fixed(byte* output = ByteCode) { + StringBuilder sb = new StringBuilder(); + int i; + for(i = 0; i < ByteCodeSize && i < 4; i++) { + sb.Append(ByteCode[i].ToString("X2") + " "); + } + return sb.ToString().Trim(); + } + } + } } diff --git a/NewUI/Localization/resources.en.xml b/NewUI/Localization/resources.en.xml index 63c13f49..272725fb 100644 --- a/NewUI/Localization/resources.en.xml +++ b/NewUI/Localization/resources.en.xml @@ -798,6 +798,13 @@ Stop +
+ Trace Logger + Format: + Condition: + Enabled +
+
Event Viewer diff --git a/NewUI/NewUI.csproj b/NewUI/NewUI.csproj index 64b3d81e..cb6c68f3 100644 --- a/NewUI/NewUI.csproj +++ b/NewUI/NewUI.csproj @@ -15,14 +15,14 @@ Assets\Icon.ico - false + true C:\Code\Mesen-S\bin\x64\Debug\ C:\Code\Mesen-S\bin\x64\Release\ false DEBUG - false + true @@ -215,6 +215,9 @@ SnesPpuView.axaml + + TraceLoggerWindow.axaml + ProfilerWindow.axaml diff --git a/NewUI/Views/MainMenuView.axaml b/NewUI/Views/MainMenuView.axaml index 8ec7c370..8a6d58fc 100644 --- a/NewUI/Views/MainMenuView.axaml +++ b/NewUI/Views/MainMenuView.axaml @@ -405,6 +405,11 @@ + + + + + diff --git a/NewUI/Views/MainMenuView.axaml.cs b/NewUI/Views/MainMenuView.axaml.cs index e5a387c1..127bf278 100644 --- a/NewUI/Views/MainMenuView.axaml.cs +++ b/NewUI/Views/MainMenuView.axaml.cs @@ -95,6 +95,13 @@ namespace Mesen.Views }.Show(); } + private void OnTraceLoggerClick(object sender, RoutedEventArgs e) + { + new TraceLoggerWindow { + DataContext = new TraceLoggerViewModel() + }.Show(); + } + private void OnEventViewerClick(object sender, RoutedEventArgs e) { RomInfo romInfo = EmuApi.GetRomInfo();