From 4ffebdb4e0d6a6d5b4e2f427a46f446774b4238b Mon Sep 17 00:00:00 2001 From: Souryo Date: Mon, 21 Nov 2016 22:34:47 -0500 Subject: [PATCH] Debugger: Ability to add labels/symbols & comments to code (work in progress) --- Core/CPU.h | 3 +- Core/Debugger.cpp | 24 +- Core/Debugger.h | 7 + Core/Disassembler.cpp | 138 ++++++++++-- Core/Disassembler.h | 5 +- Core/DisassemblyInfo.cpp | 208 +++++++++--------- Core/DisassemblyInfo.h | 19 +- Core/MemoryManager.cpp | 10 + Core/MemoryManager.h | 2 + Core/TraceLogger.cpp | 10 +- GUI.NET/Config/DebugInfo.cs | 4 +- .../Controls/ctrlDebuggerCode.Designer.cs | 14 +- GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs | 91 ++++---- GUI.NET/Debugger/Controls/ctrlFunctionList.cs | 3 +- .../Controls/ctrlLabelList.Designer.cs | 113 ++++++++++ GUI.NET/Debugger/Controls/ctrlLabelList.cs | 73 ++++++ GUI.NET/Debugger/Controls/ctrlLabelList.resx | 123 +++++++++++ .../Controls/ctrlScrollableTextbox.cs | 6 + GUI.NET/Debugger/Controls/ctrlTextbox.cs | 152 ++++++++----- GUI.NET/Debugger/LabelManager.cs | 71 ++++++ GUI.NET/Debugger/frmDebugger.Designer.cs | 51 +++-- GUI.NET/Debugger/frmDebugger.cs | 40 +++- GUI.NET/Debugger/frmEditLabel.Designer.cs | 125 +++++++++++ GUI.NET/Debugger/frmEditLabel.cs | 54 +++++ GUI.NET/Debugger/frmEditLabel.resx | 123 +++++++++++ GUI.NET/GUI.NET.csproj | 19 ++ GUI.NET/InteropEmu.cs | 11 +- InteropDLL/DebugWrapper.cpp | 2 + 28 files changed, 1231 insertions(+), 270 deletions(-) create mode 100644 GUI.NET/Debugger/Controls/ctrlLabelList.Designer.cs create mode 100644 GUI.NET/Debugger/Controls/ctrlLabelList.cs create mode 100644 GUI.NET/Debugger/Controls/ctrlLabelList.resx create mode 100644 GUI.NET/Debugger/LabelManager.cs create mode 100644 GUI.NET/Debugger/frmEditLabel.Designer.cs create mode 100644 GUI.NET/Debugger/frmEditLabel.cs create mode 100644 GUI.NET/Debugger/frmEditLabel.resx diff --git a/Core/CPU.h b/Core/CPU.h index 368404b7..21dc59eb 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -55,11 +55,12 @@ struct State class CPU : public Snapshotable { -private: +public: static const uint16_t NMIVector = 0xFFFA; static const uint16_t ResetVector = 0xFFFC; static const uint16_t IRQVector = 0xFFFE; +private: static CPU* Instance; typedef void(CPU::*Func)(); diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index d7402619..5575cbd5 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -97,7 +97,7 @@ bool Debugger::LoadCdlFile(string cdlFilepath) if(_codeDataLogger->LoadCdlFile(cdlFilepath)) { for(int i = 0, len = _mapper->GetPrgSize(); i < len; i++) { if(_codeDataLogger->IsCode(i)) { - i = _disassembler->BuildCache(i, -1, 0xFFFF, false) - 1; + i = _disassembler->BuildCache(i, -1, 0xFFFF, _codeDataLogger->IsSubEntryPoint(i)) - 1; } } @@ -127,6 +127,19 @@ CdlRatios Debugger::GetCdlRatios() return _codeDataLogger->GetRatios(); } +void Debugger::SetLabel(uint32_t address, string label, string comment) +{ + _codeLabels.erase(address); + if(!label.empty()) { + _codeLabels.emplace(address, label); + } + + _codeComments.erase(address); + if(!comment.empty()) { + _codeComments.emplace(address, comment); + } +} + void Debugger::SetBreakpoints(Breakpoint breakpoints[], uint32_t length) { _bpUpdateLock.AcquireSafe(); @@ -449,10 +462,11 @@ string Debugger::GenerateOutput() State cpuState = _cpu->GetState(); std::ostringstream output; bool showEffectiveAddresses = CheckFlag(DebuggerFlags::ShowEffectiveAddresses); + bool showOnlyDiassembledCode = CheckFlag(DebuggerFlags::ShowOnlyDisassembledCode); //Get code in internal RAM - output << _disassembler->GetCode(0x0000, 0x1FFF, 0x0000, PrgMemoryType::PrgRom, showEffectiveAddresses, cpuState, _memoryManager); - output << "2000:::--END OF INTERNAL RAM--\n"; + output << _disassembler->GetCode(0x0000, 0x1FFF, 0x0000, PrgMemoryType::PrgRom, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _codeLabels, _codeComments); + output << "2000\x1\x1\x1--End of internal RAM--\n"; for(uint32_t i = 0x2000; i < 0x10000; i += 0x100) { //Merge all sequential ranges into 1 chunk @@ -469,7 +483,7 @@ string Debugger::GenerateOutput() romAddr += 0x100; i+=0x100; } - output << _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, PrgMemoryType::PrgRom, showEffectiveAddresses, cpuState, _memoryManager); + output << _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, PrgMemoryType::PrgRom, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _codeLabels, _codeComments); } else if(ramAddr >= 0) { startAddr = ramAddr; endAddr = startAddr + 0xFF; @@ -478,7 +492,7 @@ string Debugger::GenerateOutput() ramAddr += 0x100; i += 0x100; } - output << _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, PrgMemoryType::WorkRam, showEffectiveAddresses, cpuState, _memoryManager); + output << _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, PrgMemoryType::WorkRam, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _codeLabels, _codeComments); } } diff --git a/Core/Debugger.h b/Core/Debugger.h index 912ce88d..3de00877 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -4,9 +4,11 @@ #include #include #include +#include using std::atomic; using std::deque; using std::unordered_set; +using std::unordered_map; #include "DebugState.h" #include "Breakpoint.h" @@ -25,6 +27,7 @@ enum class DebuggerFlags { PpuPartialDraw = 1, ShowEffectiveAddresses = 2, + ShowOnlyDisassembledCode = 4 }; class Debugger @@ -54,6 +57,9 @@ private: vector _breakpoints[BreakpointTypeCount]; bool _hasBreakpoint[BreakpointTypeCount]; + unordered_map _codeLabels; + unordered_map _codeComments; + deque _callstackAbsolute; deque _callstackRelative; @@ -99,6 +105,7 @@ public: bool CheckFlag(DebuggerFlags flag); void SetBreakpoints(Breakpoint breakpoints[], uint32_t length); + void SetLabel(uint32_t address, string label, string comment); void GetFunctionEntryPoints(int32_t* entryPoints); void GetCallstack(int32_t* callstackAbsolute, int32_t* callstackRelative); diff --git a/Core/Disassembler.cpp b/Core/Disassembler.cpp index 8fb9df6f..d488cf52 100644 --- a/Core/Disassembler.cpp +++ b/Core/Disassembler.cpp @@ -174,9 +174,25 @@ void Disassembler::InvalidateCache(uint16_t memoryAddr, int32_t absoluteRamAddr) } } -string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, State& cpuState, shared_ptr memoryManager) +vector Disassembler::SplitComment(string input) { + vector result; + size_t index; + while((index = input.find('\n')) != string::npos) { + result.push_back(input.substr(0, index)); + input = input.substr(index + 1, input.size() - index - 1); + } + result.push_back(input); + return result; +} + +string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, bool showOnlyDiassembledCode, State& cpuState, shared_ptr memoryManager, unordered_map &codeLabels, unordered_map &codeComments) { std::ostringstream output; + + uint16_t resetVector = memoryManager->DebugReadWord(CPU::ResetVector); + uint16_t nmiVector = memoryManager->DebugReadWord(CPU::NMIVector); + uint16_t irqVector = memoryManager->DebugReadWord(CPU::IRQVector); + vector> *cache; uint8_t *source; uint32_t mask = 0xFFFFFFFF; @@ -194,32 +210,128 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo uint32_t addr = startAddr; uint32_t byteCount = 0; + bool skippingCode = false; while(addr <= endAddr) { - shared_ptr info; - if(info = (*cache)[addr&mask]) { + auto labelSearch = codeLabels.find(addr); + auto commentSearch = codeComments.find(addr); + string label = labelSearch != codeLabels.end() ? labelSearch->second : ""; + string labelString = label.empty() ? "" : ("\x1\x1\x1" + labelSearch->second + ":\n"); + string commentString = commentSearch != codeComments.end() ? commentSearch->second : ""; + bool multilineComment = commentString.find_first_of('\n') != string::npos; + string singleLineComment = ""; + string multiLineComment = ""; + if(multilineComment) { + for(string &str : SplitComment(commentString)) { + multiLineComment += "\x1\x1\x1\x2\x2;" + str + "\n"; + } + } else if(!commentString.empty()) { + singleLineComment = "\x2;" + commentString; + } + + shared_ptr info = (*cache)[addr&mask]; + if(info) { if(byteCount > 0) { output << "\n"; byteCount = 0; + } + + if(skippingCode) { + output << std::hex << std::uppercase << (memoryAddr - 1) << "\x1" << (addr - 1) << "\x1\x1"; + if(showOnlyDiassembledCode) { + output << "----\n"; + } else { + output << "__unknown block__\n"; + } + skippingCode = false; } - string effectiveAddress = showEffectiveAddresses ? info->GetEffectiveAddress(cpuState, memoryManager) : ""; - output << std::hex << std::uppercase << memoryAddr << ":" << addr << ":" << info->ToString(memoryAddr) << "||" << effectiveAddress << "\n"; + + string effectiveAddress = showEffectiveAddresses ? info->GetEffectiveAddressString(cpuState, memoryManager, &codeLabels) : ""; + + if(info->IsSubEntryPoint()) { + if(label.empty()) { + output << "\x1\x1\x1\n\x1\x1\x1--sub start--\n"; + } else { + output << "\x1\x1\x1\n\x1\x1\x1--" + label + "()--\n"; + } + } else if(memoryAddr == resetVector) { + output << "\x1\x1\x1\n\x1\x1\x1--reset--\n"; + } else if(memoryAddr == irqVector) { + output << "\x1\x1\x1\n\x1\x1\x1--irq--\n"; + } else if(memoryAddr == nmiVector) { + output << "\x1\x1\x1\n\x1\x1\x1--nmi--\n"; + } + + output << multiLineComment; + output << labelString; + output << std::hex << std::uppercase << memoryAddr << "\x1" << addr << "\x1" << info->GetByteCode() << "\x1 " << info->ToString(memoryAddr, memoryManager, &codeLabels) << "\x2" << effectiveAddress; + output << singleLineComment; + + output << "\n"; + + if(info->IsSubExitPoint()) { + output << "\x1\x1\x1__sub end__\n\x1\x1\x1\n"; + } + addr += info->GetSize(); memoryAddr += info->GetSize(); } else { - if(byteCount >= 8) { - output << "\n"; - byteCount = 0; + if(!skippingCode) { + output << std::hex << std::uppercase << memoryAddr << "\x1" << addr << "\x1\x1"; + if(showOnlyDiassembledCode) { + output << "____\n\x1\x1\x1"; + if(label.empty()) { + output << "[[unknown block]]\n"; + } else { + output << "[[" << label << "]]\n"; + if(!singleLineComment.empty()) { + output << "\x1\x1\x1" << singleLineComment << "\n"; + } else { + output << multiLineComment; + } + } + } else { + output << "--unknown block--\n"; + } + skippingCode = true; } - if(byteCount == 0) { - output << std::hex << std::uppercase << memoryAddr << ":" << addr << "::" << ".db"; - } - output << std::hex << " $" << std::setfill('0') << std::setw(2) << (short)source[addr&mask]; - byteCount++; + if(!showOnlyDiassembledCode) { + if(byteCount >= 8 || !label.empty() || !commentString.empty()) { + output << "\n"; + byteCount = 0; + } + if(byteCount == 0) { + output << multiLineComment; + output << labelString; + output << std::hex << std::uppercase << memoryAddr << "\x1" << addr << "\x1\x1" << ".db"; + output << singleLineComment; + + if(!label.empty() || !commentString.empty()) { + byteCount = 7; + } + } + output << std::hex << " $" << std::setfill('0') << std::setw(2) << (short)source[addr&mask]; + + byteCount++; + } addr++; memoryAddr++; } } + + if(skippingCode) { + if(byteCount != 0) { + output << "\n"; + } + + output << std::hex << std::uppercase << (memoryAddr - 1) << "\x1" << (addr - 1) << "\x1\x1"; + if(showOnlyDiassembledCode) { + output << "----\n"; + } else { + output << "__unknown block__\n"; + } + } + output << "\n"; return output.str(); diff --git a/Core/Disassembler.h b/Core/Disassembler.h index 583e9fae..6766dd47 100644 --- a/Core/Disassembler.h +++ b/Core/Disassembler.h @@ -1,6 +1,8 @@ #pragma once #include "stdafx.h" #include "BaseMapper.h" +#include +using std::unordered_map; struct State; class MemoryManager; @@ -18,6 +20,7 @@ private: uint32_t _prgSize; bool IsUnconditionalJump(uint8_t opCode); + vector SplitComment(string input); public: Disassembler(uint8_t* internalRam, uint8_t* prgRom, uint32_t prgSize, uint8_t* prgRam, uint32_t prgRamSize); @@ -26,7 +29,7 @@ public: uint32_t BuildCache(int32_t absoluteAddr, int32_t absoluteRamAddr, uint16_t memoryAddr, bool isSubEntryPoint); void InvalidateCache(uint16_t memoryAddr, int32_t absoluteRamAddr); - string GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, State& cpuState, shared_ptr memoryManager); + string GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, bool showOnlyDiassembledCode, State& cpuState, shared_ptr memoryManager, unordered_map &codeLabels, unordered_map &codeComments); shared_ptr GetDisassemblyInfo(int32_t absoluteAddress, int32_t absoluteRamAddress, uint16_t memoryAddress); }; diff --git a/Core/DisassemblyInfo.cpp b/Core/DisassemblyInfo.cpp index 22525744..bc84572c 100644 --- a/Core/DisassemblyInfo.cpp +++ b/Core/DisassemblyInfo.cpp @@ -6,108 +6,76 @@ string DisassemblyInfo::OPName[256]; AddrMode DisassemblyInfo::OPMode[256]; uint32_t DisassemblyInfo::OPSize[256]; -void DisassemblyInfo::Initialize(uint32_t memoryAddr) +string DisassemblyInfo::ToString(uint32_t memoryAddr, shared_ptr memoryManager, std::unordered_map *codeLabels) { - _lastAddr = memoryAddr; - std::ostringstream output; uint8_t opCode = *_opPointer; - _opSize = DisassemblyInfo::OPSize[opCode]; - _opMode = DisassemblyInfo::OPMode[opCode]; - - //Output raw byte code - for(uint32_t i = 0; i < 3; i++) { - if(i < _opSize) { - output << "$" << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << (short)*(_opPointer + i); - } else { - output << " "; - } - if(i != 2) { - output << " "; - } - } - output << ":"; output << DisassemblyInfo::OPName[opCode]; - if(opCode == 0x40 || opCode == 0x60) { - //Make end of function/interrupt routines more obvious - output << " ---->"; - } - if(DisassemblyInfo::OPName[opCode].empty()) { output << "invalid opcode"; } - std::ostringstream nextByte; - std::ostringstream nextWord; + std::ostringstream addrString; if(_opSize == 2) { - nextByte << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << (short)(*(_opPointer + 1)); + _opAddr = *(_opPointer + 1); } else if(_opSize == 3) { - nextWord << std::uppercase << std::hex << std::setw(4) << std::setfill('0') << (*(_opPointer + 1) | (*(_opPointer + 2) << 8)); + _opAddr = *(_opPointer + 1) | (*(_opPointer + 2) << 8); + } + + string operandValue; + if(codeLabels) { + auto result = codeLabels->find(memoryManager->ToAbsolutePrgAddress(_opAddr)); + if(result != codeLabels->end()) { + operandValue = result->second; + } + } + + if(operandValue.empty()) { + std::stringstream ss; + ss << "$" << std::uppercase << std::hex << std::setw(_opSize == 2 ? 2 : 4) << std::setfill('0') << (short)_opAddr; + operandValue = ss.str(); } + output << " "; + switch(_opMode) { + case AddrMode::Acc: output << " A"; break; + case AddrMode::Imm: output << "#" << operandValue; break; + case AddrMode::Ind: output << "(" << operandValue << ")"; break; + case AddrMode::IndX: output << "(" << operandValue << ",X)"; break; + + case AddrMode::IndY: + case AddrMode::IndYW: + output << "(" << operandValue << "),Y"; + break; + case AddrMode::Abs: - output << " $" << nextWord.str(); + case AddrMode::Zero: + output << operandValue; break; case AddrMode::AbsX: case AddrMode::AbsXW: - output << " $" << nextWord.str() << ",X"; + case AddrMode::ZeroX: + output << operandValue << ",X"; break; case AddrMode::AbsY: case AddrMode::AbsYW: - output << " $" << nextWord.str() << ",Y"; - break; - - case AddrMode::Imm: - output << " #$" << nextByte.str(); - break; - - case AddrMode::Ind: - output << " ($" << nextWord.str() << ")"; - break; - - case AddrMode::IndX: - output << " ($" << nextByte.str() << ",X)"; - break; - - case AddrMode::IndY: - case AddrMode::IndYW: - output << " ($" << nextByte.str() << "),Y"; + case AddrMode::ZeroY: + output << operandValue << ",Y"; break; case AddrMode::Rel: //TODO (not correct when banks are switched around in memory) - output << " $" << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << ((int8_t)*(_opPointer + 1) + memoryAddr + 2); - break; - - case AddrMode::Zero: - output << " $" << nextByte.str(); - break; - - case AddrMode::ZeroX: - output << " $" << nextByte.str() << ",X"; - break; - - case AddrMode::ZeroY: - output << " $" << nextByte.str() << ",Y"; - break; - - case AddrMode::Acc: - output << " A"; - break; - - default: + output << "$" << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << ((int8_t)*(_opPointer + 1) + memoryAddr + 2); break; + + default: break; } - if(_isSubEntryPoint) { - output << " <----"; - } - - _disassembly = output.str(); + return output.str(); } DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint) @@ -115,72 +83,99 @@ DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint) _opPointer = opPointer; _isSubEntryPoint = isSubEntryPoint; - Initialize(); + uint8_t opCode = *_opPointer; + _opSize = DisassemblyInfo::OPSize[opCode]; + _opMode = DisassemblyInfo::OPMode[opCode]; + _isSubExitPoint = opCode == 0x40 || opCode == 0x60; + + + //Raw byte code + std::stringstream byteCodeOutput; + for(uint32_t i = 0; i < 3; i++) { + if(i < _opSize) { + byteCodeOutput << "$" << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << (short)*(_opPointer + i); + } else { + byteCodeOutput << " "; + } + if(i != 2) { + byteCodeOutput << " "; + } + } + _byteCode = byteCodeOutput.str(); + } void DisassemblyInfo::SetSubEntryPoint() { - if(!_isSubEntryPoint) { - _isSubEntryPoint = true; - Initialize(); + _isSubEntryPoint = true; +} + +string DisassemblyInfo::GetEffectiveAddressString(State& cpuState, shared_ptr memoryManager, std::unordered_map *codeLabels) +{ + int32_t effectiveAddress = GetEffectiveAddress(cpuState, memoryManager); + if(effectiveAddress < 0) { + return ""; + } else { + bool empty = true; + if(codeLabels) { + auto result = codeLabels->find(memoryManager->ToAbsolutePrgAddress(effectiveAddress)); + if(result != codeLabels->end()) { + return " @ " + result->second; + } + } + + std::stringstream ss; + ss << std::uppercase << std::setfill('0') << " @ $"; + if(_opMode == AddrMode::ZeroX || _opMode == AddrMode::ZeroY) { + ss << std::setw(2) << std::hex << (uint16_t)effectiveAddress; + } else { + ss << std::setw(4) << std::hex << (uint16_t)effectiveAddress; + } + + return ss.str(); } } -string DisassemblyInfo::GetEffectiveAddress(State& cpuState, shared_ptr memoryManager) +int32_t DisassemblyInfo::GetEffectiveAddress(State& cpuState, shared_ptr memoryManager) { - std::stringstream ss; - ss << std::uppercase << std::setfill('0'); switch(_opMode) { - case AddrMode::ZeroX: ss << " @ $" << std::setw(2) << std::hex << (short)(uint8_t)(*(_opPointer + 1) + cpuState.X); break; - case AddrMode::ZeroY: ss << " @ $" << std::setw(2) << std::hex << (short)(uint8_t)(*(_opPointer + 1) + cpuState.Y); break; + case AddrMode::ZeroX: return (uint8_t)(*(_opPointer + 1) + cpuState.X); break; + case AddrMode::ZeroY: return (uint8_t)(*(_opPointer + 1) + cpuState.Y); break; case AddrMode::IndX: { uint8_t zeroAddr = *(_opPointer + 1) + cpuState.X; - uint16_t addr = memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8; - ss << " @ $" << std::setw(4) << std::hex << addr; - break; + return memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8; } case AddrMode::IndY: case AddrMode::IndYW: { uint8_t zeroAddr = *(_opPointer + 1); uint16_t addr = memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8; - addr += cpuState.Y; - ss << " @ $" << std::setw(4) << std::hex << addr; - break; + return addr + cpuState.Y; } case AddrMode::Ind: { uint8_t zeroAddr = *(_opPointer + 1); - uint16_t addr = memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8; - ss << " @ $" << std::setw(4) << std::hex << addr; - break; + return memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8; } case AddrMode::AbsX: case AddrMode::AbsXW: { - uint16_t addr = (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.X; - ss << " @ $" << std::setw(4) << std::hex << addr; - break; + return (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.X; } case AddrMode::AbsY: case AddrMode::AbsYW: { - uint16_t addr = (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.Y; - ss << " @ $" << std::setfill('0') << std::setw(4) << std::hex << addr; - break; + return (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.Y; } } - return ss.str(); + return -1; } -string DisassemblyInfo::ToString(uint32_t memoryAddr) +string DisassemblyInfo::GetByteCode() { - if(memoryAddr != _lastAddr && _opMode == AddrMode::Rel) { - Initialize(memoryAddr); - } - return _disassembly; + return _byteCode; } uint32_t DisassemblyInfo::GetSize() @@ -188,3 +183,12 @@ uint32_t DisassemblyInfo::GetSize() return _opSize; } +bool DisassemblyInfo::IsSubEntryPoint() +{ + return _isSubEntryPoint; +} + +bool DisassemblyInfo::IsSubExitPoint() +{ + return _isSubExitPoint; +} \ No newline at end of file diff --git a/Core/DisassemblyInfo.h b/Core/DisassemblyInfo.h index 00dc27d6..81a3a470 100644 --- a/Core/DisassemblyInfo.h +++ b/Core/DisassemblyInfo.h @@ -1,5 +1,6 @@ #pragma once #include "stdafx.h" +#include #include "CPU.h" class DisassemblyInfo @@ -10,22 +11,28 @@ public: static uint32_t OPSize[256]; private: - string _disassembly; + string _byteCode; uint8_t *_opPointer = nullptr; bool _isSubEntryPoint = false; + bool _isSubExitPoint = false; uint32_t _opSize = 0; AddrMode _opMode; - uint32_t _lastAddr = 0; -private: - void Initialize(uint32_t memoryAddr = 0); + uint16_t _opAddr; public: DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint); void SetSubEntryPoint(); - string GetEffectiveAddress(State& cpuState, shared_ptr memoryManager); - string ToString(uint32_t memoryAddr); + + int32_t GetEffectiveAddress(State& cpuState, shared_ptr memoryManager); + string GetEffectiveAddressString(State& cpuState, shared_ptr memoryManager, std::unordered_map *codeLabels); + + string ToString(uint32_t memoryAddr, shared_ptr memoryManager, std::unordered_map *codeLabels = nullptr); + string GetByteCode(); uint32_t GetSize(); + + bool IsSubEntryPoint(); + bool IsSubExitPoint(); }; diff --git a/Core/MemoryManager.cpp b/Core/MemoryManager.cpp index f678e60a..c5f48d2a 100644 --- a/Core/MemoryManager.cpp +++ b/Core/MemoryManager.cpp @@ -101,6 +101,11 @@ uint8_t MemoryManager::DebugRead(uint16_t addr) return value; } +uint16_t MemoryManager::DebugReadWord(uint16_t addr) +{ + return DebugRead(addr) | (DebugRead(addr + 1) << 8); +} + void MemoryManager::ProcessCpuClock() { _mapper->ProcessCpuClock(); @@ -179,6 +184,11 @@ void MemoryManager::WriteVRAM(uint16_t addr, uint8_t value) _mapper->WriteVRAM(addr, value); } +uint32_t MemoryManager::ToAbsolutePrgAddress(uint16_t ramAddr) +{ + return _mapper->ToAbsoluteAddress(ramAddr); +} + uint32_t MemoryManager::ToAbsoluteChrAddress(uint16_t vramAddr) { return _mapper->ToAbsoluteChrAddress(vramAddr); diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index 130cd987..8c06d2b1 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -39,6 +39,7 @@ class MemoryManager: public Snapshotable void RegisterIODevice(IMemoryHandler *handler); uint8_t DebugRead(uint16_t addr); + uint16_t DebugReadWord(uint16_t addr); uint8_t DebugReadVRAM(uint16_t addr); void DebugWrite(uint16_t addr, uint8_t value); @@ -53,6 +54,7 @@ class MemoryManager: public Snapshotable uint8_t ReadVRAM(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::PpuRenderingRead); void WriteVRAM(uint16_t addr, uint8_t value); + uint32_t ToAbsolutePrgAddress(uint16_t ramAddr); uint32_t ToAbsoluteChrAddress(uint16_t vramAddr); static uint8_t GetOpenBus(uint8_t mask = 0xFF); diff --git a/Core/TraceLogger.cpp b/Core/TraceLogger.cpp index 13eb0a78..7d427657 100644 --- a/Core/TraceLogger.cpp +++ b/Core/TraceLogger.cpp @@ -42,11 +42,7 @@ void TraceLogger::Log(DebugState &state, shared_ptr disassembly State &cpuState = state.CPU; PPUDebugState &ppuState = state.PPU; - string disassembly = disassemblyInfo->ToString(cpuState.DebugPC); - auto separatorPosition = disassembly.begin() + disassembly.find_first_of(':', 0); - string byteCode(disassembly.begin(), separatorPosition); - byteCode.erase(std::remove(byteCode.begin(), byteCode.end(), '$'), byteCode.end()); - string assemblyCode(separatorPosition + 1, disassembly.end()); + string disassembly = disassemblyInfo->ToString(cpuState.DebugPC, _memoryManager); //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; @@ -66,7 +62,7 @@ void TraceLogger::Log(DebugState &state, shared_ptr disassembly _outputFile << std::uppercase << std::hex << std::setfill('0') << std::setw(4) << std::right << (short)cpuState.DebugPC << " "; if(_options.ShowByteCode) { - _outputFile << std::setfill(' ') << std::setw(10) << std::left << byteCode; + _outputFile << std::setfill(' ') << std::setw(10) << std::left << disassemblyInfo->GetByteCode(); } int indentLevel = 0; @@ -75,7 +71,7 @@ void TraceLogger::Log(DebugState &state, shared_ptr disassembly _outputFile << std::string(indentLevel, ' '); } - string codeString = assemblyCode + (_options.ShowEffectiveAddresses ? disassemblyInfo->GetEffectiveAddress(state.CPU, _memoryManager) : ""); + string codeString = disassembly + (_options.ShowEffectiveAddresses ? disassemblyInfo->GetEffectiveAddressString(state.CPU, _memoryManager, nullptr) : ""); _outputFile << std::setfill(' ') << std::setw(32 - indentLevel) << std::left << codeString; if(_options.ShowRegisters) { diff --git a/GUI.NET/Config/DebugInfo.cs b/GUI.NET/Config/DebugInfo.cs index 89125530..0f9049b4 100644 --- a/GUI.NET/Config/DebugInfo.cs +++ b/GUI.NET/Config/DebugInfo.cs @@ -12,7 +12,6 @@ namespace Mesen.GUI.Config { public class DebugViewInfo { - public bool ShowOnlyDiassembledCode = true; public bool ShowByteCode = false; public bool ShowPrgAddresses = false; public float FontSize = 13; @@ -22,6 +21,9 @@ namespace Mesen.GUI.Config { public DebugViewInfo LeftView; public DebugViewInfo RightView; + + public bool ShowOnlyDisassembledCode = true; + public bool SplitView = false; public bool HexDisplay = true; diff --git a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs index 1c72b053..8e101595 100644 --- a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs +++ b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs @@ -32,7 +32,6 @@ this.mnuShowNextStatement = new System.Windows.Forms.ToolStripMenuItem(); this.mnuSetNextStatement = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuShowOnlyDisassembledCode = new System.Windows.Forms.ToolStripMenuItem(); this.mnuShowLineNotes = new System.Windows.Forms.ToolStripMenuItem(); this.mnuShowCodeNotes = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); @@ -54,7 +53,6 @@ this.mnuShowNextStatement, this.mnuSetNextStatement, this.toolStripMenuItem1, - this.mnuShowOnlyDisassembledCode, this.mnuShowLineNotes, this.mnuShowCodeNotes, this.toolStripMenuItem2, @@ -87,16 +85,6 @@ this.toolStripMenuItem1.Name = "toolStripMenuItem1"; this.toolStripMenuItem1.Size = new System.Drawing.Size(255, 6); // - // mnuShowOnlyDisassembledCode - // - this.mnuShowOnlyDisassembledCode.Checked = true; - this.mnuShowOnlyDisassembledCode.CheckOnClick = true; - this.mnuShowOnlyDisassembledCode.CheckState = System.Windows.Forms.CheckState.Checked; - this.mnuShowOnlyDisassembledCode.Name = "mnuShowOnlyDisassembledCode"; - this.mnuShowOnlyDisassembledCode.Size = new System.Drawing.Size(258, 22); - this.mnuShowOnlyDisassembledCode.Text = "Show Only Disassembled Code"; - this.mnuShowOnlyDisassembledCode.Click += new System.EventHandler(this.mnuShowOnlyDisassembledCode_Click); - // // mnuShowLineNotes // this.mnuShowLineNotes.CheckOnClick = true; @@ -146,6 +134,7 @@ this.ctrlCodeViewer.MouseUp += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseUp); this.ctrlCodeViewer.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseMove); this.ctrlCodeViewer.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseDown); + this.ctrlCodeViewer.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.ctrlCodeViewer_MouseDoubleClick); this.ctrlCodeViewer.FontSizeChanged += new System.EventHandler(this.ctrlCodeViewer_FontSizeChanged); // // contextMenuMargin @@ -199,7 +188,6 @@ private System.Windows.Forms.ToolStripMenuItem mnuShowNextStatement; private System.Windows.Forms.ToolStripMenuItem mnuSetNextStatement; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; - private System.Windows.Forms.ToolStripMenuItem mnuShowOnlyDisassembledCode; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; private System.Windows.Forms.ToolStripMenuItem mnuGoToLocation; private System.Windows.Forms.ToolStripMenuItem mnuAddToWatch; diff --git a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs index bc5796f0..c108ae89 100644 --- a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs +++ b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs @@ -27,7 +27,6 @@ namespace Mesen.GUI.Debugger public void SetConfig(DebugViewInfo config) { _config = config; - this.mnuShowOnlyDisassembledCode.Checked = config.ShowOnlyDiassembledCode; this.mnuShowLineNotes.Checked = config.ShowPrgAddresses; this.mnuShowCodeNotes.Checked = config.ShowByteCode; this.FontSize = config.FontSize; @@ -38,7 +37,6 @@ namespace Mesen.GUI.Debugger private void UpdateConfig() { - _config.ShowOnlyDiassembledCode = this.mnuShowOnlyDisassembledCode.Checked; _config.ShowPrgAddresses = this.mnuShowLineNotes.Checked; _config.ShowByteCode = this.mnuShowCodeNotes.Checked; _config.FontSize = this.FontSize; @@ -102,50 +100,17 @@ namespace Mesen.GUI.Debugger List lineNumberNotes = new List(); List codeNotes = new List(); List codeLines = new List(); - bool diassembledCodeOnly = mnuShowOnlyDisassembledCode.Checked; - bool skippingCode = false; + string[] lines = _code.Split('\n'); for(int i = 0, len = lines.Length - 1; i < len; i++) { string line = lines[i]; - string[] lineParts = line.Split(':'); - if(skippingCode && (i == len - 1 || lineParts[3][0] != '.')) { - lineNumbers.Add(-1); - lineNumberNotes.Add(""); - codeLines.Add("[code not disassembled]"); - codeNotes.Add(""); - - int address = (int)ParseHexAddress(lineParts[0]); - if(i != len - 1 || lineParts[3][0] != '.') { - address--; - } else if(i == len - 1 && lineParts[3][0] == '.' && address >= 0xFFF8) { - address = 0xFFFF; - } - lineNumbers.Add(address); - lineNumberNotes.Add(lineParts[1]); - codeLines.Add("[code not disassembled]"); - codeNotes.Add(""); - - skippingCode = false; - if(i == len - 1 && lineParts[3][0] == '.') { - break; - } - } - + string[] lineParts = line.Split('\x1'); + if(lineParts.Length >= 4) { - if(diassembledCodeOnly && lineParts[3][0] == '.') { - if(!skippingCode) { - lineNumbers.Add((int)ParseHexAddress(lineParts[0])); - lineNumberNotes.Add(lineParts[1]); - codeLines.Add("[code not disassembled]"); - codeNotes.Add(""); - skippingCode = true; - } - } else { - lineNumbers.Add((int)ParseHexAddress(lineParts[0])); - lineNumberNotes.Add(lineParts[1]); - codeLines.Add(lineParts[3]); - codeNotes.Add(lineParts[2]); - } + lineNumbers.Add(ParseHexAddress(lineParts[0])); + lineNumberNotes.Add(lineParts[1]); + codeNotes.Add(lineParts[2]); + codeLines.Add(lineParts[3]); } } @@ -160,9 +125,13 @@ namespace Mesen.GUI.Debugger return false; } - private UInt32 ParseHexAddress(string hexAddress) + private int ParseHexAddress(string hexAddress) { - return UInt32.Parse(hexAddress, System.Globalization.NumberStyles.AllowHexSpecifier); + if(string.IsNullOrWhiteSpace(hexAddress)) { + return -1; + } else { + return (int)UInt32.Parse(hexAddress, System.Globalization.NumberStyles.AllowHexSpecifier); + } } public void HighlightBreakpoints() @@ -211,7 +180,19 @@ namespace Mesen.GUI.Debugger string valueText = "$" + memoryValue.ToString("X"); toolTip.Show(valueText, ctrlCodeViewer, e.Location.X + 5, e.Location.Y - 20, 3000); } else { - toolTip.Hide(ctrlCodeViewer); + CodeLabel label = LabelManager.GetLabel(word); + + if(label == null) { + toolTip.Hide(ctrlCodeViewer); + } else { + Byte memoryValue = InteropEmu.DebugGetMemoryValue(label.Address); + toolTip.Show( + "Label: " + label.Label + Environment.NewLine + + "Address: $" + InteropEmu.DebugGetRelativeAddress(label.Address).ToString("X4") + Environment.NewLine + + "Value: $" + memoryValue.ToString("X2") + Environment.NewLine + + "Comment: " + (label.Comment.Contains(Environment.NewLine) ? (Environment.NewLine + label.Comment) : label.Comment) + , ctrlCodeViewer, e.Location.X + 5, e.Location.Y - 60 - label.Comment.Split('\n').Length * 14, 3000); + } } _previousLocation = e.Location; } @@ -255,6 +236,26 @@ namespace Mesen.GUI.Debugger } } } + + private void ctrlCodeViewer_MouseDoubleClick(object sender, MouseEventArgs e) + { + int address = ctrlCodeViewer.GetLineNumberAtPosition(e.Y); + + if(address >= 0 && e.Location.X > this.ctrlCodeViewer.CodeMargin / 2 && e.Location.X < this.ctrlCodeViewer.CodeMargin) { + UInt32 absoluteAddr = (UInt32)InteropEmu.DebugGetAbsoluteAddress((UInt32)address); + CodeLabel existingLabel = LabelManager.GetLabel(absoluteAddr); + CodeLabel newLabel = new CodeLabel() { Label = existingLabel?.Label, Comment = existingLabel?.Comment }; + + frmEditLabel frm = new frmEditLabel(absoluteAddr, newLabel); + if(frm.ShowDialog() == DialogResult.OK) { + if(string.IsNullOrWhiteSpace(newLabel.Label) && string.IsNullOrWhiteSpace(newLabel.Comment)) { + LabelManager.DeleteLabel(absoluteAddr); + } else { + LabelManager.SetLabel(absoluteAddr, newLabel.Label, newLabel.Comment); + } + } + } + } #region Context Menu diff --git a/GUI.NET/Debugger/Controls/ctrlFunctionList.cs b/GUI.NET/Debugger/Controls/ctrlFunctionList.cs index fa889970..136c0e34 100644 --- a/GUI.NET/Debugger/Controls/ctrlFunctionList.cs +++ b/GUI.NET/Debugger/Controls/ctrlFunctionList.cs @@ -53,7 +53,8 @@ namespace Mesen.GUI.Debugger.Controls lstFunctions.ListViewItemSorter = null; lstFunctions.Items.Clear(); for(int i = 0; entryPoints[i] >= 0; i++) { - ListViewItem item = lstFunctions.Items.Add(""); + CodeLabel label = LabelManager.GetLabel((UInt32)entryPoints[i]); + ListViewItem item = lstFunctions.Items.Add(label?.Label); Int32 relativeAddress = InteropEmu.DebugGetRelativeAddress((UInt32)entryPoints[i]); if(relativeAddress >= 0) { diff --git a/GUI.NET/Debugger/Controls/ctrlLabelList.Designer.cs b/GUI.NET/Debugger/Controls/ctrlLabelList.Designer.cs new file mode 100644 index 00000000..db3bf590 --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlLabelList.Designer.cs @@ -0,0 +1,113 @@ +namespace Mesen.GUI.Debugger.Controls +{ + partial class ctrlLabelList + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.lstLabels = new System.Windows.Forms.ListView(); + this.colFunctionLabel = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colFunctionAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.colMemoryAddress = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mnuDelete = new System.Windows.Forms.ToolStripMenuItem(); + this.contextMenu.SuspendLayout(); + this.SuspendLayout(); + // + // lstLabels + // + this.lstLabels.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.colFunctionLabel, + this.colFunctionAddress, + this.colMemoryAddress}); + this.lstLabels.ContextMenuStrip = this.contextMenu; + this.lstLabels.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstLabels.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lstLabels.FullRowSelect = true; + this.lstLabels.GridLines = true; + this.lstLabels.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; + this.lstLabels.Location = new System.Drawing.Point(0, 0); + this.lstLabels.MultiSelect = false; + this.lstLabels.Name = "lstLabels"; + this.lstLabels.Size = new System.Drawing.Size(275, 112); + this.lstLabels.TabIndex = 2; + this.lstLabels.UseCompatibleStateImageBehavior = false; + this.lstLabels.View = System.Windows.Forms.View.Details; + this.lstLabels.DoubleClick += new System.EventHandler(this.lstLabels_DoubleClick); + // + // colFunctionLabel + // + this.colFunctionLabel.Text = "Label"; + this.colFunctionLabel.Width = 136; + // + // colFunctionAddress + // + this.colFunctionAddress.Text = "Address"; + this.colFunctionAddress.Width = 62; + // + // colMemoryAddress + // + this.colMemoryAddress.Text = "ROM Addr."; + this.colMemoryAddress.Width = 71; + // + // contextMenu + // + this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuDelete}); + this.contextMenu.Name = "contextMenu"; + this.contextMenu.Size = new System.Drawing.Size(153, 48); + this.contextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.mnuActions_Opening); + // + // mnuDelete + // + this.mnuDelete.Name = "mnuDelete"; + this.mnuDelete.ShortcutKeys = System.Windows.Forms.Keys.Delete; + this.mnuDelete.Size = new System.Drawing.Size(152, 22); + this.mnuDelete.Text = "Delete"; + this.mnuDelete.Click += new System.EventHandler(this.mnuDelete_Click); + // + // ctrlLabelList + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.lstLabels); + this.Name = "ctrlLabelList"; + this.Size = new System.Drawing.Size(275, 112); + this.contextMenu.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView lstLabels; + private System.Windows.Forms.ColumnHeader colFunctionLabel; + private System.Windows.Forms.ColumnHeader colFunctionAddress; + private System.Windows.Forms.ColumnHeader colMemoryAddress; + private System.Windows.Forms.ContextMenuStrip contextMenu; + private System.Windows.Forms.ToolStripMenuItem mnuDelete; + } +} diff --git a/GUI.NET/Debugger/Controls/ctrlLabelList.cs b/GUI.NET/Debugger/Controls/ctrlLabelList.cs new file mode 100644 index 00000000..e159f1a8 --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlLabelList.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Collections; + +namespace Mesen.GUI.Debugger.Controls +{ + public partial class ctrlLabelList : UserControl + { + public event EventHandler OnLabelSelected; + public ctrlLabelList() + { + InitializeComponent(); + } + + public void UpdateLabelList() + { + Int32[] entryPoints = InteropEmu.DebugGetFunctionEntryPoints(); + + lstLabels.BeginUpdate(); + lstLabels.Items.Clear(); + foreach(KeyValuePair kvp in LabelManager.GetLabels()) { + if(kvp.Value.Label.Length > 0) { + ListViewItem item = lstLabels.Items.Add(kvp.Value.Label); + + Int32 relativeAddress = InteropEmu.DebugGetRelativeAddress(kvp.Value.Address); + if(relativeAddress >= 0) { + item.SubItems.Add("$" + relativeAddress.ToString("X4")); + } else { + item.SubItems.Add("[n/a]"); + item.ForeColor = Color.Gray; + item.Font = new Font(item.Font, FontStyle.Italic); + } + item.SubItems.Add("$" + kvp.Value.Address.ToString("X4")); + item.SubItems[1].Tag = kvp.Value.Address; + + item.Tag = relativeAddress; + } + } + lstLabels.Sort(); + lstLabels.EndUpdate(); + } + + private void lstLabels_DoubleClick(object sender, EventArgs e) + { + if(lstLabels.SelectedItems.Count > 0) { + Int32 relativeAddress = (Int32)lstLabels.SelectedItems[0].Tag; + + if(relativeAddress >= 0) { + OnLabelSelected?.Invoke(relativeAddress, e); + } + } + } + + private void mnuActions_Opening(object sender, CancelEventArgs e) + { + mnuDelete.Enabled = lstLabels.SelectedItems.Count > 0; + } + + private void mnuDelete_Click(object sender, EventArgs e) + { + foreach(ListViewItem item in lstLabels.SelectedItems) { + LabelManager.DeleteLabel((UInt32)item.SubItems[1].Tag); + } + } + } +} diff --git a/GUI.NET/Debugger/Controls/ctrlLabelList.resx b/GUI.NET/Debugger/Controls/ctrlLabelList.resx new file mode 100644 index 00000000..9c222a09 --- /dev/null +++ b/GUI.NET/Debugger/Controls/ctrlLabelList.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs b/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs index 8c898f73..5fa4dc2e 100644 --- a/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs +++ b/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs @@ -30,6 +30,12 @@ namespace Mesen.GUI.Debugger remove { this.ctrlTextbox.MouseDown -= value; } } + public new event MouseEventHandler MouseDoubleClick + { + add { this.ctrlTextbox.MouseDoubleClick += value; } + remove { this.ctrlTextbox.MouseDoubleClick -= value; } + } + public event EventHandler FontSizeChanged; public ctrlScrollableTextbox() diff --git a/GUI.NET/Debugger/Controls/ctrlTextbox.cs b/GUI.NET/Debugger/Controls/ctrlTextbox.cs index 71361cbd..77fe5856 100644 --- a/GUI.NET/Debugger/Controls/ctrlTextbox.cs +++ b/GUI.NET/Debugger/Controls/ctrlTextbox.cs @@ -36,6 +36,7 @@ namespace Mesen.GUI.Debugger private string[] _contentNotes = new string[0]; private string[] _compareContents = null; private int[] _lineNumbers = new int[0]; + private int[] _lineMargins = new int[0]; private string[] _lineNumberNotes = new string[0]; private Dictionary _lineNumberIndex = new Dictionary(); private Dictionary _lineProperties = new Dictionary(); @@ -61,7 +62,13 @@ namespace Mesen.GUI.Debugger { set { - _contents = value; + _contents = new string[value.Length]; + _lineMargins = new int[value.Length]; + for(int i = 0, len = value.Length; i < len; i++) { + _contents[i] = value[i].TrimStart(); + _lineMargins[i] = (value[i].Length - _contents[i].Length) * 10; + } + _lineNumbers = new int[_contents.Length]; _lineNumberIndex.Clear(); for(int i = _contents.Length - 1; i >=0; i--) { @@ -317,7 +324,10 @@ namespace Mesen.GUI.Debugger } if(positionX >= 0 && lineIndex < _contents.Length) { - string text = _contents[lineIndex]; + string text = _contents[lineIndex].Replace("\x2", ""); + //Adjust background color highlights based on number of spaces in front of content + positionX -= _lineMargins[lineIndex]; + int previousWidth = 0; for(int i = 0, len = text.Length; i < len; i++) { int width = (int)g.MeasureString(text.Substring(0, i+1), this.Font).Width; @@ -337,7 +347,7 @@ namespace Mesen.GUI.Debugger int charIndex; int lineIndex; if(this.GetCharIndex(position, out charIndex, out lineIndex)) { - string text = (useCompareText && _compareContents != null) ? _compareContents[lineIndex] : _contents[lineIndex]; + string text = ((useCompareText && _compareContents != null) ? _compareContents[lineIndex] : _contents[lineIndex]).Replace("\x2", ""); List wordDelimiters = new List(new char[] { ' ', ',', '|', ';', '(', ')' }); if(wordDelimiters.Contains(text[charIndex])) { return string.Empty; @@ -444,8 +454,8 @@ namespace Mesen.GUI.Debugger private void DrawLine(Graphics g, int currentLine, int marginLeft, int positionY) { - string[] lineContent = _contents[currentLine].Split(new string[] { "||" }, StringSplitOptions.None); - string codeString = lineContent.Length > 0 ? lineContent[0] : ""; + string[] lineContent = _contents[currentLine].Split('\x2'); + string codeString = lineContent[0].TrimStart(); string addressString = lineContent.Length > 1 ? lineContent[1] : ""; string commentString = lineContent.Length > 2 ? lineContent[2] : ""; @@ -454,13 +464,7 @@ namespace Mesen.GUI.Debugger if(this.ShowLineNumbers) { //Show line number - string lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : ".."; - float width = g.MeasureString(lineNumber, this.Font).Width; - g.DrawString(lineNumber, this.Font, Brushes.Gray, marginLeft - width, positionY); - if(this.ShowLineNumberNotes) { - width = g.MeasureString(_lineNumberNotes[currentLine], _noteFont).Width; - g.DrawString(_lineNumberNotes[currentLine], _noteFont, Brushes.Gray, marginLeft - width, positionY+this.Font.Size+3); - } + this.DrawLineNumber(g, currentLine, marginLeft, positionY); } if(currentLine == this.CursorPosition) { @@ -468,6 +472,9 @@ namespace Mesen.GUI.Debugger g.FillRectangle(Brushes.AliceBlue, marginLeft, positionY, this.ClientRectangle.Width - marginLeft, this.LineHeight); } + //Adjust background color highlights based on number of spaces in front of content + marginLeft += _lineMargins[currentLine]; + Color textColor = Color.Black; if(_lineProperties.ContainsKey(currentLine)) { //Process background, foreground, outline color and line symbol @@ -476,59 +483,100 @@ namespace Mesen.GUI.Debugger if(lineProperties.BgColor.HasValue) { using(Brush bgBrush = new SolidBrush(lineProperties.BgColor.Value)) { - g.FillRectangle(bgBrush, marginLeft + 1, positionY + 1, codeStringLength, this.LineHeight-1); + g.FillRectangle(bgBrush, marginLeft, positionY + 1, codeStringLength, this.LineHeight-1); } } if(lineProperties.OutlineColor.HasValue) { using(Pen outlinePen = new Pen(lineProperties.OutlineColor.Value, 1)) { - g.DrawRectangle(outlinePen, marginLeft + 1, positionY + 1, codeStringLength, this.LineHeight-1); + g.DrawRectangle(outlinePen, marginLeft, positionY + 1, codeStringLength, this.LineHeight-1); } } - if(lineProperties.Symbol.HasFlag(LineSymbol.Circle)) { - using(Brush brush = new SolidBrush(lineProperties.OutlineColor.Value)) { - g.FillEllipse(brush, 1, positionY + 2, this.LineHeight - 3, this.LineHeight - 3); - } - } - if(lineProperties.Symbol.HasFlag(LineSymbol.CircleOutline) && lineProperties.OutlineColor.HasValue) { - using(Pen pen = new Pen(lineProperties.OutlineColor.Value, 1)) { - g.DrawEllipse(pen, 1, positionY + 2, this.LineHeight - 3, this.LineHeight - 3); - } - } - if(lineProperties.Symbol.HasFlag(LineSymbol.Arrow)) { - int arrowY = positionY + this.LineHeight / 2 + 1; - using(Pen pen = new Pen(Color.Black, this.LineHeight * 0.33f)) { - //Outline - g.DrawLine(pen, 3, arrowY, 3 + this.LineHeight * 0.25f, arrowY); - pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor; - g.DrawLine(pen, 3 + this.LineHeight * 0.25f, arrowY, 3 + this.LineHeight * 0.75f, arrowY); - - //Fill - pen.Width-=2f; - pen.Color = lineProperties.BgColor.Value; - pen.EndCap = System.Drawing.Drawing2D.LineCap.Square; - g.DrawLine(pen, 4, arrowY, 3 + this.LineHeight * 0.25f - 1, arrowY); - pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor; - g.DrawLine(pen, 3 + this.LineHeight * 0.25f, arrowY, this.LineHeight * 0.75f + 1, arrowY); - } - } + this.DrawLineSymbols(g, positionY, lineProperties); } + this.DrawLineText(g, currentLine, marginLeft, positionY, codeString, addressString, commentString, codeStringLength, addressStringLength, textColor); + } + + private void DrawLineNumber(Graphics g, int currentLine, int marginLeft, int positionY) + { + string lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : ".."; + float width = g.MeasureString(lineNumber, this.Font).Width; + g.DrawString(lineNumber, this.Font, Brushes.Gray, marginLeft - width, positionY); + if(this.ShowLineNumberNotes) { + width = g.MeasureString(_lineNumberNotes[currentLine], _noteFont).Width; + g.DrawString(_lineNumberNotes[currentLine], _noteFont, Brushes.Gray, marginLeft - width, positionY+this.Font.Size+3); + } + } + + private void DrawLineText(Graphics g, int currentLine, int marginLeft, int positionY, string codeString, string addressString, string commentString, float codeStringLength, float addressStringLength, Color textColor) + { using(Brush fgBrush = new SolidBrush(textColor)) { - g.DrawString(codeString, this.Font, fgBrush, marginLeft, positionY); + if(codeString.StartsWith("--") && codeString.EndsWith("--")) { + //Draw block start + string text = codeString.Substring(2, codeString.Length - 4); + float textLength = g.MeasureString(text, this._noteFont).Width; + g.DrawString(text, this._noteFont, fgBrush, (marginLeft + this.Width - textLength) / 2, positionY); + g.DrawLine(Pens.Black, marginLeft, positionY+this.LineHeight-2, marginLeft+this.Width, positionY+this.LineHeight-2); + } else if(codeString.StartsWith("__") && codeString.EndsWith("__")) { + //Draw block end + string text = codeString.Substring(2, codeString.Length - 4); + float textLength = g.MeasureString(text, this._noteFont).Width; + g.DrawString(text, this._noteFont, fgBrush, (marginLeft + this.Width - textLength) / 2, positionY + 4); + g.DrawLine(Pens.Black, marginLeft, positionY+2, marginLeft+this.Width, positionY+2); + } else if(codeString.StartsWith("[[") && codeString.EndsWith("]]")) { + //Draw small centered text + string text = codeString.Substring(2, codeString.Length - 4); + float textLength = g.MeasureString(text, this._noteFont).Width; + g.DrawString(text, new Font(this._noteFont, FontStyle.Italic), fgBrush, (marginLeft + this.Width - textLength) / 2, positionY + 2); + } else { + //Draw line content + g.DrawString(codeString, this.Font, fgBrush, marginLeft, positionY); - using(Brush addressBrush = new SolidBrush(Color.SteelBlue)) { - g.DrawString(addressString, this.Font, addressBrush, marginLeft + codeStringLength, positionY); - } - using(Brush commentBrush = new SolidBrush(Color.DarkGreen)) { - g.DrawString(commentString, this.Font, commentBrush, Math.Max(marginLeft + 220, marginLeft + codeStringLength + addressStringLength), positionY); - } + using(Brush addressBrush = new SolidBrush(Color.SteelBlue)) { + g.DrawString(addressString, this.Font, addressBrush, marginLeft + codeStringLength - 4, positionY); + } + using(Brush commentBrush = new SolidBrush(Color.DarkGreen)) { + g.DrawString(commentString, this.Font, commentBrush, codeString.Length == 0 && addressString.Length == 0 ? marginLeft : Math.Max(marginLeft + 220, marginLeft + codeStringLength + addressStringLength), positionY); + } - if(this.ShowContentNotes) { - g.DrawString(_contentNotes[currentLine], _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3); + if(this.ShowContentNotes) { + g.DrawString(_contentNotes[currentLine], _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3); + } + this.DrawHighlightedSearchString(g, codeString, marginLeft, positionY); + this.DrawHighlightedCompareString(g, codeString, currentLine, marginLeft, positionY); + } + } + } + + private void DrawLineSymbols(Graphics g, int positionY, LineProperties lineProperties) + { + if(lineProperties.Symbol.HasFlag(LineSymbol.Circle)) { + using(Brush brush = new SolidBrush(lineProperties.OutlineColor.Value)) { + g.FillEllipse(brush, 1, positionY + 2, this.LineHeight - 3, this.LineHeight - 3); + } + } + if(lineProperties.Symbol.HasFlag(LineSymbol.CircleOutline) && lineProperties.OutlineColor.HasValue) { + using(Pen pen = new Pen(lineProperties.OutlineColor.Value, 1)) { + g.DrawEllipse(pen, 1, positionY + 2, this.LineHeight - 3, this.LineHeight - 3); + } + } + if(lineProperties.Symbol.HasFlag(LineSymbol.Arrow)) { + int arrowY = positionY + this.LineHeight / 2 + 1; + using(Pen pen = new Pen(Color.Black, this.LineHeight * 0.33f)) { + //Outline + g.DrawLine(pen, 3, arrowY, 3 + this.LineHeight * 0.25f, arrowY); + pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor; + g.DrawLine(pen, 3 + this.LineHeight * 0.25f, arrowY, 3 + this.LineHeight * 0.75f, arrowY); + + //Fill + pen.Width-=2f; + pen.Color = lineProperties.BgColor.Value; + pen.EndCap = System.Drawing.Drawing2D.LineCap.Square; + g.DrawLine(pen, 4, arrowY, 3 + this.LineHeight * 0.25f - 1, arrowY); + pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor; + g.DrawLine(pen, 3 + this.LineHeight * 0.25f, arrowY, this.LineHeight * 0.75f + 1, arrowY); } - this.DrawHighlightedSearchString(g, codeString, marginLeft, positionY); - this.DrawHighlightedCompareString(g, codeString, currentLine, marginLeft, positionY); } } diff --git a/GUI.NET/Debugger/LabelManager.cs b/GUI.NET/Debugger/LabelManager.cs new file mode 100644 index 00000000..99d007f6 --- /dev/null +++ b/GUI.NET/Debugger/LabelManager.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Debugger +{ + public class CodeLabel + { + public UInt32 Address; + public string Label; + public string Comment; + } + + public class LabelManager + { + private static Dictionary _labels = new Dictionary(); + private static Dictionary _reverseLookup = new Dictionary(); + + public static event EventHandler OnLabelUpdated; + + public static CodeLabel GetLabel(UInt32 address) + { + return _labels.ContainsKey(address) ? _labels[address] : null; + } + + public static CodeLabel GetLabel(string label) + { + return _reverseLookup.ContainsKey(label) ? _reverseLookup[label] : null; + } + + public static Dictionary GetLabels() + { + return _labels; + } + + public static bool SetLabel(UInt32 address, string label, string comment) + { + if(_reverseLookup.ContainsKey(label) && _reverseLookup[label].Address != address) { + //Label already exists + return false; + } + + if(_labels.ContainsKey(address)) { + _reverseLookup.Remove(_labels[address].Label); + } + + _labels[address] = new CodeLabel() { Address = address, Label = label, Comment = comment }; + if(label.Length > 0) { + _reverseLookup[label] = _labels[address]; + } + + InteropEmu.DebugSetLabel(address, label, comment); + OnLabelUpdated?.Invoke(null, null); + + return true; + } + + public static void DeleteLabel(UInt32 address) + { + if(_labels.ContainsKey(address)) { + _reverseLookup.Remove(_labels[address].Label); + } + if(_labels.Remove(address)) { + InteropEmu.DebugSetLabel(address, string.Empty, string.Empty); + OnLabelUpdated?.Invoke(null, null); + } + } + } +} diff --git a/GUI.NET/Debugger/frmDebugger.Designer.cs b/GUI.NET/Debugger/frmDebugger.Designer.cs index a2af44fc..c7b0ff17 100644 --- a/GUI.NET/Debugger/frmDebugger.Designer.cs +++ b/GUI.NET/Debugger/frmDebugger.Designer.cs @@ -43,6 +43,7 @@ this.ctrlDebuggerCodeSplit = new Mesen.GUI.Debugger.ctrlDebuggerCode(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.grpLabels = new System.Windows.Forms.GroupBox(); + this.ctrlLabelList = new Mesen.GUI.Debugger.Controls.ctrlLabelList(); this.grpFunctions = new System.Windows.Forms.GroupBox(); this.ctrlFunctionList = new Mesen.GUI.Debugger.Controls.ctrlFunctionList(); this.tableLayoutPanel10 = new System.Windows.Forms.TableLayoutPanel(); @@ -111,6 +112,7 @@ this.lblChrAnalysisResult = new System.Windows.Forms.ToolStripStatusLabel(); this.ctrlPpuMemoryMapping = new Mesen.GUI.Debugger.Controls.ctrlMemoryMapping(); this.ctrlCpuMemoryMapping = new Mesen.GUI.Debugger.Controls.ctrlMemoryMapping(); + this.mnuShowOnlyDisassembledCode = new System.Windows.Forms.ToolStripMenuItem(); this.contextMenuCode.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); this.splitContainer.Panel1.SuspendLayout(); @@ -118,6 +120,7 @@ this.splitContainer.SuspendLayout(); this.tlpTop.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout(); + this.grpLabels.SuspendLayout(); this.grpFunctions.SuspendLayout(); this.tableLayoutPanel10.SuspendLayout(); this.grpWatch.SuspendLayout(); @@ -250,13 +253,23 @@ // // grpLabels // + this.grpLabels.Controls.Add(this.ctrlLabelList); this.grpLabels.Dock = System.Windows.Forms.DockStyle.Fill; this.grpLabels.Location = new System.Drawing.Point(3, 196); this.grpLabels.Name = "grpLabels"; this.grpLabels.Size = new System.Drawing.Size(304, 188); this.grpLabels.TabIndex = 6; this.grpLabels.TabStop = false; - this.grpLabels.Text = "Labels and Comments"; + this.grpLabels.Text = "Labels"; + // + // ctrlLabelList + // + this.ctrlLabelList.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlLabelList.Location = new System.Drawing.Point(3, 16); + this.ctrlLabelList.Name = "ctrlLabelList"; + this.ctrlLabelList.Size = new System.Drawing.Size(298, 169); + this.ctrlLabelList.TabIndex = 0; + this.ctrlLabelList.OnLabelSelected += new System.EventHandler(this.ctrlLabelList_OnLabelSelected); // // grpFunctions // @@ -393,12 +406,12 @@ // toolStripMenuItem3 // this.toolStripMenuItem3.Name = "toolStripMenuItem3"; - this.toolStripMenuItem3.Size = new System.Drawing.Size(100, 6); + this.toolStripMenuItem3.Size = new System.Drawing.Size(149, 6); // // mnuClose // this.mnuClose.Name = "mnuClose"; - this.mnuClose.Size = new System.Drawing.Size(103, 22); + this.mnuClose.Size = new System.Drawing.Size(152, 22); this.mnuClose.Text = "Close"; this.mnuClose.Click += new System.EventHandler(this.mnuClose_Click); // @@ -598,7 +611,8 @@ this.mnuShowPpuMemoryMapping, this.toolStripMenuItem6, this.mnuPpuPartialDraw, - this.mnuShowEffectiveAddresses}); + this.mnuShowEffectiveAddresses, + this.mnuShowOnlyDisassembledCode}); this.mnuOptions.Name = "mnuOptions"; this.mnuOptions.Size = new System.Drawing.Size(61, 20); this.mnuOptions.Text = "Options"; @@ -607,7 +621,7 @@ // this.mnuSplitView.CheckOnClick = true; this.mnuSplitView.Name = "mnuSplitView"; - this.mnuSplitView.Size = new System.Drawing.Size(228, 22); + this.mnuSplitView.Size = new System.Drawing.Size(237, 22); this.mnuSplitView.Text = "Split View"; this.mnuSplitView.Click += new System.EventHandler(this.mnuSplitView_Click); // @@ -618,7 +632,7 @@ this.mnuDecreaseFontSize, this.mnuResetFontSize}); this.fontSizeToolStripMenuItem.Name = "fontSizeToolStripMenuItem"; - this.fontSizeToolStripMenuItem.Size = new System.Drawing.Size(228, 22); + this.fontSizeToolStripMenuItem.Size = new System.Drawing.Size(237, 22); this.fontSizeToolStripMenuItem.Text = "Text Size"; // // mnuIncreaseFontSize @@ -651,13 +665,13 @@ // toolStripMenuItem5 // this.toolStripMenuItem5.Name = "toolStripMenuItem5"; - this.toolStripMenuItem5.Size = new System.Drawing.Size(225, 6); + this.toolStripMenuItem5.Size = new System.Drawing.Size(234, 6); // // mnuShowCpuMemoryMapping // this.mnuShowCpuMemoryMapping.CheckOnClick = true; this.mnuShowCpuMemoryMapping.Name = "mnuShowCpuMemoryMapping"; - this.mnuShowCpuMemoryMapping.Size = new System.Drawing.Size(228, 22); + this.mnuShowCpuMemoryMapping.Size = new System.Drawing.Size(237, 22); this.mnuShowCpuMemoryMapping.Text = "Show CPU Memory Mapping"; this.mnuShowCpuMemoryMapping.CheckedChanged += new System.EventHandler(this.mnuShowCpuMemoryMapping_CheckedChanged); // @@ -665,20 +679,20 @@ // this.mnuShowPpuMemoryMapping.CheckOnClick = true; this.mnuShowPpuMemoryMapping.Name = "mnuShowPpuMemoryMapping"; - this.mnuShowPpuMemoryMapping.Size = new System.Drawing.Size(228, 22); + this.mnuShowPpuMemoryMapping.Size = new System.Drawing.Size(237, 22); this.mnuShowPpuMemoryMapping.Text = "Show PPU Memory Mapping"; this.mnuShowPpuMemoryMapping.CheckedChanged += new System.EventHandler(this.mnuShowPpuMemoryMapping_CheckedChanged); // // toolStripMenuItem6 // this.toolStripMenuItem6.Name = "toolStripMenuItem6"; - this.toolStripMenuItem6.Size = new System.Drawing.Size(225, 6); + this.toolStripMenuItem6.Size = new System.Drawing.Size(234, 6); // // mnuPpuPartialDraw // this.mnuPpuPartialDraw.CheckOnClick = true; this.mnuPpuPartialDraw.Name = "mnuPpuPartialDraw"; - this.mnuPpuPartialDraw.Size = new System.Drawing.Size(228, 22); + this.mnuPpuPartialDraw.Size = new System.Drawing.Size(237, 22); this.mnuPpuPartialDraw.Text = "Draw Partial Frame"; this.mnuPpuPartialDraw.Click += new System.EventHandler(this.mnuPpuPartialDraw_Click); // @@ -686,9 +700,9 @@ // this.mnuShowEffectiveAddresses.CheckOnClick = true; this.mnuShowEffectiveAddresses.Name = "mnuShowEffectiveAddresses"; - this.mnuShowEffectiveAddresses.Size = new System.Drawing.Size(228, 22); + this.mnuShowEffectiveAddresses.Size = new System.Drawing.Size(237, 22); this.mnuShowEffectiveAddresses.Text = "Show Effective Addresses"; - this.mnuShowEffectiveAddresses.Click += new System.EventHandler(this.mnuShowEffectiveAddresses_Click); + this.mnuShowEffectiveAddresses.CheckedChanged += new System.EventHandler(this.mnuShowEffectiveAddresses_CheckedChanged); // // toolsToolStripMenuItem // @@ -852,6 +866,14 @@ this.ctrlCpuMemoryMapping.Text = "ctrlMemoryMapping1"; this.ctrlCpuMemoryMapping.Visible = false; // + // mnuShowOnlyDisassembledCode + // + this.mnuShowOnlyDisassembledCode.CheckOnClick = true; + this.mnuShowOnlyDisassembledCode.Name = "mnuShowOnlyDisassembledCode"; + this.mnuShowOnlyDisassembledCode.Size = new System.Drawing.Size(237, 22); + this.mnuShowOnlyDisassembledCode.Text = "Show Only Disassembled Code"; + this.mnuShowOnlyDisassembledCode.CheckedChanged += new System.EventHandler(this.mnuShowOnlyDisassembledCode_CheckedChanged); + // // frmDebugger // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -875,6 +897,7 @@ this.splitContainer.ResumeLayout(false); this.tlpTop.ResumeLayout(false); this.tableLayoutPanel1.ResumeLayout(false); + this.grpLabels.ResumeLayout(false); this.grpFunctions.ResumeLayout(false); this.tableLayoutPanel10.ResumeLayout(false); this.grpWatch.ResumeLayout(false); @@ -971,5 +994,7 @@ private System.Windows.Forms.GroupBox grpLabels; private System.Windows.Forms.GroupBox grpFunctions; private Controls.ctrlFunctionList ctrlFunctionList; + private Controls.ctrlLabelList ctrlLabelList; + private System.Windows.Forms.ToolStripMenuItem mnuShowOnlyDisassembledCode; } } \ No newline at end of file diff --git a/GUI.NET/Debugger/frmDebugger.cs b/GUI.NET/Debugger/frmDebugger.cs index 3b1082a5..c395743e 100644 --- a/GUI.NET/Debugger/frmDebugger.cs +++ b/GUI.NET/Debugger/frmDebugger.cs @@ -35,6 +35,9 @@ namespace Mesen.GUI.Debugger this.mnuShowEffectiveAddresses.Checked = ConfigManager.Config.DebugInfo.ShowEffectiveAddresses; this.mnuShowCpuMemoryMapping.Checked = ConfigManager.Config.DebugInfo.ShowCpuMemoryMapping; this.mnuShowPpuMemoryMapping.Checked = ConfigManager.Config.DebugInfo.ShowPpuMemoryMapping; + this.mnuShowOnlyDisassembledCode.Checked = ConfigManager.Config.DebugInfo.ShowOnlyDisassembledCode; + + LabelManager.OnLabelUpdated += LabelManager_OnLabelUpdated; _lastCodeWindow = ctrlDebuggerCode; @@ -94,6 +97,9 @@ namespace Mesen.GUI.Debugger if(mnuShowEffectiveAddresses.Checked) { flags |= DebuggerFlags.ShowEffectiveAddresses; } + if(mnuShowOnlyDisassembledCode.Checked) { + flags |= DebuggerFlags.ShowOnlyDisassembledCode; + } InteropEmu.DebugSetFlags(flags); } @@ -129,13 +135,13 @@ namespace Mesen.GUI.Debugger return mnuSplitView.Checked; } - private void UpdateDebugger() + private void UpdateDebugger(bool updateActiveAddress = true) { ctrlFunctionList.UpdateFunctionList(); UpdateDebuggerFlags(); if(InteropEmu.DebugIsCodeChanged()) { - string code = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(InteropEmu.DebugGetCode()); + string code = InteropEmu.DebugGetCode(); ctrlDebuggerCode.Code = code; ctrlDebuggerCodeSplit.Code = code; } @@ -149,8 +155,10 @@ namespace Mesen.GUI.Debugger _lastCodeWindow = ctrlDebuggerCode; } - ctrlDebuggerCode.SelectActiveAddress(state.CPU.DebugPC); - ctrlDebuggerCodeSplit.SetActiveAddress(state.CPU.DebugPC); + if(updateActiveAddress) { + ctrlDebuggerCode.SelectActiveAddress(state.CPU.DebugPC); + ctrlDebuggerCodeSplit.SetActiveAddress(state.CPU.DebugPC); + } RefreshBreakpoints(); ctrlConsoleStatus.UpdateStatus(ref state); @@ -282,7 +290,7 @@ namespace Mesen.GUI.Debugger ConfigManager.Config.DebugInfo.SplitView = this.mnuSplitView.Checked; ConfigManager.ApplyChanges(); - UpdateDebugger(); + UpdateDebugger(false); } private void mnuMemoryViewer_Click(object sender, EventArgs e) @@ -423,11 +431,18 @@ namespace Mesen.GUI.Debugger ConfigManager.ApplyChanges(); } - private void mnuShowEffectiveAddresses_Click(object sender, EventArgs e) + private void mnuShowEffectiveAddresses_CheckedChanged(object sender, EventArgs e) { ConfigManager.Config.DebugInfo.ShowEffectiveAddresses = mnuShowEffectiveAddresses.Checked; ConfigManager.ApplyChanges(); - UpdateDebugger(); + UpdateDebugger(false); + } + + private void mnuShowOnlyDisassembledCode_CheckedChanged(object sender, EventArgs e) + { + ConfigManager.Config.DebugInfo.ShowOnlyDisassembledCode = mnuShowOnlyDisassembledCode.Checked; + ConfigManager.ApplyChanges(); + UpdateDebugger(false); } private void mnuShowCpuMemoryMapping_CheckedChanged(object sender, EventArgs e) @@ -458,5 +473,16 @@ namespace Mesen.GUI.Debugger { _lastCodeWindow.ScrollToLineNumber((Int32)relativeAddress); } + + private void LabelManager_OnLabelUpdated(object sender, EventArgs e) + { + UpdateDebugger(false); + ctrlLabelList.UpdateLabelList(); + } + + private void ctrlLabelList_OnLabelSelected(object relativeAddress, EventArgs e) + { + _lastCodeWindow.ScrollToLineNumber((Int32)relativeAddress); + } } } diff --git a/GUI.NET/Debugger/frmEditLabel.Designer.cs b/GUI.NET/Debugger/frmEditLabel.Designer.cs new file mode 100644 index 00000000..7ac7353c --- /dev/null +++ b/GUI.NET/Debugger/frmEditLabel.Designer.cs @@ -0,0 +1,125 @@ +namespace Mesen.GUI.Debugger +{ + partial class frmEditLabel + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.txtComment = new System.Windows.Forms.TextBox(); + this.lblLabel = new System.Windows.Forms.Label(); + this.lblComment = new System.Windows.Forms.Label(); + this.txtLabel = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // baseConfigPanel + // + this.baseConfigPanel.Location = new System.Drawing.Point(0, 165); + this.baseConfigPanel.Size = new System.Drawing.Size(352, 29); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.txtComment, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.lblLabel, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.lblComment, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.txtLabel, 1, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(352, 165); + this.tableLayoutPanel1.TabIndex = 2; + // + // txtComment + // + this.txtComment.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtComment.Location = new System.Drawing.Point(63, 29); + this.txtComment.Multiline = true; + this.txtComment.Name = "txtComment"; + this.txtComment.Size = new System.Drawing.Size(286, 133); + this.txtComment.TabIndex = 3; + // + // lblLabel + // + this.lblLabel.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblLabel.AutoSize = true; + this.lblLabel.Location = new System.Drawing.Point(3, 6); + this.lblLabel.Name = "lblLabel"; + this.lblLabel.Size = new System.Drawing.Size(36, 13); + this.lblLabel.TabIndex = 0; + this.lblLabel.Text = "Label:"; + // + // lblComment + // + this.lblComment.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblComment.AutoSize = true; + this.lblComment.Location = new System.Drawing.Point(3, 89); + this.lblComment.Name = "lblComment"; + this.lblComment.Size = new System.Drawing.Size(54, 13); + this.lblComment.TabIndex = 1; + this.lblComment.Text = "Comment:"; + // + // txtLabel + // + this.txtLabel.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtLabel.Location = new System.Drawing.Point(63, 3); + this.txtLabel.Name = "txtLabel"; + this.txtLabel.Size = new System.Drawing.Size(286, 20); + this.txtLabel.TabIndex = 2; + // + // frmEditLabel + // + this.AcceptButton = null; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(352, 194); + this.Controls.Add(this.tableLayoutPanel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "frmEditLabel"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Edit Label"; + this.Controls.SetChildIndex(this.baseConfigPanel, 0); + this.Controls.SetChildIndex(this.tableLayoutPanel1, 0); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.TextBox txtComment; + private System.Windows.Forms.Label lblLabel; + private System.Windows.Forms.Label lblComment; + private System.Windows.Forms.TextBox txtLabel; + } +} \ No newline at end of file diff --git a/GUI.NET/Debugger/frmEditLabel.cs b/GUI.NET/Debugger/frmEditLabel.cs new file mode 100644 index 00000000..7ecc83f3 --- /dev/null +++ b/GUI.NET/Debugger/frmEditLabel.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Mesen.GUI.Forms; + +namespace Mesen.GUI.Debugger +{ + public partial class frmEditLabel : BaseConfigForm + { + UInt32 _address; + + public frmEditLabel(UInt32 address, CodeLabel label) + { + InitializeComponent(); + + _address = address; + this.Text = "Edit Label: $" + address.ToString("X4"); + + Entity = label; + + AddBinding("Label", txtLabel); + AddBinding("Comment", txtComment); + } + + protected override bool ValidateInput() + { + CodeLabel existingLabel = LabelManager.GetLabel(txtLabel.Text); + return (existingLabel == null || existingLabel.Address == _address) + && !txtComment.Text.Contains('\x1') && !txtComment.Text.Contains('\x2') + && !txtLabel.Text.Contains('\x1') && !txtLabel.Text.Contains('\x2'); + } + + protected override void OnFormClosed(FormClosedEventArgs e) + { + txtLabel.Text = txtLabel.Text.Replace("$", ""); + base.OnFormClosed(e); + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if(keyData == (Keys.Control | Keys.Enter)) { + this.DialogResult = DialogResult.OK; + this.Close(); + } + return base.ProcessCmdKey(ref msg, keyData); + } + } +} diff --git a/GUI.NET/Debugger/frmEditLabel.resx b/GUI.NET/Debugger/frmEditLabel.resx new file mode 100644 index 00000000..8766f298 --- /dev/null +++ b/GUI.NET/Debugger/frmEditLabel.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index 1dea6d4b..0e5cd52f 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -278,6 +278,12 @@ ctrlAddressList.cs + + UserControl + + + ctrlLabelList.cs + UserControl @@ -343,6 +349,12 @@ ctrlWatch.cs + + Form + + + frmEditLabel.cs + Form @@ -379,6 +391,7 @@ frmTraceLogger.cs + BaseConfigForm.cs @@ -582,6 +595,9 @@ ctrlAddressList.cs + + ctrlLabelList.cs + ctrlFunctionList.cs @@ -612,6 +628,9 @@ ctrlWatch.cs + + frmEditLabel.cs + frmBreakpoint.cs diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 3b639cee..95a4adf8 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -166,6 +166,7 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void DebugSetFlags(DebuggerFlags flags); [DllImport(DLLPath)] public static extern void DebugGetState(ref DebugState state); [DllImport(DLLPath)] public static extern void DebugSetBreakpoints([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]InteropBreakpoint[] breakpoints, UInt32 length); + [DllImport(DLLPath)] public static extern void DebugSetLabel(UInt32 address, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string label, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string comment); [DllImport(DLLPath)] public static extern void DebugStep(UInt32 count); [DllImport(DLLPath)] public static extern void DebugPpuStep(UInt32 count); [DllImport(DLLPath)] public static extern void DebugStepCycles(UInt32 count); @@ -173,9 +174,9 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void DebugStepOver(); [DllImport(DLLPath)] public static extern void DebugRun(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugIsCodeChanged(); - [DllImport(DLLPath)] public static extern IntPtr DebugGetCode(); [DllImport(DLLPath)] public static extern Byte DebugGetMemoryValue(UInt32 addr); - [DllImport(DLLPath)] public static extern Int32 DebugGetRelativeAddress(UInt32 addr); + [DllImport(DLLPath)] public static extern Int32 DebugGetRelativeAddress(UInt32 absoluteAddr); + [DllImport(DLLPath)] public static extern Int32 DebugGetAbsoluteAddress(UInt32 relativeAddr); [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); @@ -187,6 +188,9 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void DebugGetCdlRatios(ref CdlRatios ratios); [DllImport(DLLPath)] public static extern void DebugResetCdlLog(); + [DllImport(DLLPath, EntryPoint = "DebugGetCode")] private static extern IntPtr DebugGetCodeWrapper(); + public static string DebugGetCode() { return PtrToStringUtf8(InteropEmu.DebugGetCodeWrapper()); } + [DllImport(DLLPath, EntryPoint="DebugGetMemoryState")] private static extern UInt32 DebugGetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer); public static byte[] DebugGetMemoryState(DebugMemoryType type) { @@ -735,7 +739,8 @@ namespace Mesen.GUI { None = 0, PpuPartialDraw = 1, - ShowEffectiveAddresses = 2 + ShowEffectiveAddresses = 2, + ShowOnlyDisassembledCode = 4, } public struct InteropRomInfo diff --git a/InteropDLL/DebugWrapper.cpp b/InteropDLL/DebugWrapper.cpp index 64923bc9..c9fae457 100644 --- a/InteropDLL/DebugWrapper.cpp +++ b/InteropDLL/DebugWrapper.cpp @@ -31,6 +31,7 @@ extern "C" DllExport void __stdcall DebugGetState(DebugState *state) { GetDebugger()->GetState(state); } DllExport void __stdcall DebugSetBreakpoints(Breakpoint breakpoints[], uint32_t length) { GetDebugger()->SetBreakpoints(breakpoints, length); } + DllExport void __stdcall DebugSetLabel(uint32_t address, char* label, char* comment) { GetDebugger()->SetLabel(address, label, comment); } DllExport void __stdcall DebugRun() { GetDebugger()->Run(); } DllExport void __stdcall DebugStep(uint32_t count) { GetDebugger()->Step(count); } @@ -55,6 +56,7 @@ extern "C" DllExport uint8_t __stdcall DebugGetMemoryValue(uint32_t addr) { return GetDebugger()->GetMemoryValue(addr); } DllExport int32_t __stdcall DebugGetRelativeAddress(uint32_t addr) { return GetDebugger()->GetRelativeAddress(addr); } + DllExport int32_t __stdcall DebugGetAbsoluteAddress(uint32_t addr) { return GetDebugger()->GetAbsoluteAddress(addr); } DllExport bool __stdcall DebugLoadCdlFile(char* cdlFilepath) { return GetDebugger()->LoadCdlFile(cdlFilepath); } DllExport bool __stdcall DebugSaveCdlFile(char* cdlFilepath) { return GetDebugger()->SaveCdlFile(cdlFilepath); }