diff --git a/Core/CPU.h b/Core/CPU.h index 062c47e5..4a850fce 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -5,6 +5,7 @@ #include "Snapshotable.h" #include "TraceLogger.h" #include "EmulationSettings.h" +#include "CpuState.h" namespace PSFlags { @@ -24,9 +25,9 @@ namespace PSFlags enum AddrMode { None, Acc, Imp, Imm, Rel, - Zero, ZeroX, ZeroY, + Zero, Abs, ZeroX, ZeroY, Ind, IndX, IndY, IndYW, - Abs, AbsX, AbsXW, AbsY, AbsYW + AbsX, AbsXW, AbsY, AbsYW }; enum class IRQSource @@ -37,22 +38,6 @@ enum class IRQSource FdsDisk = 8, }; -struct State -{ - uint16_t PC = 0; - uint8_t SP = 0; - uint8_t A = 0; - uint8_t X = 0; - uint8_t Y = 0; - uint8_t PS = 0; - uint32_t IRQFlag = 0; - int32_t CycleCount = 0; - bool NMIFlag = false; - - //Used by debugger - uint16_t DebugPC = 0; -}; - class CPU : public Snapshotable { public: diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index d8241ed8..d92b1021 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -321,7 +321,7 @@ true true WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - true + false AnySuitable true Speed @@ -333,6 +333,11 @@ true + + + false + + Console @@ -435,6 +440,7 @@ + @@ -583,6 +589,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 4274fa9d..50cde7ec 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1117,6 +1117,12 @@ Nes\Mappers\Unif + + Nes + + + Nes + diff --git a/Core/CpuState.h b/Core/CpuState.h new file mode 100644 index 00000000..df902bff --- /dev/null +++ b/Core/CpuState.h @@ -0,0 +1,18 @@ +#pragma once +#include "stdafx.h" + +struct State +{ + uint16_t PC = 0; + uint8_t SP = 0; + uint8_t A = 0; + uint8_t X = 0; + uint8_t Y = 0; + uint8_t PS = 0; + uint32_t IRQFlag = 0; + int32_t CycleCount = 0; + bool NMIFlag = false; + + //Used by debugger + uint16_t DebugPC = 0; +}; \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index 356aab18..0d3f0d7e 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -33,6 +33,7 @@ Debugger::Debugger(shared_ptr console, shared_ptr cpu, shared_ptr< _memoryDumper.reset(new MemoryDumper(_ppu, _memoryManager, _mapper, _codeDataLogger, this)); _memoryAccessCounter.reset(new MemoryAccessCounter(this)); _profiler.reset(new Profiler(this)); + _traceLogger.reset(new TraceLogger(memoryManager, _labelManager)); _stepOut = false; _stepCount = -1; @@ -381,12 +382,8 @@ bool Debugger::PrivateProcessRamOperation(MemoryOperationType type, uint16_t &ad breakDone = SleepUntilResume(); - shared_ptr logger = _traceLogger; - if(logger) { - DebugState state; - GetState(&state); - logger->Log(state, _disassembler->GetDisassemblyInfo(absoluteAddr, absoluteRamAddr, addr)); - } + GetState(&_debugState, false); + _traceLogger->Log(_debugState.CPU, _debugState.PPU, _disassembler->GetDisassemblyInfo(absoluteAddr, absoluteRamAddr, addr)); } else { _profiler->ProcessCycle(); } @@ -623,17 +620,6 @@ void Debugger::SetNextStatement(uint16_t addr) } } -void Debugger::StartTraceLogger(TraceLoggerOptions options) -{ - string traceFilepath = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), "Trace - " + FolderUtilities::GetFilename(_romName, false) + ".log"); - _traceLogger.reset(new TraceLogger(traceFilepath, _memoryManager, options)); -} - -void Debugger::StopTraceLogger() -{ - _traceLogger.reset(); -} - void Debugger::ProcessPpuCycle() { if(Debugger::Instance) { @@ -682,6 +668,11 @@ void Debugger::GetFunctionEntryPoints(int32_t* entryPoints) entryPoints[i] = -1; } +shared_ptr Debugger::GetTraceLogger() +{ + return _traceLogger; +} + shared_ptr Debugger::GetMemoryDumper() { return _memoryDumper; diff --git a/Core/Debugger.h b/Core/Debugger.h index 2cfdcd64..a95eb4c4 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -155,7 +155,7 @@ public: void StopTraceLogger(); shared_ptr GetProfiler(); - + shared_ptr GetTraceLogger(); shared_ptr GetMemoryDumper(); shared_ptr GetMemoryAccessCounter(); diff --git a/Core/Disassembler.cpp b/Core/Disassembler.cpp index 3aabcf16..866b5109 100644 --- a/Core/Disassembler.cpp +++ b/Core/Disassembler.cpp @@ -78,9 +78,9 @@ void Disassembler::BuildOpCodeTables(bool useLowerCase) if(useLowerCase) { string name = opName[i]; std::transform(name.begin(), name.end(), name.begin(), ::tolower); - DisassemblyInfo::OPName[i] = name; + DisassemblyInfo::OPName[i] = name + " "; } else { - DisassemblyInfo::OPName[i] = opName[i]; + DisassemblyInfo::OPName[i] = opName[i] + " "; } DisassemblyInfo::OPMode[i] = opMode[i]; @@ -341,8 +341,11 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo output += commentLines; output += labelLine; - string effectiveAddress = showEffectiveAddresses ? info->GetEffectiveAddressString(cpuState, memoryManager, labelManager) : ""; - output += GetLine(" " + info->ToString(memoryAddr, memoryManager, labelManager), commentString, memoryAddr, source != _internalRam ? addr : -1, info->GetByteCode(), effectiveAddress, speculativeCode); + char* effectiveAddress = info->GetEffectiveAddressString(cpuState, memoryManager.get(), labelManager.get()); + if(!effectiveAddress) { + effectiveAddress = ""; + } + output += GetLine(" " + string(info->ToString(memoryAddr, memoryManager.get(), labelManager.get())), commentString, memoryAddr, source != _internalRam ? addr : -1, info->GetByteCode(), effectiveAddress, speculativeCode); if(info->IsSubExitPoint()) { output += GetLine("__sub end__") + GetLine(); diff --git a/Core/DisassemblyInfo.cpp b/Core/DisassemblyInfo.cpp index bc74f82d..9b9aa91a 100644 --- a/Core/DisassemblyInfo.cpp +++ b/Core/DisassemblyInfo.cpp @@ -8,69 +8,112 @@ string DisassemblyInfo::OPName[256]; AddrMode DisassemblyInfo::OPMode[256]; uint32_t DisassemblyInfo::OPSize[256]; -string DisassemblyInfo::ToString(uint32_t memoryAddr, shared_ptr memoryManager, shared_ptr labelManager) +static const char* hexTable[256] = { + "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", + "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", + "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", + "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", + "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", + "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F", + "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F", + "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", + "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F", + "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", + "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", + "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", + "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF", + "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF" +}; + +char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager) { - string out; - out.reserve(25); + uint16_t length; + return ToString(memoryAddr, memoryManager, labelManager, length); +} +char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t &length) +{ uint8_t opCode = *_opPointer; - - if(DisassemblyInfo::OPName[opCode].empty()) { - out = "invalid opcode"; - } else { - out = DisassemblyInfo::OPName[opCode]; - } + memcpy(_toStringBuffer, DisassemblyInfo::OPName[opCode].c_str(), 4); + + length = 4; + uint16_t* ptrPos = &length; uint16_t opAddr = GetOpAddr(memoryAddr); - string operandValue; - if(labelManager && _opMode != AddrMode::Imm) { - operandValue = labelManager->GetLabel(opAddr, true); - } - - if(operandValue.empty()) { - if(_opSize == 2 && _opMode != AddrMode::Rel) { - operandValue += "$" + HexUtilities::ToHex((uint8_t)opAddr); - } else { - operandValue += "$" + HexUtilities::ToHex((uint16_t)opAddr); - } + uint8_t operandLength = 0; + char operandBuffer[7]; + char* opBuffer = operandBuffer; + + operandBuffer[0] = '$'; + if(_opSize == 2 && _opMode != AddrMode::Rel) { + memcpy(operandBuffer + 1, hexTable[opAddr], 2); + operandLength = 3; + } else { + memcpy(operandBuffer + 1, hexTable[opAddr >> 8], 2); + memcpy(operandBuffer + 3, hexTable[opAddr & 0xFF], 2); + operandLength = 5; } - out += " "; + auto writeChar = [=](char c) -> void { + _toStringBuffer[(*ptrPos)++] = c; + }; + + auto copyOperand = [=]() -> void { + if(labelManager && _opMode != AddrMode::Imm) { + string label = labelManager->GetLabel(opAddr, true); + if(!label.empty()) { + memcpy(_toStringBuffer + (*ptrPos), label.c_str(), label.size()); + (*ptrPos) += (uint16_t)label.size(); + return; + } + } + memcpy(_toStringBuffer + (*ptrPos), opBuffer, operandLength); + (*ptrPos) += operandLength; + }; switch(_opMode) { - case AddrMode::Acc: out += "A"; break; - case AddrMode::Imm: out += "#" + operandValue; break; - case AddrMode::Ind: out += "(" + operandValue + ")"; break; - case AddrMode::IndX: out += "(" + operandValue + ",X)"; break; + case AddrMode::Acc: writeChar('A'); break; + case AddrMode::Imm: writeChar('#'); copyOperand(); break; + case AddrMode::Ind: writeChar('('); copyOperand(); writeChar(')'); break; + case AddrMode::IndX: writeChar('('); copyOperand(); memcpy(_toStringBuffer + length, ",X)", 3); length += 3; break; case AddrMode::IndY: case AddrMode::IndYW: - out += "(" + operandValue + "),Y"; + writeChar('('); + copyOperand(); + memcpy(_toStringBuffer + length, "),Y", 3); + length += 3; break; case AddrMode::Abs: case AddrMode::Rel: case AddrMode::Zero: - out += operandValue; + copyOperand(); break; case AddrMode::AbsX: case AddrMode::AbsXW: case AddrMode::ZeroX: - out += operandValue + ",X"; + copyOperand(); + memcpy(_toStringBuffer + length, ",X", 2); + length += 2; break; case AddrMode::AbsY: case AddrMode::AbsYW: case AddrMode::ZeroY: - out += operandValue + ",Y"; + copyOperand(); + memcpy(_toStringBuffer + length, ",Y", 2); + length += 2; break; default: break; } - - return out; + _toStringBuffer[length] = 0; + return _toStringBuffer; } uint16_t DisassemblyInfo::GetOpAddr(uint16_t memoryAddr) @@ -105,32 +148,53 @@ void DisassemblyInfo::SetSubEntryPoint() _isSubEntryPoint = true; } -string DisassemblyInfo::GetEffectiveAddressString(State& cpuState, shared_ptr memoryManager, shared_ptr labelManager) +char* DisassemblyInfo::GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager) { - int32_t effectiveAddress = GetEffectiveAddress(cpuState, memoryManager); - if(effectiveAddress < 0) { - return ""; + uint16_t length; + return GetEffectiveAddressString(cpuState, memoryManager, labelManager, length); +} + +char* DisassemblyInfo::GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t& length) +{ + if(_opMode <= AddrMode::Abs) { + length = 0; + return nullptr; } else { + int32_t effectiveAddress = GetEffectiveAddress(cpuState, memoryManager); + + _effectiveAddressBuffer[0] = ' '; + _effectiveAddressBuffer[1] = '@'; + _effectiveAddressBuffer[2] = ' '; + if(labelManager) { string label = labelManager->GetLabel(effectiveAddress, true); if(!label.empty()) { - return " @ " + label; + memcpy(_effectiveAddressBuffer + 3, label.c_str(), label.size()); + length = (uint16_t)label.size() + 3; + _effectiveAddressBuffer[length] = 0; + + return _effectiveAddressBuffer; } } - - string output; - output = " @ $"; + + _effectiveAddressBuffer[3] = '$'; + if(_opMode == AddrMode::ZeroX || _opMode == AddrMode::ZeroY) { - output += HexUtilities::ToHex((uint8_t)effectiveAddress); + memcpy(_effectiveAddressBuffer + 4, hexTable[effectiveAddress], 2); + _effectiveAddressBuffer[6] = 0; + length = 6; } else { - output += HexUtilities::ToHex((uint16_t)effectiveAddress); + memcpy(_effectiveAddressBuffer + 4, hexTable[effectiveAddress >> 8], 2); + memcpy(_effectiveAddressBuffer + 6, hexTable[effectiveAddress & 0xFF], 2); + _effectiveAddressBuffer[8] = 0; + length = 8; } - - return output; + + return _effectiveAddressBuffer; } } -int32_t DisassemblyInfo::GetEffectiveAddress(State& cpuState, shared_ptr memoryManager) +int32_t DisassemblyInfo::GetEffectiveAddress(State& cpuState, MemoryManager* memoryManager) { switch(_opMode) { case AddrMode::ZeroX: return (uint8_t)(*(_opPointer + 1) + cpuState.X); break; diff --git a/Core/DisassemblyInfo.h b/Core/DisassemblyInfo.h index 2a8928c3..7f89f700 100644 --- a/Core/DisassemblyInfo.h +++ b/Core/DisassemblyInfo.h @@ -20,15 +20,21 @@ private: uint32_t _opSize = 0; AddrMode _opMode; + char _toStringBuffer[1000]; + char _effectiveAddressBuffer[1000]; + public: DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint); void SetSubEntryPoint(); - int32_t GetEffectiveAddress(State& cpuState, shared_ptr memoryManager); - string GetEffectiveAddressString(State& cpuState, shared_ptr memoryManager, shared_ptr labelManager); + int32_t GetEffectiveAddress(State& cpuState, MemoryManager* memoryManager); + + char* GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager); + char* GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t &length); - string ToString(uint32_t memoryAddr, shared_ptr memoryManager, shared_ptr labelManager); + char* ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager); + char* ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t &length); string GetByteCode(); uint32_t GetSize(); uint16_t GetOpAddr(uint16_t memoryAddr); diff --git a/Core/LabelManager.cpp b/Core/LabelManager.cpp index e58511e5..8b2eedfe 100644 --- a/Core/LabelManager.cpp +++ b/Core/LabelManager.cpp @@ -24,6 +24,10 @@ void LabelManager::SetLabel(uint32_t address, AddressType addressType, string la _codeLabels.erase(address); if(!label.empty()) { + if(label.size() > 400) { + //Restrict labels to 400 bytes + label = label.substr(0, 400); + } _codeLabels.emplace(address, label); _codeLabelReverseLookup.emplace(label, address); } diff --git a/Core/PPU.h b/Core/PPU.h index 17aca67b..b0990f65 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -4,6 +4,7 @@ #include "Snapshotable.h" #include "MemoryManager.h" #include "EmulationSettings.h" +#include "PpuState.h" enum class NesModel; @@ -20,75 +21,6 @@ enum PPURegisters SpriteDMA = 0x4014, }; -struct PPUControlFlags -{ - bool VerticalWrite; - uint16_t SpritePatternAddr; - uint16_t BackgroundPatternAddr; - bool LargeSprites; - bool VBlank; - - bool Grayscale; - bool BackgroundMask; - bool SpriteMask; - bool BackgroundEnabled; - bool SpritesEnabled; - bool IntensifyRed; - bool IntensifyGreen; - bool IntensifyBlue; -}; - -struct PPUStatusFlags -{ - bool SpriteOverflow; - bool Sprite0Hit; - bool VerticalBlank; -}; - -struct PPUState -{ - uint8_t Control; - uint8_t Mask; - uint8_t Status; - uint32_t SpriteRamAddr; - uint16_t VideoRamAddr; - uint8_t XScroll; - uint16_t TmpVideoRamAddr; - bool WriteToggle; - - uint16_t HighBitShift; - uint16_t LowBitShift; -}; - -struct TileInfo -{ - uint8_t LowByte; - uint8_t HighByte; - uint32_t PaletteOffset; - - uint16_t TileAddr; //used by HD ppu - uint8_t OffsetY; //used by HD ppu -}; - -struct SpriteInfo : TileInfo -{ - bool HorizontalMirror; - bool BackgroundPriority; - uint8_t SpriteX; - - bool VerticalMirror; //used by HD ppu -}; - -struct PPUDebugState -{ - PPUControlFlags ControlFlags; - PPUStatusFlags StatusFlags; - PPUState State; - int32_t Scanline; - uint32_t Cycle; - uint32_t FrameCount; -}; - class PPU : public IMemoryHandler, public Snapshotable { protected: diff --git a/Core/PpuState.h b/Core/PpuState.h new file mode 100644 index 00000000..e1e7bc14 --- /dev/null +++ b/Core/PpuState.h @@ -0,0 +1,71 @@ +#pragma once +#include "stdafx.h" + +struct PPUControlFlags +{ + bool VerticalWrite; + uint16_t SpritePatternAddr; + uint16_t BackgroundPatternAddr; + bool LargeSprites; + bool VBlank; + + bool Grayscale; + bool BackgroundMask; + bool SpriteMask; + bool BackgroundEnabled; + bool SpritesEnabled; + bool IntensifyRed; + bool IntensifyGreen; + bool IntensifyBlue; +}; + +struct PPUStatusFlags +{ + bool SpriteOverflow; + bool Sprite0Hit; + bool VerticalBlank; +}; + +struct PPUState +{ + uint8_t Control; + uint8_t Mask; + uint8_t Status; + uint32_t SpriteRamAddr; + uint16_t VideoRamAddr; + uint8_t XScroll; + uint16_t TmpVideoRamAddr; + bool WriteToggle; + + uint16_t HighBitShift; + uint16_t LowBitShift; +}; + +struct TileInfo +{ + uint8_t LowByte; + uint8_t HighByte; + uint32_t PaletteOffset; + + uint16_t TileAddr; //used by HD ppu + uint8_t OffsetY; //used by HD ppu +}; + +struct SpriteInfo : TileInfo +{ + bool HorizontalMirror; + bool BackgroundPriority; + uint8_t SpriteX; + + bool VerticalMirror; //used by HD ppu +}; + +struct PPUDebugState +{ + PPUControlFlags ControlFlags; + PPUStatusFlags StatusFlags; + PPUState State; + int32_t Scanline; + uint32_t Cycle; + uint32_t FrameCount; +}; \ No newline at end of file diff --git a/Core/TraceLogger.cpp b/Core/TraceLogger.cpp index d31e9c3a..1292f7df 100644 --- a/Core/TraceLogger.cpp +++ b/Core/TraceLogger.cpp @@ -4,38 +4,58 @@ #include "DebugState.h" #include "Console.h" #include "MemoryManager.h" +#include "LabelManager.h" #include "../Utilities/HexUtilities.h" +#include "../Utilities/FolderUtilities.h" TraceLogger *TraceLogger::_instance = nullptr; -TraceLogger::TraceLogger(string outputFilepath, shared_ptr memoryManager, TraceLoggerOptions options) +TraceLogger::TraceLogger(shared_ptr memoryManager, shared_ptr labelManager) { _memoryManager = memoryManager; - _outputFile.open(outputFilepath, ios::out | ios::binary); - _options = options; - _firstLine = true; + _labelManager = labelManager; _instance = this; + _currentPos = 0; + _logToFile = false; } TraceLogger::~TraceLogger() { - Console::Pause(); - if(_instance == this) { - _instance = nullptr; - } - - if(_outputFile) { - if(!_outputBuffer.empty()) { - _outputFile << _outputBuffer; - } - _outputFile.close(); - } - Console::Resume(); + StopLogging(); + _instance = nullptr; } +void TraceLogger::SetOptions(TraceLoggerOptions options) +{ + _options = options; +} + +void TraceLogger::StartLogging(string filename) +{ + _outputFile.open(filename, ios::out | ios::binary); + _logToFile = true; + _firstLine = true; +} + +void TraceLogger::StopLogging() +{ + if(_logToFile) { + Console::Pause(); + if(_outputFile) { + if(!_outputBuffer.empty()) { + _outputFile << _outputBuffer; + } + _outputFile.close(); + } + Console::Resume(); + _logToFile = false; + } +} + + void TraceLogger::LogStatic(string log) { - if(_instance && _instance->_options.ShowExtraInfo) { + if(_instance && _instance->_logToFile && _instance->_options.ShowExtraInfo && !_instance->_firstLine) { //Flush current buffer _instance->_outputFile << _instance->_outputBuffer; _instance->_outputBuffer.clear(); @@ -44,75 +64,108 @@ void TraceLogger::LogStatic(string log) } } -void TraceLogger::Log(DebugState &state, shared_ptr disassemblyInfo) +void TraceLogger::GetTraceRow(string &output, State &cpuState, PPUDebugState &ppuState, shared_ptr &disassemblyInfo, bool firstLine) +{ + //Roughly adjust PPU cycle & scanline to take into account the PPU already ran 3 cycles by the time we get here + short ppuCycle = (short)ppuState.Cycle - 3; + short scanline = (short)ppuState.Scanline; + if(ppuCycle < 0) { + ppuCycle = 341 + ppuCycle; + scanline--; + if(scanline < -1) { + scanline = EmulationSettings::GetNesModel() == NesModel::NTSC ? 260 : 310; + } + } + + if(!firstLine) { + output += "\n"; + } + + output += HexUtilities::ToHex(cpuState.DebugPC) + " "; + + if(_options.ShowByteCode) { + output += disassemblyInfo->GetByteCode() + std::string(13 - disassemblyInfo->GetByteCode().size(), ' '); + } + + int indentLevel = 0; + if(_options.IndentCode) { + indentLevel = 0xFF - cpuState.SP; + output += std::string(indentLevel, ' '); + } + + uint16_t disassemblyLength = 0; + uint16_t effectiveAddressLength = 0; + LabelManager* labelManager = _options.UseLabels ? _labelManager.get() : nullptr; + char* disassembly = disassemblyInfo->ToString(cpuState.DebugPC, _memoryManager.get(), labelManager, disassemblyLength); + char* effectiveAddress = (_options.ShowEffectiveAddresses ? disassemblyInfo->GetEffectiveAddressString(cpuState, _memoryManager.get(), labelManager, effectiveAddressLength) : nullptr); + + output += disassembly; + if(effectiveAddress) { + output += effectiveAddress; + } + output += std::string(32 - disassemblyLength - effectiveAddressLength, ' '); + + if(_options.ShowRegisters) { + output += " A:" + HexUtilities::ToHex(cpuState.A) + + " X:" + HexUtilities::ToHex(cpuState.X) + + " Y:" + HexUtilities::ToHex(cpuState.Y) + + " P:" + HexUtilities::ToHex(cpuState.PS) + + " SP:" + HexUtilities::ToHex(cpuState.SP); + } + + if(_options.ShowPpuCycles) { + string str = std::to_string(ppuCycle); + output += " CYC:" + std::string(3 - str.size(), ' ') + str; + } + + if(_options.ShowPpuScanline) { + string str = std::to_string(scanline); + output += " SL:" + std::string(3 - str.size(), ' ') + str; + } + + if(_options.ShowPpuFrames) { + output += " FC:" + std::to_string(ppuState.FrameCount); + } + + if(_options.ShowCpuCycles) { + output += " CPU Cycle:" + std::to_string(cpuState.CycleCount); + } +} + +SimpleLock _lock; +void TraceLogger::Log(State &cpuState, PPUDebugState &ppuState, shared_ptr disassemblyInfo) { if(disassemblyInfo) { - State &cpuState = state.CPU; - PPUDebugState &ppuState = state.PPU; - - string disassembly = disassemblyInfo->ToString(cpuState.DebugPC, _memoryManager, nullptr); - - //Roughly adjust PPU cycle & scanline to take into account the PPU already ran 3 cycles by the time we get here - short ppuCycle = (short)ppuState.Cycle - 3; - short scanline = (short)ppuState.Scanline; - if(ppuCycle < 0) { - ppuCycle = 341 + ppuCycle; - scanline--; - if(scanline < -1) { - scanline = EmulationSettings::GetNesModel() == NesModel::NTSC ? 260 : 310; + auto lock = _lock.AcquireSafe(); + _disassemblyCache[_currentPos] = disassemblyInfo; + _cpuStateCache[_currentPos] = cpuState; + _ppuStateCache[_currentPos] = ppuState; + _currentPos = (_currentPos + 1) % ExecutionLogSize; + + if(_logToFile) { + GetTraceRow(_outputBuffer, cpuState, ppuState, disassemblyInfo, _firstLine); + if(_outputBuffer.size() > 32768) { + _outputFile << _outputBuffer; + _outputBuffer.clear(); } + + _firstLine = false; } - - if(!_firstLine) { - _outputBuffer += "\n"; - } - - _outputBuffer += HexUtilities::ToHex(cpuState.DebugPC) + " "; - - if(_options.ShowByteCode) { - _outputBuffer += disassemblyInfo->GetByteCode() + std::string(13 - disassemblyInfo->GetByteCode().size(), ' '); - } - - int indentLevel = 0; - if(_options.IndentCode) { - indentLevel = 0xFF - state.CPU.SP; - _outputBuffer += std::string(indentLevel, ' '); - } - - string codeString = disassembly + (_options.ShowEffectiveAddresses ? disassemblyInfo->GetEffectiveAddressString(state.CPU, _memoryManager, nullptr) : ""); - _outputBuffer += codeString + std::string(32 - codeString.size(), ' '); - - if(_options.ShowRegisters) { - _outputBuffer += " A:" + HexUtilities::ToHex(cpuState.A) + - " X:" + HexUtilities::ToHex(cpuState.X) + - " Y:" + HexUtilities::ToHex(cpuState.Y) + - " P:" + HexUtilities::ToHex(cpuState.PS) + - " SP:" + HexUtilities::ToHex(cpuState.SP); - } - - if(_options.ShowPpuCycles) { - string str = std::to_string(ppuCycle); - _outputBuffer += " CYC:" + std::string(3 - str.size(), ' ') + str; - } - - if(_options.ShowPpuScanline) { - string str = std::to_string(scanline); - _outputBuffer += " SL:" + std::string(3 - str.size(), ' ') + str; - } - - if(_options.ShowPpuFrames) { - _outputBuffer += " FC:" + std::to_string(ppuState.FrameCount); - } - - if(_options.ShowCpuCycles) { - _outputBuffer += " CPU Cycle:" + std::to_string(cpuState.CycleCount); - } - - if(_outputBuffer.size() > 32768) { - _outputFile << _outputBuffer; - _outputBuffer.clear(); - } - - _firstLine = false; } +} + +const char* TraceLogger::GetExecutionTrace(uint32_t lineCount) +{ + _executionTrace.clear(); + auto lock = _lock.AcquireSafe(); + int startPos = _currentPos + ExecutionLogSize - lineCount; + bool firstLine = true; + for(int i = 0; i < (int)lineCount; i++) { + int index = (startPos + i) % ExecutionLogSize; + if(_disassemblyCache[index]) { + GetTraceRow(_executionTrace, _cpuStateCache[index], _ppuStateCache[index], _disassemblyCache[index], firstLine); + firstLine = false; + } + } + return _executionTrace.c_str(); } \ No newline at end of file diff --git a/Core/TraceLogger.h b/Core/TraceLogger.h index ac18e1f6..30b4792a 100644 --- a/Core/TraceLogger.h +++ b/Core/TraceLogger.h @@ -1,9 +1,11 @@ #pragma once #include "stdafx.h" +#include "CpuState.h" +#include "PpuState.h" class DisassemblyInfo; class MemoryManager; -struct DebugState; +class LabelManager; struct TraceLoggerOptions { @@ -16,6 +18,7 @@ struct TraceLoggerOptions bool ShowExtraInfo; bool IndentCode; bool ShowEffectiveAddresses; + bool UseLabels; }; class TraceLogger @@ -28,12 +31,30 @@ private: ofstream _outputFile; bool _firstLine; shared_ptr _memoryManager; + shared_ptr _labelManager; + + constexpr static int ExecutionLogSize = 30000; + bool _logToFile; + uint16_t _currentPos; + State _cpuStateCache[ExecutionLogSize] = {}; + PPUDebugState _ppuStateCache[ExecutionLogSize] = {}; + int _memoryAddrCache[ExecutionLogSize] = {}; + shared_ptr _disassemblyCache[ExecutionLogSize] = {}; + + string _executionTrace; public: - TraceLogger(string outputFilepath, shared_ptr memoryManager, TraceLoggerOptions options); + TraceLogger(shared_ptr memoryManager, shared_ptr labelManager); ~TraceLogger(); - void Log(DebugState &state, shared_ptr disassemblyInfo); + void Log(State &cpuState, PPUDebugState &ppuState, shared_ptr disassemblyInfo); + void SetOptions(TraceLoggerOptions options); + void StartLogging(string filename); + void StopLogging(); + + void GetTraceRow(string &output, State &cpuState, PPUDebugState &ppuState, shared_ptr &disassemblyInfo, bool firstLine); + const char* GetExecutionTrace(uint32_t lineCount); static void LogStatic(string log); + }; \ No newline at end of file diff --git a/GUI.NET/Config/DebugInfo.cs b/GUI.NET/Config/DebugInfo.cs index 023c1757..641af507 100644 --- a/GUI.NET/Config/DebugInfo.cs +++ b/GUI.NET/Config/DebugInfo.cs @@ -144,6 +144,19 @@ namespace Mesen.GUI.Config public bool BreakOnOpen = true; public bool BreakOnReset = true; + public bool TraceAutoRefresh = true; + public int TraceLineCount = 1000; + public bool TraceIndentCode = false; + public bool TraceShowByteCode = true; + public bool TraceShowCpuCycles = false; + public bool TraceShowEffectiveAddresses = true; + public bool TraceShowExtraInfo = true; + public bool TraceShowFrameCount = false; + public bool TraceShowPpuCycles = true; + public bool TraceShowPpuScanline = true; + public bool TraceShowRegisters = true; + internal bool TraceUseLabels; + public DebugInfo() { LeftView = new DebugViewInfo(); diff --git a/GUI.NET/Debugger/frmTraceLogger.Designer.cs b/GUI.NET/Debugger/frmTraceLogger.Designer.cs index d0ccf1e0..1a6eb37b 100644 --- a/GUI.NET/Debugger/frmTraceLogger.Designer.cs +++ b/GUI.NET/Debugger/frmTraceLogger.Designer.cs @@ -27,24 +27,43 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.btnOpenTrace = new System.Windows.Forms.Button(); this.btnStartLogging = new System.Windows.Forms.Button(); this.btnStopLogging = new System.Windows.Forms.Button(); this.grpLogOptions = new System.Windows.Forms.GroupBox(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); - this.chkShowCpuCycles = new System.Windows.Forms.CheckBox(); + this.chkShowEffectiveAddresses = new System.Windows.Forms.CheckBox(); this.chkShowPpuCycles = new System.Windows.Forms.CheckBox(); this.chkShowRegisters = new System.Windows.Forms.CheckBox(); - this.chkShowPpuScanline = new System.Windows.Forms.CheckBox(); - this.chkShowFrameCount = new System.Windows.Forms.CheckBox(); this.chkShowExtraInfo = new System.Windows.Forms.CheckBox(); - this.chkShowByteCode = new System.Windows.Forms.CheckBox(); this.chkIndentCode = new System.Windows.Forms.CheckBox(); - this.chkShowEffectiveAddresses = new System.Windows.Forms.CheckBox(); + this.chkShowByteCode = new System.Windows.Forms.CheckBox(); + this.chkShowFrameCount = new System.Windows.Forms.CheckBox(); + this.chkShowPpuScanline = new System.Windows.Forms.CheckBox(); + this.chkShowCpuCycles = new System.Windows.Forms.CheckBox(); + this.chkUseLabels = new System.Windows.Forms.CheckBox(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.grpExecutionLog = new System.Windows.Forms.GroupBox(); + this.txtTraceLog = new System.Windows.Forms.TextBox(); + this.tmrUpdateLog = new System.Windows.Forms.Timer(this.components); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.showToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.logLinesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu100Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu1000Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu10000Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu30000Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuAutoRefresh = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuRefresh = new System.Windows.Forms.ToolStripMenuItem(); this.tableLayoutPanel1.SuspendLayout(); this.grpLogOptions.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + this.grpExecutionLog.SuspendLayout(); + this.menuStrip1.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel1 @@ -57,23 +76,21 @@ this.tableLayoutPanel1.Controls.Add(this.btnStartLogging, 0, 0); this.tableLayoutPanel1.Controls.Add(this.btnStopLogging, 1, 0); this.tableLayoutPanel1.Controls.Add(this.grpLogOptions, 0, 1); - this.tableLayoutPanel1.Controls.Add(this.chkIndentCode, 0, 2); - this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 314); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 3; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(339, 193); + this.tableLayoutPanel1.Size = new System.Drawing.Size(442, 206); this.tableLayoutPanel1.TabIndex = 0; // // btnOpenTrace // this.btnOpenTrace.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnOpenTrace.Enabled = false; - this.btnOpenTrace.Location = new System.Drawing.Point(241, 3); + this.btnOpenTrace.Location = new System.Drawing.Point(344, 3); this.btnOpenTrace.Name = "btnOpenTrace"; this.btnOpenTrace.Size = new System.Drawing.Size(95, 23); this.btnOpenTrace.TabIndex = 2; @@ -107,52 +124,55 @@ // this.tableLayoutPanel1.SetColumnSpan(this.grpLogOptions, 3); this.grpLogOptions.Controls.Add(this.tableLayoutPanel2); - this.grpLogOptions.Dock = System.Windows.Forms.DockStyle.Fill; this.grpLogOptions.Location = new System.Drawing.Point(3, 32); this.grpLogOptions.Name = "grpLogOptions"; - this.grpLogOptions.Size = new System.Drawing.Size(333, 132); + this.grpLogOptions.Size = new System.Drawing.Size(436, 168); this.grpLogOptions.TabIndex = 3; this.grpLogOptions.TabStop = false; - this.grpLogOptions.Text = "Log Contents"; + this.grpLogOptions.Text = "Log Options"; // // tableLayoutPanel2 // this.tableLayoutPanel2.ColumnCount = 3; - this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33332F)); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33334F)); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33334F)); this.tableLayoutPanel2.Controls.Add(this.chkShowEffectiveAddresses, 0, 3); - this.tableLayoutPanel2.Controls.Add(this.chkShowCpuCycles, 1, 0); this.tableLayoutPanel2.Controls.Add(this.chkShowPpuCycles, 0, 1); this.tableLayoutPanel2.Controls.Add(this.chkShowRegisters, 0, 0); - this.tableLayoutPanel2.Controls.Add(this.chkShowPpuScanline, 1, 1); - this.tableLayoutPanel2.Controls.Add(this.chkShowFrameCount, 2, 1); this.tableLayoutPanel2.Controls.Add(this.chkShowExtraInfo, 0, 4); + this.tableLayoutPanel2.Controls.Add(this.chkIndentCode, 0, 6); this.tableLayoutPanel2.Controls.Add(this.chkShowByteCode, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.chkShowFrameCount, 2, 2); + this.tableLayoutPanel2.Controls.Add(this.chkShowPpuScanline, 2, 1); + this.tableLayoutPanel2.Controls.Add(this.chkShowCpuCycles, 2, 0); + this.tableLayoutPanel2.Controls.Add(this.chkUseLabels, 2, 6); this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 16); this.tableLayoutPanel2.Name = "tableLayoutPanel2"; - this.tableLayoutPanel2.RowCount = 6; + this.tableLayoutPanel2.RowCount = 7; this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel2.Size = new System.Drawing.Size(327, 113); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.Size = new System.Drawing.Size(430, 149); this.tableLayoutPanel2.TabIndex = 0; // - // chkShowCpuCycles + // chkShowEffectiveAddresses // - this.chkShowCpuCycles.AutoSize = true; - this.chkShowCpuCycles.Checked = true; - this.chkShowCpuCycles.CheckState = System.Windows.Forms.CheckState.Checked; - this.chkShowCpuCycles.Location = new System.Drawing.Point(111, 3); - this.chkShowCpuCycles.Name = "chkShowCpuCycles"; - this.chkShowCpuCycles.Size = new System.Drawing.Size(82, 17); - this.chkShowCpuCycles.TabIndex = 3; - this.chkShowCpuCycles.Text = "CPU Cycles"; - this.chkShowCpuCycles.UseVisualStyleBackColor = true; + this.chkShowEffectiveAddresses.AutoSize = true; + this.chkShowEffectiveAddresses.Checked = true; + this.chkShowEffectiveAddresses.CheckState = System.Windows.Forms.CheckState.Checked; + this.tableLayoutPanel2.SetColumnSpan(this.chkShowEffectiveAddresses, 2); + this.chkShowEffectiveAddresses.Location = new System.Drawing.Point(3, 72); + this.chkShowEffectiveAddresses.Name = "chkShowEffectiveAddresses"; + this.chkShowEffectiveAddresses.Size = new System.Drawing.Size(150, 17); + this.chkShowEffectiveAddresses.TabIndex = 10; + this.chkShowEffectiveAddresses.Text = "Show Effective Addresses"; + this.chkShowEffectiveAddresses.UseVisualStyleBackColor = true; // // chkShowPpuCycles // @@ -178,28 +198,6 @@ this.chkShowRegisters.Text = "Registers"; this.chkShowRegisters.UseVisualStyleBackColor = true; // - // chkShowPpuScanline - // - this.chkShowPpuScanline.AutoSize = true; - this.chkShowPpuScanline.Checked = true; - this.chkShowPpuScanline.CheckState = System.Windows.Forms.CheckState.Checked; - this.chkShowPpuScanline.Location = new System.Drawing.Point(111, 26); - this.chkShowPpuScanline.Name = "chkShowPpuScanline"; - this.chkShowPpuScanline.Size = new System.Drawing.Size(92, 17); - this.chkShowPpuScanline.TabIndex = 6; - this.chkShowPpuScanline.Text = "PPU Scanline"; - this.chkShowPpuScanline.UseVisualStyleBackColor = true; - // - // chkShowFrameCount - // - this.chkShowFrameCount.AutoSize = true; - this.chkShowFrameCount.Location = new System.Drawing.Point(220, 26); - this.chkShowFrameCount.Name = "chkShowFrameCount"; - this.chkShowFrameCount.Size = new System.Drawing.Size(86, 17); - this.chkShowFrameCount.TabIndex = 7; - this.chkShowFrameCount.Text = "Frame Count"; - this.chkShowFrameCount.UseVisualStyleBackColor = true; - // // chkShowExtraInfo // this.chkShowExtraInfo.AutoSize = true; @@ -213,6 +211,17 @@ this.chkShowExtraInfo.Text = "Additional information (IRQ, NMI, etc.)"; this.chkShowExtraInfo.UseVisualStyleBackColor = true; // + // chkIndentCode + // + this.chkIndentCode.AutoSize = true; + this.tableLayoutPanel2.SetColumnSpan(this.chkIndentCode, 2); + this.chkIndentCode.Location = new System.Drawing.Point(3, 129); + this.chkIndentCode.Name = "chkIndentCode"; + this.chkIndentCode.Size = new System.Drawing.Size(194, 17); + this.chkIndentCode.TabIndex = 8; + this.chkIndentCode.Text = "Indent code based on stack pointer"; + this.chkIndentCode.UseVisualStyleBackColor = true; + // // chkShowByteCode // this.chkShowByteCode.AutoSize = true; @@ -225,48 +234,201 @@ this.chkShowByteCode.Text = "Byte Code"; this.chkShowByteCode.UseVisualStyleBackColor = true; // - // chkIndentCode + // chkShowFrameCount // - this.chkIndentCode.AutoSize = true; - this.tableLayoutPanel1.SetColumnSpan(this.chkIndentCode, 3); - this.chkIndentCode.Location = new System.Drawing.Point(3, 170); - this.chkIndentCode.Name = "chkIndentCode"; - this.chkIndentCode.Size = new System.Drawing.Size(194, 17); - this.chkIndentCode.TabIndex = 8; - this.chkIndentCode.Text = "Indent code based on stack pointer"; - this.chkIndentCode.UseVisualStyleBackColor = true; + this.chkShowFrameCount.AutoSize = true; + this.chkShowFrameCount.Location = new System.Drawing.Point(289, 49); + this.chkShowFrameCount.Name = "chkShowFrameCount"; + this.chkShowFrameCount.Size = new System.Drawing.Size(86, 17); + this.chkShowFrameCount.TabIndex = 7; + this.chkShowFrameCount.Text = "Frame Count"; + this.chkShowFrameCount.UseVisualStyleBackColor = true; // - // chkShowEffectiveAddresses + // chkShowPpuScanline // - this.chkShowEffectiveAddresses.AutoSize = true; - this.chkShowEffectiveAddresses.Checked = true; - this.chkShowEffectiveAddresses.CheckState = System.Windows.Forms.CheckState.Checked; - this.tableLayoutPanel2.SetColumnSpan(this.chkShowEffectiveAddresses, 2); - this.chkShowEffectiveAddresses.Location = new System.Drawing.Point(3, 72); - this.chkShowEffectiveAddresses.Name = "chkShowEffectiveAddresses"; - this.chkShowEffectiveAddresses.Size = new System.Drawing.Size(150, 17); - this.chkShowEffectiveAddresses.TabIndex = 10; - this.chkShowEffectiveAddresses.Text = "Show Effective Addresses"; - this.chkShowEffectiveAddresses.UseVisualStyleBackColor = true; + this.chkShowPpuScanline.AutoSize = true; + this.chkShowPpuScanline.Checked = true; + this.chkShowPpuScanline.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkShowPpuScanline.Location = new System.Drawing.Point(289, 26); + this.chkShowPpuScanline.Name = "chkShowPpuScanline"; + this.chkShowPpuScanline.Size = new System.Drawing.Size(92, 17); + this.chkShowPpuScanline.TabIndex = 6; + this.chkShowPpuScanline.Text = "PPU Scanline"; + this.chkShowPpuScanline.UseVisualStyleBackColor = true; + // + // chkShowCpuCycles + // + this.chkShowCpuCycles.AutoSize = true; + this.chkShowCpuCycles.Checked = true; + this.chkShowCpuCycles.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkShowCpuCycles.Location = new System.Drawing.Point(289, 3); + this.chkShowCpuCycles.Name = "chkShowCpuCycles"; + this.chkShowCpuCycles.Size = new System.Drawing.Size(82, 17); + this.chkShowCpuCycles.TabIndex = 3; + this.chkShowCpuCycles.Text = "CPU Cycles"; + this.chkShowCpuCycles.UseVisualStyleBackColor = true; + // + // chkUseLabels + // + this.chkUseLabels.AutoSize = true; + this.chkUseLabels.Location = new System.Drawing.Point(289, 129); + this.chkUseLabels.Name = "chkUseLabels"; + this.chkUseLabels.Size = new System.Drawing.Size(79, 17); + this.chkUseLabels.TabIndex = 11; + this.chkUseLabels.Text = "Use Labels"; + this.chkUseLabels.UseVisualStyleBackColor = true; + // + // tableLayoutPanel3 + // + this.tableLayoutPanel3.ColumnCount = 2; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.Controls.Add(this.tableLayoutPanel1, 0, 1); + this.tableLayoutPanel3.Controls.Add(this.grpExecutionLog, 0, 0); + this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel3.Location = new System.Drawing.Point(0, 24); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + this.tableLayoutPanel3.RowCount = 2; + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel3.Size = new System.Drawing.Size(808, 523); + this.tableLayoutPanel3.TabIndex = 1; + // + // grpExecutionLog + // + this.tableLayoutPanel3.SetColumnSpan(this.grpExecutionLog, 2); + this.grpExecutionLog.Controls.Add(this.txtTraceLog); + this.grpExecutionLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpExecutionLog.Location = new System.Drawing.Point(3, 3); + this.grpExecutionLog.Name = "grpExecutionLog"; + this.grpExecutionLog.Size = new System.Drawing.Size(802, 305); + this.grpExecutionLog.TabIndex = 2; + this.grpExecutionLog.TabStop = false; + this.grpExecutionLog.Text = "Execution Log"; + // + // txtTraceLog + // + this.txtTraceLog.BackColor = System.Drawing.SystemColors.Window; + this.txtTraceLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtTraceLog.Location = new System.Drawing.Point(3, 16); + this.txtTraceLog.Multiline = true; + this.txtTraceLog.Name = "txtTraceLog"; + this.txtTraceLog.ReadOnly = true; + this.txtTraceLog.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtTraceLog.Size = new System.Drawing.Size(796, 286); + this.txtTraceLog.TabIndex = 1; + // + // tmrUpdateLog + // + this.tmrUpdateLog.Interval = 150; + this.tmrUpdateLog.Tick += new System.EventHandler(this.tmrUpdateLog_Tick); + // + // menuStrip1 + // + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.showToolStripMenuItem}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(808, 24); + this.menuStrip1.TabIndex = 2; + this.menuStrip1.Text = "menuStrip1"; + // + // showToolStripMenuItem + // + this.showToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.logLinesToolStripMenuItem, + this.mnuAutoRefresh, + this.toolStripMenuItem1, + this.mnuRefresh}); + this.showToolStripMenuItem.Name = "showToolStripMenuItem"; + this.showToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.showToolStripMenuItem.Text = "View"; + // + // logLinesToolStripMenuItem + // + this.logLinesToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnu100Lines, + this.mnu1000Lines, + this.mnu10000Lines, + this.mnu30000Lines}); + this.logLinesToolStripMenuItem.Name = "logLinesToolStripMenuItem"; + this.logLinesToolStripMenuItem.Size = new System.Drawing.Size(141, 22); + this.logLinesToolStripMenuItem.Text = "Line Count"; + // + // mnu100Lines + // + this.mnu100Lines.Name = "mnu100Lines"; + this.mnu100Lines.Size = new System.Drawing.Size(104, 22); + this.mnu100Lines.Text = "100"; + this.mnu100Lines.Click += new System.EventHandler(this.mnu100Lines_Click); + // + // mnu1000Lines + // + this.mnu1000Lines.Checked = true; + this.mnu1000Lines.CheckState = System.Windows.Forms.CheckState.Checked; + this.mnu1000Lines.Name = "mnu1000Lines"; + this.mnu1000Lines.Size = new System.Drawing.Size(104, 22); + this.mnu1000Lines.Text = "1000"; + this.mnu1000Lines.Click += new System.EventHandler(this.mnu1000Lines_Click); + // + // mnu10000Lines + // + this.mnu10000Lines.Name = "mnu10000Lines"; + this.mnu10000Lines.Size = new System.Drawing.Size(104, 22); + this.mnu10000Lines.Text = "10000"; + this.mnu10000Lines.Click += new System.EventHandler(this.mnu10000Lines_Click); + // + // mnu30000Lines + // + this.mnu30000Lines.Name = "mnu30000Lines"; + this.mnu30000Lines.Size = new System.Drawing.Size(104, 22); + this.mnu30000Lines.Text = "30000"; + this.mnu30000Lines.Click += new System.EventHandler(this.mnu30000Lines_Click); + // + // mnuAutoRefresh + // + this.mnuAutoRefresh.Checked = true; + this.mnuAutoRefresh.CheckOnClick = true; + this.mnuAutoRefresh.CheckState = System.Windows.Forms.CheckState.Checked; + this.mnuAutoRefresh.Name = "mnuAutoRefresh"; + this.mnuAutoRefresh.Size = new System.Drawing.Size(141, 22); + this.mnuAutoRefresh.Text = "Auto-refresh"; + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(138, 6); + // + // mnuRefresh + // + this.mnuRefresh.Name = "mnuRefresh"; + this.mnuRefresh.ShortcutKeys = System.Windows.Forms.Keys.F5; + this.mnuRefresh.Size = new System.Drawing.Size(141, 22); + this.mnuRefresh.Text = "Refresh"; + this.mnuRefresh.Click += new System.EventHandler(this.mnuRefresh_Click); // // frmTraceLogger // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(339, 193); - this.Controls.Add(this.tableLayoutPanel1); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.MaximizeBox = false; - this.MinimizeBox = false; + this.ClientSize = new System.Drawing.Size(808, 547); + this.Controls.Add(this.tableLayoutPanel3); + this.Controls.Add(this.menuStrip1); this.Name = "frmTraceLogger"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Trace Logger"; this.tableLayoutPanel1.ResumeLayout(false); - this.tableLayoutPanel1.PerformLayout(); this.grpLogOptions.ResumeLayout(false); this.tableLayoutPanel2.ResumeLayout(false); this.tableLayoutPanel2.PerformLayout(); + this.tableLayoutPanel3.ResumeLayout(false); + this.grpExecutionLog.ResumeLayout(false); + this.grpExecutionLog.PerformLayout(); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); this.ResumeLayout(false); + this.PerformLayout(); } @@ -287,5 +449,20 @@ private System.Windows.Forms.CheckBox chkIndentCode; private System.Windows.Forms.Button btnOpenTrace; private System.Windows.Forms.CheckBox chkShowEffectiveAddresses; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.TextBox txtTraceLog; + private System.Windows.Forms.Timer tmrUpdateLog; + private System.Windows.Forms.GroupBox grpExecutionLog; + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem showToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem logLinesToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuAutoRefresh; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem mnuRefresh; + private System.Windows.Forms.ToolStripMenuItem mnu100Lines; + private System.Windows.Forms.ToolStripMenuItem mnu1000Lines; + private System.Windows.Forms.ToolStripMenuItem mnu10000Lines; + private System.Windows.Forms.ToolStripMenuItem mnu30000Lines; + private System.Windows.Forms.CheckBox chkUseLabels; } } \ No newline at end of file diff --git a/GUI.NET/Debugger/frmTraceLogger.cs b/GUI.NET/Debugger/frmTraceLogger.cs index 548f335f..1ed4a630 100644 --- a/GUI.NET/Debugger/frmTraceLogger.cs +++ b/GUI.NET/Debugger/frmTraceLogger.cs @@ -9,29 +9,65 @@ using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Mesen.GUI.Config; +using Mesen.GUI.Controls; using Mesen.GUI.Forms; namespace Mesen.GUI.Debugger { public partial class frmTraceLogger : BaseForm { + private int _lineCount; private bool _loggingEnabled = false; + private string _lastFilename; public frmTraceLogger() { InitializeComponent(); + + DebugInfo debugInfo = ConfigManager.Config.DebugInfo; + mnuAutoRefresh.Checked = debugInfo.TraceAutoRefresh; + _lineCount = debugInfo.TraceLineCount; + chkIndentCode.Checked = debugInfo.TraceIndentCode; + chkShowByteCode.Checked = debugInfo.TraceShowByteCode; + chkShowCpuCycles.Checked = debugInfo.TraceShowCpuCycles; + chkShowEffectiveAddresses.Checked = debugInfo.TraceShowEffectiveAddresses; + chkShowExtraInfo.Checked = debugInfo.TraceShowExtraInfo; + chkShowFrameCount.Checked = debugInfo.TraceShowFrameCount; + chkShowPpuCycles.Checked = debugInfo.TraceShowPpuCycles; + chkShowPpuScanline.Checked = debugInfo.TraceShowPpuScanline; + chkShowRegisters.Checked = debugInfo.TraceShowRegisters; + chkUseLabels.Checked = debugInfo.TraceUseLabels; + + UpdateMenu(); + txtTraceLog.Font = new Font(BaseControl.MonospaceFontFamily, 10); + tmrUpdateLog.Start(); } protected override void OnFormClosing(FormClosingEventArgs e) { base.OnFormClosing(e); + DebugInfo debugInfo = ConfigManager.Config.DebugInfo; + debugInfo.TraceAutoRefresh = mnuAutoRefresh.Checked; + debugInfo.TraceLineCount = _lineCount; + debugInfo.TraceIndentCode = chkIndentCode.Checked; + debugInfo.TraceShowByteCode = chkShowByteCode.Checked; + debugInfo.TraceShowCpuCycles = chkShowCpuCycles.Checked; + debugInfo.TraceShowEffectiveAddresses = chkShowEffectiveAddresses.Checked; + debugInfo.TraceShowExtraInfo = chkShowExtraInfo.Checked; + debugInfo.TraceShowFrameCount = chkShowFrameCount.Checked; + debugInfo.TraceShowPpuCycles = chkShowPpuCycles.Checked; + debugInfo.TraceShowPpuScanline = chkShowPpuScanline.Checked; + debugInfo.TraceShowRegisters = chkShowRegisters.Checked; + debugInfo.TraceUseLabels = chkUseLabels.Checked; + ConfigManager.ApplyChanges(); + if(_loggingEnabled) { InteropEmu.DebugStopTraceLogger(); } } - private void btnStartLogging_Click(object sender, EventArgs e) + private void SetOptions() { TraceLoggerOptions options = new TraceLoggerOptions() { ShowByteCode = chkShowByteCode.Checked, @@ -42,16 +78,31 @@ namespace Mesen.GUI.Debugger ShowPpuScanline = chkShowPpuScanline.Checked, ShowRegisters = chkShowRegisters.Checked, IndentCode = chkIndentCode.Checked, - ShowEffectiveAddresses = chkShowEffectiveAddresses.Checked + ShowEffectiveAddresses = chkShowEffectiveAddresses.Checked, + UseLabels = chkUseLabels.Checked }; - InteropEmu.DebugStartTraceLogger(options); + InteropEmu.DebugSetTraceOptions(options); + } - btnStartLogging.Enabled = false; - btnStopLogging.Enabled = true; - btnOpenTrace.Enabled = true; + private void btnStartLogging_Click(object sender, EventArgs e) + { + using(SaveFileDialog sfd = new SaveFileDialog()) { + sfd.FileName = "Trace logs (*.txt)|*.txt"; + sfd.FileName = "Trace - " + InteropEmu.GetRomInfo().GetRomName() + ".txt"; + sfd.InitialDirectory = ConfigManager.DebuggerFolder; + if(sfd.ShowDialog() == DialogResult.OK) { + _lastFilename = sfd.FileName; + SetOptions(); + InteropEmu.DebugStartTraceLogger(sfd.FileName); - _loggingEnabled = true; + btnStartLogging.Enabled = false; + btnStopLogging.Enabled = true; + btnOpenTrace.Enabled = false; + + _loggingEnabled = true; + } + } } private void btnStopLogging_Click(object sender, EventArgs e) @@ -59,13 +110,80 @@ namespace Mesen.GUI.Debugger InteropEmu.DebugStopTraceLogger(); btnStartLogging.Enabled = true; btnStopLogging.Enabled = false; + btnOpenTrace.Enabled = true; } private void btnOpenTrace_Click(object sender, EventArgs e) { try { - System.Diagnostics.Process.Start(Path.Combine(ConfigManager.DebuggerFolder, "Trace - " + InteropEmu.GetRomInfo().GetRomName() + ".log")); + System.Diagnostics.Process.Start(_lastFilename); } catch { } } + + private void RefreshLog() + { + SetOptions(); + string newTrace = InteropEmu.DebugGetExecutionTrace((UInt32)_lineCount).Replace("\n", Environment.NewLine); + + if(newTrace != txtTraceLog.Text) { + txtTraceLog.Text = newTrace; + txtTraceLog.SelectionStart = txtTraceLog.TextLength; + txtTraceLog.ScrollToCaret(); + } + } + + private void UpdateMenu() + { + mnu30000Lines.Checked = _lineCount == 30000; + mnu10000Lines.Checked = _lineCount == 10000; + mnu1000Lines.Checked = _lineCount == 1000; + mnu100Lines.Checked = _lineCount == 100; + + if(_lineCount >= 10000) { + mnuAutoRefresh.Checked = false; + } else if(_lineCount == 1000) { + tmrUpdateLog.Interval = 250; + } else { + tmrUpdateLog.Interval = 150; + } + + mnuAutoRefresh.Enabled = _lineCount < 10000; + } + + private void tmrUpdateLog_Tick(object sender, EventArgs e) + { + if(mnuAutoRefresh.Checked) { + RefreshLog(); + } + } + + private void mnu30000Lines_Click(object sender, EventArgs e) + { + _lineCount = 30000; + UpdateMenu(); + } + + private void mnu10000Lines_Click(object sender, EventArgs e) + { + _lineCount = 10000; + UpdateMenu(); + } + + private void mnu1000Lines_Click(object sender, EventArgs e) + { + _lineCount = 1000; + UpdateMenu(); + } + + private void mnu100Lines_Click(object sender, EventArgs e) + { + _lineCount = 100; + UpdateMenu(); + } + + private void mnuRefresh_Click(object sender, EventArgs e) + { + RefreshLog(); + } } } diff --git a/GUI.NET/Debugger/frmTraceLogger.resx b/GUI.NET/Debugger/frmTraceLogger.resx index 8766f298..24e4a526 100644 --- a/GUI.NET/Debugger/frmTraceLogger.resx +++ b/GUI.NET/Debugger/frmTraceLogger.resx @@ -120,4 +120,10 @@ 17, 17 + + 107, 17 + + + 238, 17 + \ No newline at end of file diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index af7064ba..753b8bb7 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -195,10 +195,13 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void DebugSetNextStatement(UInt16 addr); [DllImport(DLLPath)] public static extern Int32 DebugEvaluateExpression([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string expression, out EvalResultType resultType); - - [DllImport(DLLPath)] public static extern void DebugStartTraceLogger(TraceLoggerOptions options); - [DllImport(DLLPath)] public static extern void DebugStopTraceLogger(); + [DllImport(DLLPath)] public static extern void DebugStartTraceLogger([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename); + [DllImport(DLLPath)] public static extern void DebugStopTraceLogger(); + [DllImport(DLLPath)] public static extern void DebugSetTraceOptions(TraceLoggerOptions options); + [DllImport(DLLPath, EntryPoint = "DebugGetExecutionTrace")] private static extern IntPtr DebugGetExecutionTraceWrapper(UInt32 lineCount); + public static string DebugGetExecutionTrace(UInt32 lineCount) { return PtrToStringUtf8(InteropEmu.DebugGetExecutionTraceWrapper(lineCount)); } + [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugLoadCdlFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string cdlFilepath); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugSaveCdlFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string cdlFilepath); [DllImport(DLLPath)] public static extern void DebugGetCdlRatios(ref CdlRatios ratios); @@ -806,6 +809,7 @@ namespace Mesen.GUI [MarshalAs(UnmanagedType.I1)] public bool ShowExtraInfo; [MarshalAs(UnmanagedType.I1)] public bool IndentCode; [MarshalAs(UnmanagedType.I1)] public bool ShowEffectiveAddresses; + [MarshalAs(UnmanagedType.I1)] public bool UseLabels; } public enum ProfilerDataType diff --git a/InteropDLL/DebugWrapper.cpp b/InteropDLL/DebugWrapper.cpp index 0ac928a7..7693b80c 100644 --- a/InteropDLL/DebugWrapper.cpp +++ b/InteropDLL/DebugWrapper.cpp @@ -74,8 +74,10 @@ extern "C" DllExport int32_t __stdcall DebugEvaluateExpression(char* expression, EvalResultType *resultType) { return GetDebugger()->EvaluateExpression(expression, *resultType); } - DllExport void __stdcall DebugStartTraceLogger(TraceLoggerOptions options) { GetDebugger()->StartTraceLogger(options); } - DllExport void __stdcall DebugStopTraceLogger() { GetDebugger()->StopTraceLogger(); } + DllExport void __stdcall DebugSetTraceOptions(TraceLoggerOptions options) { GetDebugger()->GetTraceLogger()->SetOptions(options); } + DllExport void __stdcall DebugStartTraceLogger(char* filename) { GetDebugger()->GetTraceLogger()->StartLogging(filename); } + DllExport void __stdcall DebugStopTraceLogger() { GetDebugger()->GetTraceLogger()->StopLogging(); } + DllExport const char* DebugGetExecutionTrace(uint32_t lineCount) { return GetDebugger()->GetTraceLogger()->GetExecutionTrace(lineCount); } DllExport void __stdcall DebugSetMemoryValue(DebugMemoryType type, uint32_t address, uint8_t value) { return GetDebugger()->GetMemoryDumper()->SetMemoryValue(type, address, value); } DllExport int32_t __stdcall DebugGetMemorySize(DebugMemoryType type) { return GetDebugger()->GetMemorySize(type); }