From 802bd75df1039dcdd29e8774ba6bfc9ebc653264 Mon Sep 17 00:00:00 2001 From: Sour Date: Wed, 27 Feb 2019 19:49:26 -0500 Subject: [PATCH] Debugger: Disassembly window, code data logger --- Core/CodeDataLogger.cpp | 134 ++++++ Core/CodeDataLogger.h | 35 ++ Core/Core.vcxproj | 7 + Core/Core.vcxproj.filters | 21 + Core/DebugTypes.h | 52 +++ Core/Debugger.cpp | 75 ++- Core/Debugger.h | 8 +- Core/Disassembler.cpp | 292 ++++++++++++ Core/Disassembler.h | 50 ++ Core/DisassemblyInfo.cpp | 42 +- Core/DisassemblyInfo.h | 10 +- InteropDLL/DebugApiWrapper.cpp | 7 +- UI/Debugger/CodeLineData.cs | 106 +++++ UI/Debugger/Config/DebugInfo.cs | 10 +- UI/Debugger/Controls/ctrlCodeScrollbar.cs | 21 +- .../Controls/ctrlDisassemblyView.Designer.cs | 66 +++ UI/Debugger/Controls/ctrlDisassemblyView.cs | 192 ++++++++ UI/Debugger/Controls/ctrlDisassemblyView.resx | 120 +++++ UI/Debugger/Controls/ctrlScrollableTextbox.cs | 126 +---- .../ctrlScrollableTextbox.designer.cs | 5 +- UI/Debugger/Controls/ctrlTextbox.cs | 432 +++++++----------- UI/Debugger/DebugWindowManager.cs | 3 + UI/Debugger/frmDebugger.Designer.cs | 289 ++++++++++++ UI/Debugger/frmDebugger.cs | 94 ++++ UI/Debugger/frmDebugger.resx | 126 +++++ UI/Debugger/frmTraceLogger.cs | 55 ++- UI/Forms/frmMain.Designer.cs | 22 +- UI/Forms/frmMain.cs | 8 +- UI/Interop/DebugApi.cs | 23 + UI/SingleInstance.cs | 2 +- UI/UI.csproj | 20 + UI/Utilities/HexConverter.cs | 49 ++ 32 files changed, 2044 insertions(+), 458 deletions(-) create mode 100644 Core/CodeDataLogger.cpp create mode 100644 Core/CodeDataLogger.h create mode 100644 Core/Disassembler.cpp create mode 100644 Core/Disassembler.h create mode 100644 UI/Debugger/CodeLineData.cs create mode 100644 UI/Debugger/Controls/ctrlDisassemblyView.Designer.cs create mode 100644 UI/Debugger/Controls/ctrlDisassemblyView.cs create mode 100644 UI/Debugger/Controls/ctrlDisassemblyView.resx create mode 100644 UI/Debugger/frmDebugger.Designer.cs create mode 100644 UI/Debugger/frmDebugger.cs create mode 100644 UI/Debugger/frmDebugger.resx create mode 100644 UI/Utilities/HexConverter.cs diff --git a/Core/CodeDataLogger.cpp b/Core/CodeDataLogger.cpp new file mode 100644 index 0000000..1c26aee --- /dev/null +++ b/Core/CodeDataLogger.cpp @@ -0,0 +1,134 @@ +#include "stdafx.h" +#include "CodeDataLogger.h" + +CodeDataLogger::CodeDataLogger(uint32_t prgSize) +{ + _prgSize = prgSize; + _cdlData = new uint8_t[prgSize]; + Reset(); +} + +CodeDataLogger::~CodeDataLogger() +{ + delete[] _cdlData; +} + +void CodeDataLogger::Reset() +{ + _codeSize = 0; + _dataSize = 0; + memset(_cdlData, 0, _prgSize); +} + +bool CodeDataLogger::LoadCdlFile(string cdlFilepath) +{ + ifstream cdlFile(cdlFilepath, ios::in | ios::binary); + if(cdlFile) { + cdlFile.seekg(0, std::ios::end); + size_t fileSize = (size_t)cdlFile.tellg(); + cdlFile.seekg(0, std::ios::beg); + + if(fileSize == _prgSize) { + Reset(); + + cdlFile.read((char*)_cdlData, _prgSize); + cdlFile.close(); + + CalculateStats(); + + return true; + } + } + return false; +} + +void CodeDataLogger::CalculateStats() +{ + _codeSize = 0; + _dataSize = 0; + + for(int i = 0, len = _prgSize; i < len; i++) { + if(IsCode(i)) { + _codeSize++; + } else if(IsData(i)) { + _dataSize++; + } + } +} + +bool CodeDataLogger::SaveCdlFile(string cdlFilepath) +{ + ofstream cdlFile(cdlFilepath, ios::out | ios::binary); + if(cdlFile) { + cdlFile.write((char*)_cdlData, _prgSize); + cdlFile.close(); + return true; + } + return false; +} + +void CodeDataLogger::SetFlags(int32_t absoluteAddr, uint8_t flags) +{ + if(absoluteAddr >= 0 && absoluteAddr < (int32_t)_prgSize) { + if((_cdlData[absoluteAddr] & flags) != flags) { + if(flags & CdlFlags::Code) { + if(IsData(absoluteAddr)) { + //Remove the data flag from bytes that we are flagging as code + _cdlData[absoluteAddr] &= ~CdlFlags::Data; + _dataSize--; + } + _cdlData[absoluteAddr] |= flags; + _codeSize++; + } else if(flags & CdlFlags::Data) { + if(!IsCode(absoluteAddr)) { + _cdlData[absoluteAddr] |= flags; + _dataSize++; + } + } else { + _cdlData[absoluteAddr] |= flags; + } + } + } +} + +CdlRatios CodeDataLogger::GetRatios() +{ + CdlRatios ratios; + ratios.CodeRatio = (float)_codeSize / (float)_prgSize; + ratios.DataRatio = (float)_dataSize / (float)_prgSize; + ratios.PrgRatio = (float)(_codeSize + _dataSize) / (float)_prgSize; + return ratios; +} + +bool CodeDataLogger::IsCode(uint32_t absoluteAddr) +{ + return (_cdlData[absoluteAddr] & CdlFlags::Code) != 0; +} + +bool CodeDataLogger::IsJumpTarget(uint32_t absoluteAddr) +{ + return (_cdlData[absoluteAddr] & CdlFlags::JumpTarget) != 0; +} + +bool CodeDataLogger::IsSubEntryPoint(uint32_t absoluteAddr) +{ + return (_cdlData[absoluteAddr] & CdlFlags::SubEntryPoint) != 0; +} + +bool CodeDataLogger::IsData(uint32_t absoluteAddr) +{ + return (_cdlData[absoluteAddr] & CdlFlags::Data) != 0; +} + +uint8_t CodeDataLogger::GetCpuFlags(uint32_t absoluteAddr) +{ + return _cdlData[absoluteAddr] & (CdlFlags::MemoryMode8 | CdlFlags::IndexMode8); +} + +void CodeDataLogger::SetCdlData(uint8_t *cdlData, uint32_t length) +{ + if(length <= _prgSize) { + memcpy(_cdlData, cdlData, length); + CalculateStats(); + } +} diff --git a/Core/CodeDataLogger.h b/Core/CodeDataLogger.h new file mode 100644 index 0000000..df5c374 --- /dev/null +++ b/Core/CodeDataLogger.h @@ -0,0 +1,35 @@ +#pragma once +#include "stdafx.h" +#include "DebugTypes.h" + +class CodeDataLogger +{ +private: + uint8_t *_cdlData = nullptr; + uint32_t _prgSize = 0; + uint32_t _codeSize = 0; + uint32_t _dataSize = 0; + + void CalculateStats(); + +public: + CodeDataLogger(uint32_t prgSize); + ~CodeDataLogger(); + + void Reset(); + + bool LoadCdlFile(string cdlFilepath); + bool SaveCdlFile(string cdlFilepath); + + void SetFlags(int32_t absoluteAddr, uint8_t flags); + + CdlRatios GetRatios(); + + bool IsCode(uint32_t absoluteAddr); + bool IsJumpTarget(uint32_t absoluteAddr); + bool IsSubEntryPoint(uint32_t absoluteAddr); + bool IsData(uint32_t absoluteAddr); + uint8_t GetCpuFlags(uint32_t absoluteAddr); + + void SetCdlData(uint8_t *cdlData, uint32_t length); +}; \ No newline at end of file diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index b6b2c63..9b24f27 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -52,15 +52,18 @@ + + + @@ -88,6 +91,7 @@ + @@ -108,6 +112,7 @@ + @@ -115,11 +120,13 @@ + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 3878ada..8ae66a4 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -173,6 +173,18 @@ SNES + + Debugger + + + Debugger + + + SNES + + + SNES + @@ -266,6 +278,15 @@ SNES\Input + + Debugger + + + Debugger + + + SNES + diff --git a/Core/DebugTypes.h b/Core/DebugTypes.h index 10269b0..b0fdaa3 100644 --- a/Core/DebugTypes.h +++ b/Core/DebugTypes.h @@ -46,3 +46,55 @@ struct CdlRatios float DataRatio; float PrgRatio; }; + +struct DisassemblyResult +{ + AddressInfo Address; + int32_t CpuAddress; + uint8_t Flags; + + DisassemblyResult(int32_t cpuAddress, uint8_t flags) + { + Flags = flags; + CpuAddress = cpuAddress; + Address.Address = -1; + } + + DisassemblyResult(AddressInfo address, int32_t cpuAddress, uint8_t flags = 0) + { + Address = address; + CpuAddress = cpuAddress; + Flags = flags; + } +}; + +namespace LineFlags +{ + enum LineFlags : uint8_t + { + None = 0, + PrgRom = 1, + WorkRam = 2, + SaveRam = 4, + VerifiedData = 8, + VerifiedCode = 16, + BlockStart = 32, + BlockEnd = 64, + SubStart = 128 + }; +} + +struct CodeLineData +{ + int32_t Address; + int32_t AbsoluteAddress; + uint8_t OpSize; + uint8_t Flags; + + int32_t EffectiveAddress; + int32_t Value; + + uint8_t ByteCode[4]; + char Text[1000]; + char Comment[1000]; +}; \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index f91f0a4..6c380fc 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -3,12 +3,17 @@ #include "Console.h" #include "Cpu.h" #include "Ppu.h" +#include "BaseCartridge.h" +#include "MemoryManager.h" #include "NotificationManager.h" #include "CpuTypes.h" #include "DisassemblyInfo.h" #include "TraceLogger.h" #include "MemoryDumper.h" +#include "CodeDataLogger.h" +#include "Disassembler.h" #include "../Utilities/HexUtilities.h" +#include "../Utilities/FolderUtilities.h" Debugger::Debugger(shared_ptr console) { @@ -17,51 +22,90 @@ Debugger::Debugger(shared_ptr console) _ppu = console->GetPpu(); _memoryManager = console->GetMemoryManager(); + _codeDataLogger.reset(new CodeDataLogger(console->GetCartridge()->DebugGetPrgRomSize())); + _disassembler.reset(new Disassembler(console, _codeDataLogger)); _traceLogger.reset(new TraceLogger(this, _memoryManager)); _memoryDumper.reset(new MemoryDumper(_ppu, _memoryManager, console->GetCartridge())); - _cpuStepCount = 0; + + string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_console->GetCartridge()->GetRomInfo().RomPath, false) + ".cdl"); + _codeDataLogger->LoadCdlFile(cdlFile); + + //TODO: Thread safety + uint32_t prgRomSize = console->GetCartridge()->DebugGetPrgRomSize(); + AddressInfo addrInfo; + addrInfo.Type = SnesMemoryType::PrgRom; + for(uint32_t i = 0; i < prgRomSize; i++) { + if(_codeDataLogger->IsCode(i)) { + addrInfo.Address = (int32_t)i; + i += _disassembler->BuildCache(addrInfo, _codeDataLogger->GetCpuFlags(i)) - 1; + } + } } Debugger::~Debugger() { + string cdlFile = FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_console->GetCartridge()->GetRomInfo().RomPath, false) + ".cdl"); + _codeDataLogger->SaveCdlFile(cdlFile); } void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type) { + AddressInfo addressInfo = _memoryManager->GetAbsoluteAddress(addr); + CpuState state = _cpu->GetState(); + if(type == MemoryOperationType::ExecOpCode) { - CpuState state = _cpu->GetState(); - DisassemblyInfo disassemblyInfo(state, _memoryManager.get()); + if(addressInfo.Address >= 0) { + if(addressInfo.Type == SnesMemoryType::PrgRom) { + uint8_t flags = CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8)); + if(_prevOpCode == 0x20 || _prevOpCode == 0x5C || _prevOpCode == 0xDC || _prevOpCode == 0xFC) { + flags |= CdlFlags::SubEntryPoint; + } + _codeDataLogger->SetFlags(addressInfo.Address, flags); + } + _disassembler->BuildCache(addressInfo, state.PS); + } + DebugState debugState; GetState(&debugState); _traceLogger->LogEffectiveAddress(_cpu->GetLastOperand()); - _traceLogger->Log(debugState, disassemblyInfo); - bool sendNotif = false; - if(value == 0x00) { - //break on BRK + DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo); + _traceLogger->Log(debugState, disInfo); + + _prevOpCode = value; + + if(value == 0x00 || value == 0xCB) { + //break on BRK/WAI _cpuStepCount = 1; - sendNotif = true; } if(_cpuStepCount > 0) { _cpuStepCount--; if(_cpuStepCount == 0) { - if(sendNotif) { - _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak); - } + _disassembler->Disassemble(); + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak); while(_cpuStepCount == 0) { std::this_thread::sleep_for(std::chrono::duration(10)); } } } + } else if(type == MemoryOperationType::ExecOperand) { + if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) { + _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Code | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8))); + } + } else { + if(addressInfo.Type == SnesMemoryType::PrgRom && addressInfo.Address >= 0) { + _codeDataLogger->SetFlags(addressInfo.Address, CdlFlags::Data | (state.PS & (CdlFlags::IndexMode8 | CdlFlags::MemoryMode8))); + } } } void Debugger::ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType type) { - if(type == MemoryOperationType::ExecOpCode) { - //_traceLogger->Trace + AddressInfo addressInfo = _memoryManager->GetAbsoluteAddress(addr); + if(addressInfo.Address >= 0 && (addressInfo.Type == SnesMemoryType::WorkRam || addressInfo.Type == SnesMemoryType::SaveRam)) { + _disassembler->InvalidateCache(addressInfo); } } @@ -96,3 +140,8 @@ shared_ptr Debugger::GetMemoryDumper() { return _memoryDumper; } + +shared_ptr Debugger::GetDisassembler() +{ + return _disassembler; +} diff --git a/Core/Debugger.h b/Core/Debugger.h index 140b2b4..f79e110 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -8,11 +8,12 @@ class Cpu; class Ppu; class BaseCartridge; class MemoryManager; +class CodeDataLogger; enum class MemoryOperationType; class TraceLogger; class MemoryDumper; -//class Disassembler; +class Disassembler; struct DebugState { @@ -32,9 +33,11 @@ private: shared_ptr _traceLogger; shared_ptr _memoryDumper; - //unique_ptr _disassembler; + shared_ptr _codeDataLogger; + shared_ptr _disassembler; atomic _cpuStepCount; + uint8_t _prevOpCode = 0; public: Debugger(shared_ptr console); @@ -51,4 +54,5 @@ public: shared_ptr GetTraceLogger(); shared_ptr GetMemoryDumper(); + shared_ptr GetDisassembler(); }; \ No newline at end of file diff --git a/Core/Disassembler.cpp b/Core/Disassembler.cpp new file mode 100644 index 0000000..6ce6e76 --- /dev/null +++ b/Core/Disassembler.cpp @@ -0,0 +1,292 @@ +#include "stdafx.h" +#include +#include "Disassembler.h" +#include "DisassemblyInfo.h" +#include "Cpu.h" +#include "MemoryManager.h" +#include "CpuTypes.h" +#include "Console.h" +#include "CodeDataLogger.h" +#include "BaseCartridge.h" +#include "../Utilities/HexUtilities.h" + +Disassembler::Disassembler(shared_ptr console, shared_ptr cdl) +{ + _cdl = cdl; + _console = console.get(); + _memoryManager = console->GetMemoryManager().get(); + + _prgRom = console->GetCartridge()->DebugGetPrgRom(); + _prgRomSize = console->GetCartridge()->DebugGetPrgRomSize(); + _sram = console->GetCartridge()->DebugGetSaveRam(); + _sramSize = console->GetCartridge()->DebugGetSaveRamSize(); + _wram = _memoryManager->DebugGetWorkRam(); + _wramSize = MemoryManager::WorkRamSize; + + _prgCache = vector>(_prgRomSize); + _sramCache = vector>(_sramSize); + _wramCache = vector>(_wramSize); +} + +void Disassembler::GetSource(AddressInfo &info, uint8_t **source, uint32_t &size, vector> **cache) +{ + switch(info.Type) { + case SnesMemoryType::PrgRom: + *source = _prgRom; + *cache = &_prgCache; + size = _prgRomSize; + break; + + case SnesMemoryType::WorkRam: + *source = _wram; + *cache = &_wramCache; + size = MemoryManager::WorkRamSize; + break; + + case SnesMemoryType::SaveRam: + *source = _sram; + *cache = &_sramCache; + size = _sramSize; + break; + + default: + throw std::runtime_error("Disassembler::GetSource() invalid memory type"); + } +} + +uint32_t Disassembler::BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags) +{ + uint8_t *source; + uint32_t sourceLength; + vector> *cache; + GetSource(addrInfo, &source, sourceLength, &cache); + + if(addrInfo.Address >= 0) { + DisassemblyInfo *disInfo = (*cache)[addrInfo.Address].get(); + if(!disInfo) { + shared_ptr disassemblyInfo(new DisassemblyInfo(source+addrInfo.Address, cpuFlags)); + (*cache)[addrInfo.Address] = disassemblyInfo; + _needDisassemble = true; + disInfo = disassemblyInfo.get(); + } + return disInfo->GetOperandSize() + 1; + } + return 0; +} + +void Disassembler::InvalidateCache(AddressInfo addrInfo) +{ + uint8_t *source; + uint32_t sourceLength; + vector> *cache; + GetSource(addrInfo, &source, sourceLength, &cache); + + if(addrInfo.Address >= 0) { + for(int i = 0; i < 4; i++) { + if(addrInfo.Address >= i) { + if((*cache)[addrInfo.Address - i]) { + _needDisassemble = true; + (*cache)[addrInfo.Address - i].reset(); + } + } + } + } +} + +void Disassembler::Disassemble() +{ + if(!_needDisassemble) { + return; + } + + _needDisassemble = false; + + auto lock = _disassemblyLock.AcquireSafe(); + + _disassembly.clear(); + + uint8_t *source; + uint32_t sourceLength; + vector> *cache; + + bool disassembleAll = false; + bool inUnknownBlock = false; + shared_ptr disassemblyInfo; + shared_ptr tmpInfo(new DisassemblyInfo()); + + AddressInfo addrInfo = {}; + AddressInfo prevAddrInfo = {}; + for(int32_t i = 0; i <= 0xFFFFFF; i++) { + prevAddrInfo = addrInfo; + addrInfo = _memoryManager->GetAbsoluteAddress(i); + + if(addrInfo.Address < 0) { + continue; + } + + GetSource(addrInfo, &source, sourceLength, &cache); + + DisassemblyInfo *disassemblyInfo = (*cache)[addrInfo.Address].get(); + + uint8_t opSize = 0; + uint8_t opCode = (source + addrInfo.Address)[0]; + bool needRealign = true; + if(!disassemblyInfo && disassembleAll) { + opSize = DisassemblyInfo::GetOperandSize(opCode, 0); + } else if(disassemblyInfo) { + opSize = disassemblyInfo->GetOperandSize(); + needRealign = false; + } + + if(disassemblyInfo || disassembleAll) { + if(inUnknownBlock) { + _disassembly.push_back(DisassemblyResult(prevAddrInfo, i - 1, LineFlags::BlockEnd)); + inUnknownBlock = false; + } + + if(addrInfo.Type == SnesMemoryType::PrgRom && _cdl->IsSubEntryPoint(addrInfo.Address)) { + _disassembly.push_back(DisassemblyResult(-1, LineFlags::SubStart | LineFlags::BlockStart | LineFlags::VerifiedCode)); + } + + _disassembly.push_back(DisassemblyResult(addrInfo, i)); + if(needRealign) { + for(int j = 1; j < opSize; j++) { + if((*cache)[addrInfo.Address + j]) { + break; + } + i++; + } + } else { + i += opSize; + } + + if(opCode == 0x60 || opCode == 0x6B) { + //End of function + _disassembly.push_back(DisassemblyResult(-1, LineFlags::VerifiedCode | LineFlags::BlockEnd)); + } + } else { + if(!inUnknownBlock) { + inUnknownBlock = true; + _disassembly.push_back(DisassemblyResult(addrInfo, i, LineFlags::BlockStart)); + _disassembly.push_back(DisassemblyResult(-1, LineFlags::None)); + } + } + } + + if(inUnknownBlock) { + _disassembly.push_back(DisassemblyResult(addrInfo, 0xFFFFFF, LineFlags::BlockEnd)); + } +} + +DisassemblyInfo Disassembler::GetDisassemblyInfo(AddressInfo &info) +{ + DisassemblyInfo* disassemblyInfo = nullptr; + switch(info.Type) { + case SnesMemoryType::PrgRom: disassemblyInfo = _prgCache[info.Address].get(); break; + case SnesMemoryType::WorkRam: disassemblyInfo = _wramCache[info.Address].get(); break; + case SnesMemoryType::SaveRam: disassemblyInfo = _sramCache[info.Address].get(); break; + } + + if(disassemblyInfo) { + return *disassemblyInfo; + } else { + return DisassemblyInfo(); + } +} + +uint32_t Disassembler::GetLineCount() +{ + auto lock = _disassemblyLock.AcquireSafe(); + return (uint32_t)_disassembly.size(); +} + +uint32_t Disassembler::GetLineIndex(uint32_t cpuAddress) +{ + auto lock = _disassemblyLock.AcquireSafe(); + for(int i = 0; i < _disassembly.size(); i++) { + if(_disassembly[i].CpuAddress == cpuAddress) { + return i; + } + } + return 0; +} + +bool Disassembler::GetLineData(uint32_t lineIndex, CodeLineData &data) +{ + auto lock =_disassemblyLock.AcquireSafe(); + if(lineIndex < _disassembly.size()) { + DisassemblyResult result = _disassembly[lineIndex]; + data.Address = result.CpuAddress; + data.AbsoluteAddress = result.Address.Address; + data.Flags = result.Flags; + + switch(result.Address.Type) { + case SnesMemoryType::PrgRom: data.Flags |= (uint8_t)LineFlags::PrgRom; break; + case SnesMemoryType::WorkRam: data.Flags |= (uint8_t)LineFlags::WorkRam; break; + case SnesMemoryType::SaveRam: data.Flags |= (uint8_t)LineFlags::SaveRam; break; + } + + bool isBlockStartEnd = (data.Flags & (LineFlags::BlockStart | LineFlags::BlockEnd)) != 0; + + if(!isBlockStartEnd && result.Address.Address >= 0) { + shared_ptr disInfo; + uint8_t *source; + uint32_t sourceLength; + vector> *cache; + GetSource(result.Address, &source, sourceLength, &cache); + disInfo = (*cache)[result.Address.Address]; + + if(!disInfo) { + disInfo.reset(new DisassemblyInfo(source + result.Address.Address, 0)); + } else { + data.Flags |= (uint8_t)LineFlags::VerifiedCode; + } + + string text; + disInfo->GetDisassembly(text, result.CpuAddress); + memcpy(data.Text, text.c_str(), std::min((int)text.size(), 1000)); + + data.OpSize = disInfo->GetOperandSize() + 1; + data.EffectiveAddress = disInfo->GetEffectiveAddress(); + data.Value = _memoryManager->Peek(result.CpuAddress); + + disInfo->GetByteCode(data.ByteCode); + data.Comment[0] = 0; + } else { + if(data.Flags & LineFlags::SubStart) { + string label = "sub start"; + memcpy(data.Text, label.c_str(), label.size() + 1); + } else if(data.Flags & LineFlags::BlockStart) { + string label = (data.Flags & LineFlags::VerifiedCode) ? "verified code" : "unidentified"; + memcpy(data.Text, label.c_str(), label.size() + 1); + } + } + return true; + } + return false; +} + +int32_t Disassembler::SearchCode(const char *searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards) +{ + auto lock = _disassemblyLock.AcquireSafe(); + int step = searchBackwards ? -1 : 1; + CodeLineData lineData = {}; + for(int i = startPosition; i != endPosition; i += step) { + GetLineData(i, lineData); + string line = lineData.Text; + std::transform(line.begin(), line.end(), line.begin(), ::tolower); + + if(line.find(searchString) != string::npos) { + return i; + } + + //Continue search from start/end of document + if(!searchBackwards && i == _disassembly.size() - 1) { + i = 0; + } else if(searchBackwards && i == 0) { + i = (int32_t)(_disassembly.size() - 1); + } + } + + return -1; +} diff --git a/Core/Disassembler.h b/Core/Disassembler.h new file mode 100644 index 0000000..065f387 --- /dev/null +++ b/Core/Disassembler.h @@ -0,0 +1,50 @@ +#pragma once +#include "stdafx.h" +#include "DisassemblyInfo.h" +#include "DebugTypes.h" +#include "../Utilities/SimpleLock.h" + +class MemoryManager; +class Console; +class CodeDataLogger; +struct CpuState; + +class Disassembler +{ +private: + MemoryManager *_memoryManager; + Console *_console; + shared_ptr _cdl; + + vector> _prgCache; + vector> _wramCache; + vector> _sramCache; + + SimpleLock _disassemblyLock; + vector _disassembly; + + bool _needDisassemble = true; + + uint8_t *_prgRom; + uint32_t _prgRomSize; + uint8_t *_wram; + uint32_t _wramSize; + uint8_t *_sram; + uint32_t _sramSize; + + void GetSource(AddressInfo &info, uint8_t **source, uint32_t &size, vector> **cache); + +public: + Disassembler(shared_ptr console, shared_ptr cdl); + + uint32_t BuildCache(AddressInfo &addrInfo, uint8_t cpuFlags); + void InvalidateCache(AddressInfo addrInfo); + void Disassemble(); + + DisassemblyInfo GetDisassemblyInfo(AddressInfo &info); + + uint32_t GetLineCount(); + uint32_t GetLineIndex(uint32_t cpuAddress); + bool GetLineData(uint32_t lineIndex, CodeLineData &data); + int32_t SearchCode(const char* searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards); +}; \ No newline at end of file diff --git a/Core/DisassemblyInfo.cpp b/Core/DisassemblyInfo.cpp index 5a0c77f..e74498f 100644 --- a/Core/DisassemblyInfo.cpp +++ b/Core/DisassemblyInfo.cpp @@ -10,21 +10,23 @@ DisassemblyInfo::DisassemblyInfo() { } -DisassemblyInfo::DisassemblyInfo(CpuState &state, MemoryManager *memoryManager) +DisassemblyInfo::DisassemblyInfo(uint8_t *opPointer, uint8_t cpuFlags) { - _flags = state.PS; + Initialize(opPointer, cpuFlags); +} + +void DisassemblyInfo::Initialize(uint8_t *opPointer, uint8_t cpuFlags) +{ + _flags = cpuFlags; _effectiveAddress = -1; - uint32_t addr = (state.K << 16) | state.PC; - _byteCode[0] = memoryManager->Peek(addr); + _byteCode[0] = opPointer[0]; _addrMode = DisassemblyInfo::OpMode[_byteCode[0]]; - _opSize = GetOperandSize() + 1; + _opSize = GetOperandSize(_addrMode, _flags) + 1; for(int i = 1; i < _opSize; i++) { - _byteCode[i] = memoryManager->Peek((addr+i) & 0xFFFFFF); + _byteCode[i] = opPointer[i]; } - - _emulationMode = state.EmulationMode; } void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr) @@ -41,7 +43,6 @@ void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr) operand.Write(HexUtilities::ToHex(opAddr)); } - switch(_addrMode) { case AddrMode::Abs: str.Write(operand); break; case AddrMode::AbsJmp: str.Write(operand); break; @@ -54,7 +55,7 @@ void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr) case AddrMode::AbsLng: str.Write(operand); break; case AddrMode::AbsLngJmp: str.Write(operand); break; case AddrMode::Acc: break; - case AddrMode::BlkMov: str.Write(operand[1], operand[2], " -> ", operand[3], operand[4]); break; + case AddrMode::BlkMov: str.Write(operand[1], operand[2], " -> "); str.Write(operand[3], operand[4]); break; case AddrMode::DirIdxIndX: str.Write('(', operand, ",X)"); break; case AddrMode::DirIdxX: str.Write(operand, ",X"); break; case AddrMode::DirIdxY: str.Write(operand, ",Y"); break; @@ -105,7 +106,12 @@ uint32_t DisassemblyInfo::GetOperandAddress(uint32_t memoryAddr) uint8_t DisassemblyInfo::GetOperandSize() { - switch(_addrMode) { + return _opSize - 1; +} + +uint8_t DisassemblyInfo::GetOperandSize(AddrMode addrMode, uint8_t flags) +{ + switch(addrMode) { case AddrMode::Acc: case AddrMode::Imp: case AddrMode::Stk: @@ -142,13 +148,23 @@ uint8_t DisassemblyInfo::GetOperandSize() case AddrMode::AbsLng: return 3; - case AddrMode::ImmX: return (_flags & ProcFlags::IndexMode8) ? 1 : 2; - case AddrMode::ImmM: return (_flags & ProcFlags::MemoryMode8) ? 1 : 2; + case AddrMode::ImmX: return (flags & ProcFlags::IndexMode8) ? 1 : 2; + case AddrMode::ImmM: return (flags & ProcFlags::MemoryMode8) ? 1 : 2; } throw std::runtime_error("Invalid mode"); } +uint8_t DisassemblyInfo::GetOperandSize(uint8_t opCode, uint8_t flags) +{ + return GetOperandSize(DisassemblyInfo::OpMode[opCode], flags); +} + +void DisassemblyInfo::GetByteCode(uint8_t copyBuffer[4]) +{ + memcpy(copyBuffer, _byteCode, _opSize); +} + void DisassemblyInfo::GetByteCode(string &out) { FastString str; diff --git a/Core/DisassemblyInfo.h b/Core/DisassemblyInfo.h index 580b06f..f6cde5d 100644 --- a/Core/DisassemblyInfo.h +++ b/Core/DisassemblyInfo.h @@ -19,16 +19,22 @@ private: uint8_t _opSize; AddrMode _addrMode; uint8_t _flags; - bool _emulationMode; int32_t _effectiveAddress; public: DisassemblyInfo(); - DisassemblyInfo(CpuState &state, MemoryManager *memoryManager); + DisassemblyInfo(uint8_t *opPointer, uint8_t cpuFlags); + + void Initialize(uint8_t * opPointer, uint8_t cpuFlags); void GetDisassembly(string &out, uint32_t memoryAddr); uint32_t GetOperandAddress(uint32_t memoryAddr); + uint8_t GetOperandSize(); + static uint8_t GetOperandSize(AddrMode addrMode, uint8_t flags); + static uint8_t GetOperandSize(uint8_t opCode, uint8_t flags); + + void GetByteCode(uint8_t copyBuffer[4]); void GetByteCode(string &out); void SetEffectiveAddress(int32_t effectiveAddress); diff --git a/InteropDLL/DebugApiWrapper.cpp b/InteropDLL/DebugApiWrapper.cpp index 0ce9a03..7a411b4 100644 --- a/InteropDLL/DebugApiWrapper.cpp +++ b/InteropDLL/DebugApiWrapper.cpp @@ -3,6 +3,7 @@ #include "../Core/Debugger.h" #include "../Core/TraceLogger.h" #include "../Core/MemoryDumper.h" +#include "../Core/Disassembler.h" #include "../Core/DebugTypes.h" extern shared_ptr _console; @@ -34,7 +35,11 @@ extern "C" DllExport bool __stdcall IsExecutionStopped() { return GetDebugger()->IsExecutionStopped(); } DllExport void __stdcall ResumeExecution() { GetDebugger()->Run(); } DllExport void __stdcall Step(uint32_t count) { GetDebugger()->Step(count); } - //DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); } + + DllExport void __stdcall GetDisassemblyLineData(uint32_t lineIndex, CodeLineData &data) { GetDebugger()->GetDisassembler()->GetLineData(lineIndex, data); } + DllExport uint32_t __stdcall GetDisassemblyLineCount() { return GetDebugger()->GetDisassembler()->GetLineCount(); } + DllExport uint32_t __stdcall GetDisassemblyLineIndex(uint32_t cpuAddress) { return GetDebugger()->GetDisassembler()->GetLineIndex(cpuAddress); } + DllExport int32_t __stdcall SearchDisassembly(const char* searchString, int32_t startPosition, int32_t endPosition, bool searchBackwards) { return GetDebugger()->GetDisassembler()->SearchCode(searchString, startPosition, endPosition, searchBackwards); } DllExport void __stdcall SetTraceOptions(TraceLoggerOptions options) { GetDebugger()->GetTraceLogger()->SetOptions(options); } DllExport void __stdcall StartTraceLogger(char* filename) { GetDebugger()->GetTraceLogger()->StartLogging(filename); } diff --git a/UI/Debugger/CodeLineData.cs b/UI/Debugger/CodeLineData.cs new file mode 100644 index 0000000..5cd22ba --- /dev/null +++ b/UI/Debugger/CodeLineData.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Debugger +{ + public class CodeLineData + { + public string Text; + + public Int32 Address; + public Int32 AbsoluteAddress; + + public LineFlags Flags; + + public byte OpSize; + public string ByteCode; + public string Comment; + + public Int32 EffectiveAddress; + public Int32 Value; + + public string GetEffectiveAddressString() + { + return "[" + EffectiveAddress.ToString("X6") + "]"; + } + + public int Indentation + { + get + { + if(Flags.HasFlag(LineFlags.BlockStart) || Flags.HasFlag(LineFlags.BlockEnd)) { + return 0; + } else { + return 10; + } + } + } + + public CodeLineData() + { + } + + public CodeLineData(InteropCodeLineData data) + { + this.Text = ConvertString(data.Text); + this.Comment = ConvertString(data.Comment); + this.OpSize = data.OpSize; + for(int i = 0; i < this.OpSize; i++) { + this.ByteCode += "$" + data.ByteCode[i].ToString("X2") + " "; + } + + this.Address = data.Address; + this.AbsoluteAddress = data.AbsoluteAddress; + this.EffectiveAddress = data.EffectiveAddress; + this.Flags = (LineFlags)data.Flags; + this.Value = data.Value; + } + + private string ConvertString(byte[] stringArray) + { + int length = 0; + for(int i = 0; i < 1000; i++) { + if(stringArray[i] == 0) { + length = i; + break; + } + } + return Encoding.UTF8.GetString(stringArray, 0, length); + } + } + + public struct InteropCodeLineData + { + public Int32 Address; + public Int32 AbsoluteAddress; + public byte OpSize; + public byte Flags; + + public Int32 EffectiveAddress; + public Int32 Value; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] ByteCode; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)] + public byte[] Text; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)] + public byte[] Comment; + } + + public enum LineFlags : byte + { + PrgRom = 1, + WorkRam = 2, + SaveRam = 4, + VerifiedData = 8, + VerifiedCode = 16, + BlockStart = 32, + BlockEnd = 64, + } +} diff --git a/UI/Debugger/Config/DebugInfo.cs b/UI/Debugger/Config/DebugInfo.cs index 784d62f..c05eda6 100644 --- a/UI/Debugger/Config/DebugInfo.cs +++ b/UI/Debugger/Config/DebugInfo.cs @@ -30,7 +30,15 @@ namespace Mesen.GUI.Config public XmlColor CodeCommentColor = Color.Green; public XmlColor CodeEffectiveAddressColor = Color.SteelBlue; - + public XmlColor CodeVerifiedDataColor = Color.FromArgb(255, 252, 236); + public XmlColor CodeUnidentifiedDataColor = Color.FromArgb(255, 242, 242); + public XmlColor CodeUnexecutedCodeColor = Color.FromArgb(225, 244, 228); + + public XmlColor CodeExecBreakpointColor = Color.FromArgb(140, 40, 40); + public XmlColor CodeWriteBreakpointColor = Color.FromArgb(40, 120, 80); + public XmlColor CodeReadBreakpointColor = Color.FromArgb(40, 40, 200); + public XmlColor CodeActiveStatementColor = Color.Yellow; + public DebugInfo() { } diff --git a/UI/Debugger/Controls/ctrlCodeScrollbar.cs b/UI/Debugger/Controls/ctrlCodeScrollbar.cs index 62e9d30..665efa7 100644 --- a/UI/Debugger/Controls/ctrlCodeScrollbar.cs +++ b/UI/Debugger/Controls/ctrlCodeScrollbar.cs @@ -15,6 +15,7 @@ namespace Mesen.GUI.Debugger.Controls private Timer _tmrScroll; private bool _scrollUp = false; + private bool _mouseDown = false; private const int _buttonSize = 15; @@ -137,14 +138,13 @@ namespace Mesen.GUI.Debugger.Controls e.Graphics.FillPolygon(Brushes.DimGray, new Point[] { new Point(left, bottom - arrowHeight), new Point(left + arrowWidth, bottom - arrowHeight), new Point(left + arrowWidth / 2, bottom) }, FillMode.Winding); } - //private bool _mouseDown = false; protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if(e.Y > _buttonSize && e.Y < (this.Height - _buttonSize)) { this.UpdatePosition(e.Y); - //this._mouseDown = true; + this._mouseDown = true; } else { if(e.Y <= _buttonSize) { if(this.Value > 0) { @@ -175,21 +175,20 @@ namespace Mesen.GUI.Debugger.Controls protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); - //this._mouseDown = false; + this._mouseDown = false; this._tmrScroll.Stop(); } //TODO - /* - frmCodePreviewTooltip _codeTooltip = null; - int _lastPreviewScrollPosition = -1; + //frmCodePreviewTooltip _codeTooltip = null; + //int _lastPreviewScrollPosition = -1; protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if(this._mouseDown) { this.UpdatePosition(e.Y); } else { - if(this.ColorProvider != null && e.Y > _buttonSize && e.Y < (this.Height - _buttonSize)) { + /*if(this.ColorProvider != null && e.Y > _buttonSize && e.Y < (this.Height - _buttonSize)) { int scrollPosition = Math.Max(0, (e.Y - this.Top - _buttonSize) * this.Maximum / (this.Height - _buttonSize * 2)); if(_lastPreviewScrollPosition != scrollPosition) { Point p = this.PointToScreen(new Point(this.ClientRectangle.Right, e.Y)); @@ -206,12 +205,12 @@ namespace Mesen.GUI.Debugger.Controls } _lastPreviewScrollPosition = scrollPosition; } - } + }*/ } } - - protected override void OnMouseLeave(EventArgs e) + //TODO + /*protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); if(_codeTooltip != null) { @@ -242,7 +241,7 @@ namespace Mesen.GUI.Debugger.Controls private void UpdatePosition(int y) { - this.Value = Math.Max(0 , y - HighlightHeight / 2 + HighlightOffset - this.Top - _buttonSize) * this.Maximum / (this.Height - _buttonSize * 2); + this.Value = (int)(Math.Max(0 , y - HighlightHeight / 2 + HighlightOffset - this.Top - _buttonSize) * (long)this.Maximum / (this.Height - _buttonSize * 2)); } private int _value = 0; diff --git a/UI/Debugger/Controls/ctrlDisassemblyView.Designer.cs b/UI/Debugger/Controls/ctrlDisassemblyView.Designer.cs new file mode 100644 index 0000000..2541471 --- /dev/null +++ b/UI/Debugger/Controls/ctrlDisassemblyView.Designer.cs @@ -0,0 +1,66 @@ +namespace Mesen.GUI.Debugger.Controls +{ + partial class ctrlDisassemblyView + { + /// + /// 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.ctrlCode = new Mesen.GUI.Debugger.Controls.ctrlScrollableTextbox(); + this.SuspendLayout(); + // + // ctrlCode + // + this.ctrlCode.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.ctrlCode.CodeHighlightingEnabled = true; + this.ctrlCode.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlCode.HideSelection = false; + this.ctrlCode.Location = new System.Drawing.Point(0, 0); + this.ctrlCode.Name = "ctrlCode"; + this.ctrlCode.ShowCompactPrgAddresses = false; + this.ctrlCode.ShowContentNotes = false; + this.ctrlCode.ShowLineNumberNotes = false; + this.ctrlCode.ShowMemoryValues = false; + this.ctrlCode.ShowScrollbars = true; + this.ctrlCode.ShowSingleContentLineNotes = true; + this.ctrlCode.ShowSingleLineLineNumberNotes = false; + this.ctrlCode.Size = new System.Drawing.Size(465, 398); + this.ctrlCode.TabIndex = 0; + // + // ctrlDisassemblyView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.ctrlCode); + this.Name = "ctrlDisassemblyView"; + this.Size = new System.Drawing.Size(465, 398); + this.ResumeLayout(false); + + } + + #endregion + + private ctrlScrollableTextbox ctrlCode; + } +} diff --git a/UI/Debugger/Controls/ctrlDisassemblyView.cs b/UI/Debugger/Controls/ctrlDisassemblyView.cs new file mode 100644 index 0000000..139cbe9 --- /dev/null +++ b/UI/Debugger/Controls/ctrlDisassemblyView.cs @@ -0,0 +1,192 @@ +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 Mesen.GUI.Controls; +using Mesen.GUI.Config; + +namespace Mesen.GUI.Debugger.Controls +{ + public partial class ctrlDisassemblyView : BaseControl + { + private LineStyleProvider _styleProvider; + + public ctrlDisassemblyView() + { + InitializeComponent(); + + _styleProvider = new LineStyleProvider(); + ctrlCode.StyleProvider = _styleProvider; + ctrlCode.ShowContentNotes = true; + ctrlCode.ShowMemoryValues = true; + } + + public void SetActiveAddress(int? address) + { + UpdateCode(); + + _styleProvider.ActiveAddress = address; + if(address.HasValue && address.Value >= 0) { + ctrlCode.ScrollToAddress(address.Value); + } + } + + private void UpdateCode() + { + CodeDataProvider provider = new CodeDataProvider(); + + int centerLineIndex = ctrlCode.GetLineIndexAtPosition(0) + ctrlCode.GetNumberVisibleLines() / 2; + int centerLineAddress; + int scrollOffset = -1; + do { + //Save the address at the center of the debug view + centerLineAddress = provider.GetLineAddress(centerLineIndex); + centerLineIndex--; + scrollOffset++; + } while(centerLineAddress < 0 && centerLineIndex > 0); + + ctrlCode.DataProvider = provider; + + if(centerLineAddress >= 0) { + //Scroll to the same address as before, to prevent the code view from changing due to setting or banking changes, etc. + int lineIndex = provider.GetLineIndex((UInt32)centerLineAddress) + scrollOffset; + ctrlCode.ScrollToLineIndex(lineIndex, eHistoryType.None, false, true); + } + } + + public class CodeDataProvider : ICodeDataProvider + { + private int _lineCount; + + public CodeDataProvider() + { + _lineCount = (int)DebugApi.GetDisassemblyLineCount(); + } + + public CodeLineData GetCodeLineData(int lineIndex) + { + return DebugApi.GetDisassemblyLineData((UInt32)lineIndex); + } + + public int GetLineAddress(int lineIndex) + { + return DebugApi.GetDisassemblyLineData((UInt32)lineIndex).Address; + } + + public int GetLineCount() + { + return _lineCount; + } + + public int GetLineIndex(uint cpuAddress) + { + return (int)DebugApi.GetDisassemblyLineIndex(cpuAddress); + } + + public bool UseOptimizedSearch { get { return true; } } + + public int GetNextResult(string searchString, int startPosition, int endPosition, bool searchBackwards) + { + return DebugApi.SearchDisassembly(searchString, startPosition, endPosition, searchBackwards); + } + } + + public class LineStyleProvider : ctrlTextbox.ILineStyleProvider + { + public LineStyleProvider() + { + } + + public int? ActiveAddress { get; set; } + + public string GetLineComment(int lineNumber) + { + return null; + } + + public static void ConfigureActiveStatement(LineProperties props) + { + props.FgColor = Color.Black; + props.TextBgColor = ConfigManager.Config.Debug.CodeActiveStatementColor; + props.Symbol |= LineSymbol.Arrow; + } + + public LineProperties GetLineStyle(CodeLineData lineData, int lineNumber) + { + DebugInfo info = ConfigManager.Config.Debug; + LineProperties props = new LineProperties(); + //TODO + //AddressInfo addressInfo = _code.GetAddressInfo(lineNumber); + //GetBreakpointLineProperties(props, cpuAddress, addressInfo); + + bool isActiveStatement = ActiveAddress.HasValue && ActiveAddress.Value == lineData.Address; + if(isActiveStatement) { + ConfigureActiveStatement(props); + } + + //TODO + /* else if(_code._code.UnexecutedAddresses.Contains(lineNumber)) { + props.LineBgColor = info.CodeUnexecutedCodeColor; + }*/ + + if(lineData.Flags.HasFlag(LineFlags.PrgRom)) { + props.AddressColor = Color.Gray; + } else if(lineData.Flags.HasFlag(LineFlags.WorkRam)) { + props.AddressColor = Color.DarkBlue; + } else if(lineData.Flags.HasFlag(LineFlags.SaveRam)) { + props.AddressColor = Color.DarkRed; + } + + if(lineData.Flags.HasFlag(LineFlags.VerifiedData)) { + props.LineBgColor = info.CodeVerifiedDataColor; + } else if(!lineData.Flags.HasFlag(LineFlags.VerifiedCode)) { + props.LineBgColor = info.CodeUnidentifiedDataColor; + } + + return props; + } + + public static void GetBreakpointLineProperties(LineProperties props, int cpuAddress, AddressInfo addressInfo) + { + //TODO + + /*DebugInfo config = ConfigManager.Config.Debug; + foreach(Breakpoint breakpoint in BreakpointManager.Breakpoints) { + if(breakpoint.Matches(cpuAddress, addressInfo)) { + Color fgColor = Color.White; + Color? bgColor = null; + Color bpColor = breakpoint.BreakOnExec ? config.CodeExecBreakpointColor : (breakpoint.BreakOnWrite ? config.CodeWriteBreakpointColor : config.CodeReadBreakpointColor); + Color outlineColor = bpColor; + LineSymbol symbol; + if(breakpoint.Enabled) { + bgColor = bpColor; + symbol = LineSymbol.Circle; + } else { + fgColor = Color.Black; + symbol = LineSymbol.CircleOutline; + } + + if(breakpoint.MarkEvent) { + symbol |= LineSymbol.Mark; + } + + if(!string.IsNullOrWhiteSpace(breakpoint.Condition)) { + symbol |= LineSymbol.Plus; + } + + props.FgColor = fgColor; + props.TextBgColor = bgColor; + props.OutlineColor = outlineColor; + props.Symbol = symbol; + return; + } + }*/ + } + } + } +} diff --git a/UI/Debugger/Controls/ctrlDisassemblyView.resx b/UI/Debugger/Controls/ctrlDisassemblyView.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Debugger/Controls/ctrlDisassemblyView.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/UI/Debugger/Controls/ctrlScrollableTextbox.cs b/UI/Debugger/Controls/ctrlScrollableTextbox.cs index 4819507..ba79706 100644 --- a/UI/Debugger/Controls/ctrlScrollableTextbox.cs +++ b/UI/Debugger/Controls/ctrlScrollableTextbox.cs @@ -61,7 +61,6 @@ namespace Mesen.GUI.Debugger.Controls this.panelSearch.Location = new System.Drawing.Point(this.Width - this.panelSearch.Width - 20, -1); this.ctrlTextbox.ShowLineNumbers = true; - this.ctrlTextbox.ShowLineInHex = true; this.hScrollBar.ValueChanged += hScrollBar_ValueChanged; this.vScrollBar.ValueChanged += vScrollBar_ValueChanged; @@ -164,35 +163,21 @@ namespace Mesen.GUI.Debugger.Controls public ctrlTextbox.ILineStyleProvider StyleProvider { set { this.ctrlTextbox.StyleProvider = value; } } - public int GetLineIndex(int lineNumber) + public ICodeDataProvider DataProvider { - return this.ctrlTextbox.GetLineIndex(lineNumber); + set + { + this.ctrlTextbox.DataProvider = value; + UpdateHorizontalScrollbar(); + UpdateVerticalScrollbar(); + } } public int GetLineIndexAtPosition(int yPos) { return this.ctrlTextbox.GetLineIndexAtPosition(yPos); } - - public string GetLineNoteAtLineIndex(int lineIndex) - { - if(lineIndex >= 0 && lineIndex < this.ctrlTextbox.LineNumberNotes.Length) { - return this.ctrlTextbox.LineNumberNotes[lineIndex]; - } else { - return ""; - } - } - - public int GetLineNumber(int lineIndex) - { - return this.ctrlTextbox.GetLineNumber(lineIndex); - } - - public int GetLineNumberAtPosition(int yPos) - { - return this.GetLineNumber(this.GetLineIndexAtPosition(yPos)); - } - + public int GetNumberVisibleLines() { return this.ctrlTextbox.GetNumberVisibleLines(); @@ -203,9 +188,9 @@ namespace Mesen.GUI.Debugger.Controls this.ctrlTextbox.ScrollToLineIndex(lineIndex, historyType, scrollToTop, forceScroll); } - public void ScrollToLineNumber(int lineNumber, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false) + public void ScrollToAddress(int lineNumber, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false) { - this.ctrlTextbox.ScrollToLineNumber(lineNumber, historyType, scrollToTop, forceScroll); + this.ctrlTextbox.ScrollToAddress(lineNumber, historyType, scrollToTop, forceScroll); } public void CopySelection(bool copyLineNumbers, bool copyContentNotes, bool copyComments) @@ -363,52 +348,6 @@ namespace Mesen.GUI.Debugger.Controls this.ctrlTextbox.HorizontalScrollPosition = this.hScrollBar.Value; } - public string[] Addressing { set { this.ctrlTextbox.Addressing = value; } } - public string[] Comments { set { this.ctrlTextbox.Comments = value; } } - public int[] LineIndentations{ set { this.ctrlTextbox.LineIndentations = value; } } - - public string[] TextLines - { - set - { - this.ctrlTextbox.TextLines = value; - UpdateVerticalScrollbar(); - UpdateHorizontalScrollbar(); - } - } - - public string[] TextLineNotes - { - set - { - this.ctrlTextbox.TextLineNotes = value; - } - } - - public string[] CompareLines - { - set - { - this.ctrlTextbox.CompareLines = value; - } - } - - public int[] LineNumbers - { - set - { - this.ctrlTextbox.LineNumbers = value; - } - } - - public string[] LineNumberNotes - { - set - { - this.ctrlTextbox.LineNumberNotes = value; - } - } - public bool ShowSingleContentLineNotes { get { return this.ctrlTextbox.ShowSingleContentLineNotes; } @@ -417,16 +356,16 @@ namespace Mesen.GUI.Debugger.Controls public bool ShowContentNotes { - get { return this.ctrlTextbox.ShowContentNotes; } - set { this.ctrlTextbox.ShowContentNotes = value; } + get { return this.ctrlTextbox.ShowByteCode; } + set { this.ctrlTextbox.ShowByteCode = value; } } public bool ShowCompactPrgAddresses { get { return this.ctrlTextbox.ShowCompactPrgAddresses; } set { this.ctrlTextbox.ShowCompactPrgAddresses = value; } } public bool ShowLineNumberNotes { - get { return this.ctrlTextbox.ShowLineNumberNotes; } - set { this.ctrlTextbox.ShowLineNumberNotes = value; } + get { return this.ctrlTextbox.ShowAbsoluteAddreses; } + set { this.ctrlTextbox.ShowAbsoluteAddreses = value; } } public bool ShowSingleLineLineNumberNotes @@ -457,14 +396,6 @@ namespace Mesen.GUI.Debugger.Controls public int SelectionStart { get { return this.ctrlTextbox.SelectionStart; } } public int SelectionLength { get { return this.ctrlTextbox.SelectionLength; } } - public string Header - { - set - { - this.ctrlTextbox.Header = value; - } - } - public int MarginWidth { set { this.ctrlTextbox.MarginWidth = value; } } public void OpenSearchBox(bool forceFocus = false) @@ -534,11 +465,6 @@ namespace Mesen.GUI.Debugger.Controls } } - public string GetLineContent(int lineIndex) - { - return this.ctrlTextbox.GetFullWidthString(lineIndex); - } - public void NavigateForward() { this.ctrlTextbox.NavigateForward(); @@ -548,29 +474,5 @@ namespace Mesen.GUI.Debugger.Controls { this.ctrlTextbox.NavigateBackward(); } - - public bool GetNoteRangeAtLocation(int yPos, out int rangeStart, out int rangeEnd) - { - rangeStart = -1; - rangeEnd = -1; - int lineIndex = GetLineIndexAtPosition(yPos); - - while(lineIndex < LineCount - 2 && string.IsNullOrWhiteSpace(GetLineNoteAtLineIndex(lineIndex))) { - //Find the address of the next line with an address - lineIndex++; - } - - if(Int32.TryParse(GetLineNoteAtLineIndex(lineIndex), NumberStyles.AllowHexSpecifier, null, out rangeStart)) { - while(lineIndex < LineCount - 2 && string.IsNullOrWhiteSpace(GetLineNoteAtLineIndex(lineIndex + 1))) { - //Find the next line with an address - lineIndex++; - } - if(Int32.TryParse(GetLineNoteAtLineIndex(lineIndex + 1), NumberStyles.AllowHexSpecifier, null, out rangeEnd)) { - rangeEnd--; - return true; - } - } - return false; - } } } diff --git a/UI/Debugger/Controls/ctrlScrollableTextbox.designer.cs b/UI/Debugger/Controls/ctrlScrollableTextbox.designer.cs index d3d2f78..fe8211d 100644 --- a/UI/Debugger/Controls/ctrlScrollableTextbox.designer.cs +++ b/UI/Debugger/Controls/ctrlScrollableTextbox.designer.cs @@ -155,9 +155,8 @@ this.ctrlTextbox.Location = new System.Drawing.Point(0, 0); this.ctrlTextbox.Name = "ctrlTextbox"; this.ctrlTextbox.ShowCompactPrgAddresses = false; - this.ctrlTextbox.ShowContentNotes = false; - this.ctrlTextbox.ShowLineInHex = false; - this.ctrlTextbox.ShowLineNumberNotes = false; + this.ctrlTextbox.ShowByteCode = false; + this.ctrlTextbox.ShowAbsoluteAddreses = false; this.ctrlTextbox.ShowLineNumbers = true; this.ctrlTextbox.ShowMemoryValues = false; this.ctrlTextbox.ShowSingleContentLineNotes = true; diff --git a/UI/Debugger/Controls/ctrlTextbox.cs b/UI/Debugger/Controls/ctrlTextbox.cs index b214ba6..9e206bb 100644 --- a/UI/Debugger/Controls/ctrlTextbox.cs +++ b/UI/Debugger/Controls/ctrlTextbox.cs @@ -26,25 +26,22 @@ namespace Mesen.GUI.Debugger.Controls private TextboxHistory _history = new TextboxHistory(); - private string[] _contents = new string[0]; - private string[] _contentNotes = new string[0]; - private string[] _compareContents = null; - private int[] _lineNumbers = new int[0]; - private string[] _lineNumberNotes = new string[0]; - private Dictionary _lineNumberIndex = new Dictionary(); + private ICodeDataProvider _dataProvider; + private bool _showLineNumbers = false; - private bool _showLineInHex = false; - private bool _showLineNumberNotes = false; + private bool _showAbsoluteAddresses = false; private bool _showSingleLineLineNumberNotes = false; private bool _showContentNotes = false; private bool _showSingleLineContentNotes = true; private bool _showCompactPrgAddresses = false; + private int _selectionStart = 0; private int _selectionLength = 0; + private int _scrollPosition = 0; private int _horizontalScrollPosition = 0; + private string _searchString = null; - private string _header = null; private Font _noteFont = null; private int _marginWidth = 9; private int _extendedMarginWidth = 16; @@ -59,41 +56,12 @@ namespace Mesen.GUI.Debugger.Controls this.DoubleBuffered = true; this.UpdateFont(); } - - public string[] Comments; - public string[] Addressing; - public int[] LineIndentations; - public string[] TextLines + public ICodeDataProvider DataProvider { set { - int maxLength = 0; - - _maxLineWidthIndex = 0; - - _contents = value; - for(int i = 0, len = value.Length; i < len; i++) { - int length = _contents[i].Length + (Addressing != null ? Addressing[i].Length : 0); - if(Comments?[i].Length > 0) { - length = Math.Max(length, length > 0 ? CommentSpacingCharCount : 0) + Comments[i].Length; - } - if(length > maxLength) { - maxLength = length; - _maxLineWidthIndex = i; - } - } - - UpdateHorizontalScrollWidth(); - - if(_lineNumbers.Length != _contents.Length) { - _lineNumbers = new int[_contents.Length]; - _lineNumberIndex.Clear(); - for(int i = _contents.Length - 1; i >=0; i--) { - _lineNumbers[i] = i; - _lineNumberIndex[i] = i; - } - } + this._dataProvider = value; this.Invalidate(); } } @@ -165,7 +133,7 @@ namespace Mesen.GUI.Debugger.Controls } } - public bool ShowContentNotes + public bool ShowByteCode { get { return _showContentNotes; } set @@ -207,78 +175,24 @@ namespace Mesen.GUI.Debugger.Controls } } - public bool ShowLineNumberNotes + public bool ShowAbsoluteAddreses { - get { return this._showLineNumberNotes; } + get { return this._showAbsoluteAddresses; } set { - this._showLineNumberNotes = value; + this._showAbsoluteAddresses = value; this.Invalidate(); } } - public string[] TextLineNotes - { - set - { - this._contentNotes = value; - this.Invalidate(); - } - } - - public string[] CompareLines - { - set - { - _compareContents = value; - } - } - public int LineCount { get { - return _contents.Length; + return _dataProvider.GetLineCount(); } } - - public int[] LineNumbers - { - set - { - _lineNumbers = value; - _lineNumberIndex.Clear(); - int i = 0; - foreach(int line in _lineNumbers) { - _lineNumberIndex[line] = i; - i++; - } - this.Invalidate(); - } - } - - public string[] LineNumberNotes - { - set - { - _lineNumberNotes = value; - this.Invalidate(); - } - get - { - return _lineNumberNotes; - } - } - - public string Header - { - set - { - this._header = value; - this.Invalidate(); - } - } - + public int MarginWidth { set @@ -306,8 +220,8 @@ namespace Mesen.GUI.Debugger.Controls startPosition = this.SelectionStart; endPosition = this.SelectionStart - searchOffset; if(endPosition < 0) { - endPosition = _contents.Length - 1; - } else if(endPosition >= _contents.Length) { + endPosition = this.LineCount - 1; + } else if(endPosition >= this.LineCount) { endPosition = 0; } @@ -315,35 +229,45 @@ namespace Mesen.GUI.Debugger.Controls startPosition = this.SelectionStart + searchOffset; endPosition = this.SelectionStart; if(startPosition < 0) { - startPosition = _contents.Length - 1; - } else if(startPosition >= _contents.Length) { + startPosition = this.LineCount - 1; + } else if(startPosition >= this.LineCount) { startPosition = 0; } } - for(int i = startPosition; i != endPosition; i += searchOffset) { - string line = _contents[i].ToLowerInvariant(); - if(line.Contains(this._searchString)) { - this.ScrollToLineIndex(i); + if(_dataProvider.UseOptimizedSearch) { + Int32 lineIndex = _dataProvider.GetNextResult(searchString, startPosition, endPosition, searchBackwards); + if(lineIndex >= 0) { + this.ScrollToLineIndex(lineIndex); this.Invalidate(); return true; } + return false; + } else { + for(int i = startPosition; i != endPosition; i += searchOffset) { + string line = _dataProvider.GetCodeLineData(i).Text.ToLowerInvariant(); + if(line.Contains(this._searchString)) { + this.ScrollToLineIndex(i); + this.Invalidate(); + return true; + } - //Continue search from start/end of document - if(!searchBackwards && i == this._contents.Length - 1) { - i = 0; - } else if(searchBackwards && i == 0) { - i = this._contents.Length - 1; + //Continue search from start/end of document + if(!searchBackwards && i == this.LineCount - 1) { + i = 0; + } else if(searchBackwards && i == 0) { + i = this.LineCount - 1; + } } + this.Invalidate(); + return _dataProvider.GetCodeLineData(_selectionStart).Text.ToLowerInvariant().Contains(this._searchString); } - this.Invalidate(); - return _contents[_selectionStart].ToLowerInvariant().Contains(this._searchString); } } public interface ILineStyleProvider { - LineProperties GetLineStyle(int cpuAddress, int lineIndex); + LineProperties GetLineStyle(CodeLineData lineData, int lineIndex); string GetLineComment(int lineIndex); } @@ -361,35 +285,12 @@ namespace Mesen.GUI.Debugger.Controls public LineProperties GetLineStyle(int lineIndex) { if(StyleProvider != null) { - return StyleProvider.GetLineStyle(_lineNumbers[lineIndex], lineIndex); + return StyleProvider.GetLineStyle(_dataProvider.GetCodeLineData(lineIndex), lineIndex); } else { return null; } } - public int GetLineIndex(int lineNumber) - { - if(_lineNumberIndex.ContainsKey(lineNumber)) { - return _lineNumberIndex[lineNumber]; - } else { - foreach(int line in _lineNumbers) { - if(line > lineNumber) { - return Math.Max(0, GetLineIndex(line) - 1); - } - } - } - return -1; - } - - public int GetLineNumber(int lineIndex) - { - if(_lineNumbers.Length <= lineIndex) { - return 0; - } else { - return _lineNumbers[lineIndex]; - } - } - public void ScrollToLineIndex(int lineIndex, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false) { bool scrolled = false; @@ -403,9 +304,10 @@ namespace Mesen.GUI.Debugger.Controls //Line isn't currently visible, scroll it to the middle of the viewport if(scrollToTop) { int scrollPos = lineIndex; - while(scrollPos > 0 && _lineNumbers[scrollPos - 1] < 0 && string.IsNullOrWhiteSpace(_lineNumberNotes[scrollPos - 1])) { + CodeLineData lineData = _dataProvider.GetCodeLineData(scrollPos); + while(scrollPos > 0 && lineData.Address < 0 && lineData.AbsoluteAddress < 0) { //Make sure any comment for the line is in scroll view - bool emptyLine = string.IsNullOrWhiteSpace(_contents[scrollPos]) && string.IsNullOrWhiteSpace(this.Comments[scrollPos]); + bool emptyLine = string.IsNullOrWhiteSpace(lineData.Text) && string.IsNullOrWhiteSpace(lineData.Comment); if(emptyLine) { //If there's a blank line, stop scrolling up scrollPos++; @@ -413,7 +315,8 @@ namespace Mesen.GUI.Debugger.Controls } scrollPos--; - if(emptyLine || _contents[scrollPos].StartsWith("--") || _contents[scrollPos].StartsWith("__")) { + _dataProvider.GetCodeLineData(scrollPos); + if(emptyLine || lineData.Flags.HasFlag(LineFlags.BlockEnd) || lineData.Flags.HasFlag(LineFlags.BlockStart)) { //Reached the start of a block, stop going back up break; } @@ -437,9 +340,9 @@ namespace Mesen.GUI.Debugger.Controls } } - public void ScrollToLineNumber(int lineNumber, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false) + public void ScrollToAddress(int address, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false) { - int lineIndex = this.GetLineIndex(lineNumber); + int lineIndex = _dataProvider.GetLineIndex((UInt32)address); if(lineIndex >= 0) { ScrollToLineIndex(lineIndex, historyType, scrollToTop, forceScroll); } @@ -457,7 +360,7 @@ namespace Mesen.GUI.Debugger.Controls private int GetMargin(Graphics g, bool getExtendedMargin) { - int marginWidth = getExtendedMargin && this.ShowContentNotes && this.ShowSingleContentLineNotes ? _marginWidth + _extendedMarginWidth : _marginWidth; + int marginWidth = getExtendedMargin && this.ShowByteCode && this.ShowSingleContentLineNotes ? _marginWidth + _extendedMarginWidth : _marginWidth; if(ShowCompactPrgAddresses) { marginWidth += 4; } @@ -466,18 +369,23 @@ namespace Mesen.GUI.Debugger.Controls public int GetLineIndexAtPosition(int yPos) { + if(this._dataProvider == null) { + return 0; + } + int lineIndex = this.ScrollPosition + this.GetLineAtPosition(yPos); - if(lineIndex > _contents.Length && _contents.Length != 0) { - lineIndex = _contents.Length - 1; + if(lineIndex > this.LineCount && this.LineCount != 0) { + lineIndex = this.LineCount - 1; } return lineIndex; } public string GetFullWidthString(int lineIndex) { - string text = _contents[lineIndex] + Addressing?[lineIndex]; - if(Comments?[lineIndex].Length > 0) { - return text.PadRight(text.Length > 0 ? CommentSpacingCharCount : 0) + Comments[lineIndex]; + CodeLineData lineData = _dataProvider.GetCodeLineData(lineIndex); + string text = lineData.Text + lineData.GetEffectiveAddressString(); + if(lineData.Comment.Length > 0) { + return text.PadRight(text.Length > 0 ? CommentSpacingCharCount : 0) + lineData.Comment; } return text; } @@ -488,12 +396,13 @@ namespace Mesen.GUI.Debugger.Controls using(Graphics g = Graphics.FromHwnd(this.Handle)) { int marginLeft = this.GetMargin(g, true); lineIndex = this.GetLineIndexAtPosition(position.Y); - if(lineIndex >= _contents.Length) { + if(lineIndex >= this.LineCount) { return false; } int positionX = position.X - marginLeft; - positionX -= (LineIndentations != null ? LineIndentations[lineIndex] : 0); + //TODO + //positionX -= (LineIndentations != null ? LineIndentations[lineIndex] : 0); if(positionX >= 0) { float charWidth = g.MeasureString("W", this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; charIndex = (int)(positionX / charWidth); @@ -534,9 +443,6 @@ namespace Mesen.GUI.Debugger.Controls private int GetLineAtPosition(int yPos) { - if(!string.IsNullOrWhiteSpace(this._header)) { - yPos -= this.LineHeight; - } return Math.Max(0, yPos / this.LineHeight); } @@ -555,10 +461,10 @@ namespace Mesen.GUI.Debugger.Controls [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectionStart { - get { return Math.Min(this._contents.Length - 1, Math.Max(0, _selectionStart)); } + get { return Math.Min(this.LineCount - 1, Math.Max(0, _selectionStart)); } set { - int selectionStart = Math.Max(0, Math.Min(this._contents.Length - 1, Math.Max(0, value))); + int selectionStart = Math.Max(0, Math.Min(this.LineCount - 1, Math.Max(0, value))); _selectionStart = selectionStart; @@ -574,13 +480,13 @@ namespace Mesen.GUI.Debugger.Controls [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public int SelectionLength { - get { return (this.SelectionStart + _selectionLength) > this._contents.Length - 1 ? this._contents.Length - this.SelectionStart - 1 : _selectionLength; } + get { return (this.SelectionStart + _selectionLength) > this.LineCount - 1 ? this.LineCount - this.SelectionStart - 1 : _selectionLength; } set { _selectionLength = value; - if(this.SelectionStart + _selectionLength > this._contents.Length - 1) { - _selectionLength = this._contents.Length - this.SelectionStart - 1; + if(this.SelectionStart + _selectionLength > this.LineCount - 1) { + _selectionLength = this.LineCount - this.SelectionStart - 1; } if(value == 0) { @@ -616,14 +522,14 @@ namespace Mesen.GUI.Debugger.Controls bool singleLineSelection = this.SelectionLength == 0; if(singleLineSelection) { - if(this.SelectionStart + this.SelectionLength >= this._contents.Length - 1) { + if(this.SelectionStart + this.SelectionLength >= this.LineCount - 1) { //End of document reached break; } this.SelectedLine = this.SelectionStart + 1; this.SelectionLength++; } else if(this.SelectionStart + this.SelectionLength == this.SelectedLine) { - if(this.SelectionStart + this.SelectionLength >= this._contents.Length - 1) { + if(this.SelectionStart + this.SelectionLength >= this.LineCount - 1) { //End of document reached break; } @@ -674,12 +580,12 @@ namespace Mesen.GUI.Debugger.Controls public int CurrentLine { - get { return _lineNumbers.Length > _selectionStart ? _lineNumbers[_selectionStart] : 0; } + get { return this.LineCount > _selectionStart ? _dataProvider.GetLineAddress(_selectionStart) : 0; } } public int LastSelectedLine { - get { return _lineNumbers.Length > _selectionStart + this.SelectionLength ? _lineNumbers[_selectionStart + this.SelectionLength] : 0; } + get { return this.LineCount > _selectionStart + this.SelectionLength ? _dataProvider.GetLineAddress(_selectionStart + this.SelectionLength) : 0; } } [Browsable(false)] @@ -689,7 +595,7 @@ namespace Mesen.GUI.Debugger.Controls get { return _scrollPosition; } set { - value = Math.Max(0, Math.Min(value, this._contents.Length-this.GetNumberVisibleLines())); + value = Math.Max(0, Math.Min(value, this.LineCount-this.GetNumberVisibleLines())); _scrollPosition = value; if(!_disableScrollPositionChangedEvent && this.ScrollPositionChanged != null) { ScrollPositionChanged(this, null); @@ -721,7 +627,8 @@ namespace Mesen.GUI.Debugger.Controls private void UpdateHorizontalScrollWidth() { - if(LineIndentations != null && LineIndentations.Length > _maxLineWidthIndex) { + //TODO + /*if(LineIndentations != null && LineIndentations.Length > _maxLineWidthIndex) { using(Graphics g = this.CreateGraphics()) { _maxLineWidth = (LineIndentations != null ? LineIndentations[_maxLineWidthIndex] : 0) + g.MeasureString(GetFullWidthString(_maxLineWidthIndex), this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; HorizontalScrollWidth = (int)(Math.Max(0, HorizontalScrollFactor + _maxLineWidth - (this.Width - GetMargin(g, true))) / HorizontalScrollFactor); @@ -730,7 +637,7 @@ namespace Mesen.GUI.Debugger.Controls _maxLineWidth = 0; HorizontalScrollPosition = 0; HorizontalScrollWidth = 0; - } + }*/ } public void SetMessage(TextboxMessageInfo message) @@ -744,6 +651,10 @@ namespace Mesen.GUI.Debugger.Controls protected override void OnResize(EventArgs e) { base.OnResize(e); + if(_dataProvider == null) { + return; + } + this.ScrollPosition = this.ScrollPosition; UpdateHorizontalScrollWidth(); ScrollPositionChanged?.Invoke(this, e); @@ -755,17 +666,11 @@ namespace Mesen.GUI.Debugger.Controls set { _showLineNumbers = value; } } - public bool ShowLineInHex - { - get { return _showLineInHex; } - set { _showLineInHex = value; } - } - private int LineHeight { get { - if(this.ShowLineNumberNotes && !this.ShowSingleLineLineNumberNotes || this.ShowContentNotes && !this.ShowSingleContentLineNotes) { + if(this.ShowAbsoluteAddreses && !this.ShowSingleLineLineNumberNotes || this.ShowByteCode && !this.ShowSingleContentLineNotes) { return (int)(this.FontHeight * 1.60); } else { return this.FontHeight - 1; @@ -847,14 +752,14 @@ namespace Mesen.GUI.Debugger.Controls { StringBuilder sb = new StringBuilder(); for(int i = this.SelectionStart, end = this.SelectionStart + this.SelectionLength; i <= end; i++) { - string indent = ""; - if(LineIndentations != null) { - indent = "".PadLeft(LineIndentations[i] / 10); - } + CodeLineData lineData = _dataProvider.GetCodeLineData(i); - string codeString = _contents[i].Trim(); - if(codeString.StartsWith("__") || codeString.StartsWith("--")) { - codeString = "--------" + codeString.Substring(2, codeString.Length - 4) + "--------"; + string indent = ""; + indent = "".PadLeft(lineData.Indentation / 10); + + string codeString = lineData.Text.Trim(); + if(lineData.Flags.HasFlag(LineFlags.BlockEnd) || lineData.Flags.HasFlag(LineFlags.BlockStart)) { + codeString = "--------" + codeString + "--------"; } int padding = Math.Max(CommentSpacingCharCount, codeString.Length); @@ -865,14 +770,14 @@ namespace Mesen.GUI.Debugger.Controls codeString = codeString.PadRight(padding); string line = indent + codeString; - if(copyContentNotes && _contentNotes[i].Length > 0) { - line = _contentNotes[i].PadRight(13) + line; + if(copyContentNotes && lineData.ByteCode.Length > 0) { + line = lineData.ByteCode.PadRight(13) + line; } - if(copyLineNumbers && _lineNumbers[i] >= 0) { - line = _lineNumbers[i].ToString("X4") + " " + line; + if(copyLineNumbers && lineData.Address >= 0) { + line = lineData.Address.ToString("X4") + " " + line; } - if(copyComments && !string.IsNullOrWhiteSpace(Comments[i])) { - line = line + Comments[i]; + if(copyComments && !string.IsNullOrWhiteSpace(lineData.Comment)) { + line = line + lineData.Comment; } sb.AppendLine(line); } @@ -891,19 +796,16 @@ namespace Mesen.GUI.Debugger.Controls private void DrawLine(Graphics g, int currentLine, int marginLeft, int positionY, int lineHeight) { - string codeString = _contents[currentLine]; - string addressString = this.Addressing?[currentLine]; - string commentString = this.Comments?[currentLine]; - + CodeLineData lineData = _dataProvider.GetCodeLineData(currentLine); + string codeString = lineData.Text; float codeStringLength = g.MeasureString(codeString, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; - float addressStringLength = g.MeasureString(addressString, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; //Adjust background color highlights based on number of spaces in front of content int originalMargin = marginLeft; - marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0); + marginLeft += lineData.Indentation; - bool isBlockStart = codeString.StartsWith("__") && codeString.EndsWith("__"); - bool isBlockEnd = codeString.StartsWith("--") && codeString.EndsWith("--"); + bool isBlockStart = lineData.Flags.HasFlag(LineFlags.BlockStart); + bool isBlockEnd = lineData.Flags.HasFlag(LineFlags.BlockEnd); Color? textColor = null; LineProperties lineProperties = GetLineStyle(currentLine); @@ -953,28 +855,28 @@ namespace Mesen.GUI.Debugger.Controls } } - this.DrawLineText(g, currentLine, marginLeft, positionY, codeString, addressString, commentString, codeStringLength, addressStringLength, textColor, lineHeight); + this.DrawLineText(g, currentLine, marginLeft, positionY, lineData, codeStringLength, textColor, lineHeight); } - private void DrawLineNumber(Graphics g, int currentLine, int marginLeft, int positionY, Color addressColor) + private void DrawLineNumber(Graphics g, CodeLineData lineData, int marginLeft, int positionY, Color addressColor) { using(Brush numberBrush = new SolidBrush(addressColor)) { - if(this.ShowLineNumberNotes && this.ShowSingleLineLineNumberNotes) { + if(this.ShowAbsoluteAddreses && this.ShowSingleLineLineNumberNotes) { //Display line note instead of line number string lineNumber; - if(string.IsNullOrEmpty(_lineNumberNotes[currentLine])) { - lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : ".."; + if(lineData.AbsoluteAddress == 0) { + lineNumber = lineData.Address >= 0 ? lineData.Address.ToString("X6") : ".."; } else { - lineNumber = _lineNumberNotes[currentLine]; + lineNumber = lineData.AbsoluteAddress.ToString("X6"); } float width = g.MeasureString(lineNumber, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; g.DrawString(lineNumber, this.Font, numberBrush, marginLeft - width, positionY, StringFormat.GenericTypographic); } else { //Display line number - string lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : ".."; + string lineNumber = lineData.Address >= 0 ? lineData.Address.ToString("X6") : ".."; if(ShowCompactPrgAddresses) { - string lineNumberNote = _lineNumberNotes[currentLine]; + string lineNumberNote = lineData.AbsoluteAddress.ToString("X6"); if(lineNumberNote.Length > 3) { string compactView = lineNumberNote.Substring(0, lineNumberNote.Length - 3).TrimStart('0'); if(compactView.Length == 0) { @@ -987,34 +889,39 @@ namespace Mesen.GUI.Debugger.Controls float width = g.MeasureString(lineNumber, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; g.DrawString(lineNumber, this.Font, numberBrush, marginLeft - width, positionY, StringFormat.GenericTypographic); - if(this.ShowLineNumberNotes && !this.ShowSingleLineLineNumberNotes) { + if(this.ShowAbsoluteAddreses && !this.ShowSingleLineLineNumberNotes) { //Display line note below line number - width = g.MeasureString(_lineNumberNotes[currentLine], _noteFont, int.MaxValue, StringFormat.GenericTypographic).Width; - g.DrawString(_lineNumberNotes[currentLine], _noteFont, numberBrush, marginLeft - width, positionY+this.Font.Size+3, StringFormat.GenericTypographic); + string absAddress = lineData.AbsoluteAddress.ToString("X6"); + width = g.MeasureString(absAddress, _noteFont, int.MaxValue, StringFormat.GenericTypographic).Width; + g.DrawString(absAddress, _noteFont, numberBrush, marginLeft - width, positionY+this.Font.Size+3, StringFormat.GenericTypographic); } } } } - private void DrawLineText(Graphics g, int currentLine, int marginLeft, int positionY, string codeString, string addressString, string commentString, float codeStringLength, float addressStringLength, Color? textColor, int lineHeight) + private void DrawLineText(Graphics g, int currentLine, int marginLeft, int positionY, CodeLineData lineData, float codeStringLength, Color? textColor, int lineHeight) { + string codeString = lineData.Text; + string addressString = lineData.EffectiveAddress>= 0 ? (" [" + lineData.EffectiveAddress.ToString("X6") + "]") : ""; + float addressStringLength = g.MeasureString(addressString, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; + string commentString = lineData.Comment; + DebugInfo info = ConfigManager.Config.Debug; - if(codeString.StartsWith("__") && codeString.EndsWith("__") || codeString.StartsWith("--") && codeString.EndsWith("--")) { + if(lineData.Flags.HasFlag(LineFlags.BlockEnd) || lineData.Flags.HasFlag(LineFlags.BlockStart)) { //Draw block start/end g.TranslateTransform(HorizontalScrollPosition * HorizontalScrollFactor, 0); - string text = codeString.Substring(2, codeString.Length - 4); - float yOffset = codeString.StartsWith("__") ? 2 : -2; - if(text.Length > 0) { - SizeF size = g.MeasureString(text, this._noteFont, int.MaxValue, StringFormat.GenericTypographic); + float yOffset = lineData.Flags.HasFlag(LineFlags.BlockStart) ? 2 : -2; + if(codeString.Length > 0) { + SizeF size = g.MeasureString(codeString, this._noteFont, int.MaxValue, StringFormat.GenericTypographic); float textLength = size.Width; float textHeight = size.Height; float positionX = (marginLeft + this.Width - textLength) / 2; g.DrawLine(Pens.Black, marginLeft, yOffset + positionY + lineHeight / 2, marginLeft + this.Width, yOffset + positionY + lineHeight / 2); - yOffset = codeString.StartsWith("__") ? 3 : 2; + yOffset = lineData.Flags.HasFlag(LineFlags.BlockStart) ? 3 : 2; g.FillRectangle(Brushes.White, positionX - 4, yOffset + positionY, textLength + 8, textHeight); g.DrawRectangle(Pens.Black, positionX - 4, yOffset + positionY, textLength + 8, textHeight); - g.DrawString(text, this._noteFont, Brushes.Black, positionX, yOffset + positionY, StringFormat.GenericTypographic); + g.DrawString(codeString, this._noteFont, Brushes.Black, positionX, yOffset + positionY, StringFormat.GenericTypographic); } else { g.DrawLine(Pens.Black, marginLeft, yOffset + positionY + lineHeight / 2, marginLeft + this.Width, yOffset + positionY + lineHeight / 2); } @@ -1069,31 +976,10 @@ namespace Mesen.GUI.Debugger.Controls } if(this.ShowMemoryValues && memoryAddress.Length > 0) { - int address = -1; - if(memoryAddress[0] == '$') { - Int32.TryParse(memoryAddress.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier, null, out address); - } else { - //Label - UInt32 arrayOffset = 0; - if(!string.IsNullOrWhiteSpace(arrayPosition)) { - memoryAddress = memoryAddress.Substring(0, memoryAddress.Length - arrayPosition.Length - 1); - arrayOffset = UInt32.Parse(arrayPosition); - } - - //TODO - /*CodeLabel label = LabelManager.GetLabel(memoryAddress); - if(label != null) { - address = InteropEmu.DebugGetRelativeAddress(label.Address + arrayOffset, label.AddressType); - }*/ - } - - //TODO - /* - if(address >= 0) { + if(lineData.Value >= 0) { colors.Add(defaultColor); - parts.Add(" = $" + InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, (UInt32)address).ToString("X2")); + parts.Add(" = $" + lineData.Value.ToString("X4")); } - */ } //Display the rest of the line (used by trace logger) @@ -1128,11 +1014,10 @@ namespace Mesen.GUI.Debugger.Controls } } - if(this.ShowContentNotes && !this.ShowSingleContentLineNotes) { - g.DrawString(_contentNotes[currentLine], _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3, StringFormat.GenericTypographic); + if(this.ShowByteCode && !this.ShowSingleContentLineNotes) { + g.DrawString(lineData.ByteCode, _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3, StringFormat.GenericTypographic); } this.DrawHighlightedSearchString(g, codeString, marginLeft, positionY); - this.DrawHighlightedCompareString(g, codeString, currentLine, marginLeft, positionY); } } string _lastSymbolComment = null; @@ -1237,25 +1122,6 @@ namespace Mesen.GUI.Debugger.Controls } } - private void DrawHighlightedCompareString(Graphics g, string lineText, int currentLine, int marginLeft, int positionY) - { - if(_compareContents != null && _compareContents.Length > currentLine) { - string compareText = _compareContents[currentLine]; - - if(compareText != lineText) { - StringBuilder sb = new StringBuilder(); - for(int i = 0, len = lineText.Length; i < len; i++) { - if(lineText[i] == compareText[i]) { - sb.Append(" "); - } else { - sb.Append(lineText[i]); - } - } - - g.DrawString(sb.ToString(), new Font(this.Font, FontStyle.Bold), Brushes.Red, marginLeft, positionY, StringFormat.GenericTypographic); - } - } - } private void DrawHighlightedSearchString(Graphics g, string lineText, int marginLeft, int positionY) { @@ -1288,6 +1154,7 @@ namespace Mesen.GUI.Debugger.Controls private void DrawMargin(Graphics g, int currentLine, int marginLeft, int regularMargin, int positionY, int lineHeight) { + CodeLineData lineData = _dataProvider.GetCodeLineData(currentLine); LineProperties lineProperties = GetLineStyle(currentLine); //Draw instruction progress here to avoid it being scrolled horizontally when window is small (or comments/etc exist) @@ -1300,14 +1167,14 @@ namespace Mesen.GUI.Debugger.Controls if(this.ShowLineNumbers) { //Show line number Color lineNumberColor = lineProperties != null && lineProperties.AddressColor.HasValue ? lineProperties.AddressColor.Value : Color.Gray; - this.DrawLineNumber(g, currentLine, regularMargin, positionY, lineNumberColor); + this.DrawLineNumber(g, lineData, regularMargin, positionY, lineNumberColor); } - if(this.ShowContentNotes && this.ShowSingleContentLineNotes) { - g.DrawString(_contentNotes[currentLine], this.Font, Brushes.Gray, regularMargin + 6, positionY, StringFormat.GenericTypographic); + if(this.ShowByteCode && this.ShowSingleContentLineNotes) { + g.DrawString(lineData.ByteCode, this.Font, Brushes.Gray, regularMargin + 6, positionY, StringFormat.GenericTypographic); } //Adjust background color highlights based on number of spaces in front of content - marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0); + marginLeft += lineData.Indentation; if(lineProperties != null) { this.DrawLineSymbols(g, positionY, lineProperties, lineHeight); @@ -1321,14 +1188,14 @@ namespace Mesen.GUI.Debugger.Controls int endAddress = -1; int end = this.SelectionStart + this.SelectionLength + 1; - while(endAddress < 0 && end < _lineNumbers.Length) { - endAddress = _lineNumbers[end]; + while(endAddress < 0 && end < this.LineCount) { + endAddress = _dataProvider.GetLineAddress(end); end++; } int start = this.SelectionStart; - while(startAddress < 0 && start < _lineNumbers.Length && start < end) { - startAddress = _lineNumbers[start]; + while(startAddress < 0 && start < this.LineCount && start < end) { + startAddress = _dataProvider.GetLineAddress(start); start++; } @@ -1359,6 +1226,10 @@ namespace Mesen.GUI.Debugger.Controls protected override void OnPaint(PaintEventArgs pe) { + if(_dataProvider == null) { + return; + } + _lastSymbolComment = null; int lineHeight = this.LineHeight; pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; @@ -1372,15 +1243,7 @@ namespace Mesen.GUI.Debugger.Controls int currentLine = this.ScrollPosition; int positionY = 0; - if(!string.IsNullOrWhiteSpace(this._header)) { - using(Brush lightGrayBrush = new SolidBrush(Color.FromArgb(240, 240, 240))) { - pe.Graphics.FillRectangle(lightGrayBrush, marginLeft, 0, Math.Max(_maxLineWidth, rect.Right), lineHeight); - } - pe.Graphics.DrawString(_header, this.Font, Brushes.Gray, marginLeft, positionY, StringFormat.GenericTypographic); - positionY += lineHeight; - } - - while(positionY < rect.Bottom && currentLine < _contents.Length) { + while(positionY < rect.Bottom && currentLine < this.LineCount) { this.DrawLine(pe.Graphics, currentLine, marginLeft, positionY, lineHeight); positionY += lineHeight; currentLine++; @@ -1403,8 +1266,8 @@ namespace Mesen.GUI.Debugger.Controls } currentLine = this.ScrollPosition; - positionY = string.IsNullOrWhiteSpace(this._header) ? 0 : lineHeight; - while(positionY < rect.Bottom && currentLine < _contents.Length) { + positionY = 0; + while(positionY < rect.Bottom && currentLine < this.LineCount) { this.DrawMargin(pe.Graphics, currentLine, marginLeft, regularMargin, positionY, lineHeight); positionY += lineHeight; currentLine++; @@ -1456,4 +1319,15 @@ namespace Mesen.GUI.Debugger.Controls { public string Message; } + + public interface ICodeDataProvider + { + CodeLineData GetCodeLineData(int lineIndex); + int GetLineCount(); + int GetNextResult(string searchString, int startPosition, int endPosition, bool searchBackwards); + bool UseOptimizedSearch { get; } + + int GetLineAddress(int lineIndex); + int GetLineIndex(UInt32 address); + } } diff --git a/UI/Debugger/DebugWindowManager.cs b/UI/Debugger/DebugWindowManager.cs index 6aed252..30add7b 100644 --- a/UI/Debugger/DebugWindowManager.cs +++ b/UI/Debugger/DebugWindowManager.cs @@ -31,6 +31,7 @@ namespace Mesen.GUI.Debugger } else { BaseForm frm = null; switch(window) { + case DebugWindow.Debugger: frm = new frmDebugger(); frm.Icon = Properties.Resources.Debugger; break; case DebugWindow.TraceLogger: frm = new frmTraceLogger(); frm.Icon = Properties.Resources.LogWindow; break; case DebugWindow.MemoryTools: frm = new frmMemoryTools(); frm.Icon = Properties.Resources.CheatCode; break; } @@ -85,6 +86,7 @@ namespace Mesen.GUI.Debugger { //Only one of each of these windows can be opened at once, check if one is already opened switch(window) { + case DebugWindow.Debugger: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmDebugger)); case DebugWindow.TraceLogger: return _openedWindows.ToList().Find((form) => form.GetType() == typeof(frmTraceLogger)); } @@ -111,6 +113,7 @@ namespace Mesen.GUI.Debugger public enum DebugWindow { + Debugger, MemoryTools, TraceLogger } diff --git a/UI/Debugger/frmDebugger.Designer.cs b/UI/Debugger/frmDebugger.Designer.cs new file mode 100644 index 0000000..ea4c3d3 --- /dev/null +++ b/UI/Debugger/frmDebugger.Designer.cs @@ -0,0 +1,289 @@ +namespace Mesen.GUI.Debugger +{ + partial class frmDebugger + { + /// + /// 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(); + } + if(this._notifListener != null) { + this._notifListener.Dispose(); + this._notifListener = null; + } + 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.ctrlDisassemblyView = new Mesen.GUI.Debugger.Controls.ctrlDisassemblyView(); + this.ctrlMesenMenuStrip1 = new Mesen.GUI.Controls.ctrlMesenMenuStrip(); + this.debugToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuContinue = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuBreak = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuStepInto = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuStepOver = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuStepOut = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuStepBack = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuReset = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPowerCycle = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem24 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuToggleBreakpoint = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuDisableEnableBreakpoint = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuRunCpuCycle = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRunPpuCycle = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRunScanline = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuRunOneFrame = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuBreakIn = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuBreakOn = new System.Windows.Forms.ToolStripMenuItem(); + this.ctrlMesenMenuStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // ctrlDisassemblyView + // + this.ctrlDisassemblyView.Dock = System.Windows.Forms.DockStyle.Left; + this.ctrlDisassemblyView.Location = new System.Drawing.Point(0, 24); + this.ctrlDisassemblyView.Name = "ctrlDisassemblyView"; + this.ctrlDisassemblyView.Size = new System.Drawing.Size(559, 620); + this.ctrlDisassemblyView.TabIndex = 0; + // + // ctrlMesenMenuStrip1 + // + this.ctrlMesenMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.debugToolStripMenuItem}); + this.ctrlMesenMenuStrip1.Location = new System.Drawing.Point(0, 0); + this.ctrlMesenMenuStrip1.Name = "ctrlMesenMenuStrip1"; + this.ctrlMesenMenuStrip1.Size = new System.Drawing.Size(852, 24); + this.ctrlMesenMenuStrip1.TabIndex = 1; + this.ctrlMesenMenuStrip1.Text = "ctrlMesenMenuStrip1"; + // + // debugToolStripMenuItem + // + this.debugToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuContinue, + this.mnuBreak, + this.toolStripMenuItem3, + this.mnuStepInto, + this.mnuStepOver, + this.mnuStepOut, + this.mnuStepBack, + this.toolStripMenuItem1, + this.mnuReset, + this.mnuPowerCycle, + this.toolStripMenuItem24, + this.mnuToggleBreakpoint, + this.mnuDisableEnableBreakpoint, + this.toolStripMenuItem2, + this.mnuRunCpuCycle, + this.mnuRunPpuCycle, + this.mnuRunScanline, + this.mnuRunOneFrame, + this.toolStripMenuItem8, + this.mnuBreakIn, + this.mnuBreakOn}); + this.debugToolStripMenuItem.Name = "debugToolStripMenuItem"; + this.debugToolStripMenuItem.Size = new System.Drawing.Size(54, 20); + this.debugToolStripMenuItem.Text = "Debug"; + // + // mnuContinue + // + this.mnuContinue.Image = global::Mesen.GUI.Properties.Resources.MediaPlay; + this.mnuContinue.Name = "mnuContinue"; + this.mnuContinue.Size = new System.Drawing.Size(212, 22); + this.mnuContinue.Text = "Continue"; + this.mnuContinue.Click += new System.EventHandler(this.mnuContinue_Click); + // + // mnuBreak + // + this.mnuBreak.Enabled = false; + this.mnuBreak.Image = global::Mesen.GUI.Properties.Resources.MediaPause; + this.mnuBreak.Name = "mnuBreak"; + this.mnuBreak.ShortcutKeyDisplayString = ""; + this.mnuBreak.Size = new System.Drawing.Size(212, 22); + this.mnuBreak.Text = "Break"; + // + // toolStripMenuItem3 + // + this.toolStripMenuItem3.Name = "toolStripMenuItem3"; + this.toolStripMenuItem3.Size = new System.Drawing.Size(209, 6); + // + // mnuStepInto + // + this.mnuStepInto.Image = global::Mesen.GUI.Properties.Resources.StepInto; + this.mnuStepInto.Name = "mnuStepInto"; + this.mnuStepInto.Size = new System.Drawing.Size(212, 22); + this.mnuStepInto.Text = "Step Into"; + this.mnuStepInto.Click += new System.EventHandler(this.mnuStepInto_Click); + // + // mnuStepOver + // + this.mnuStepOver.Image = global::Mesen.GUI.Properties.Resources.StepOver; + this.mnuStepOver.Name = "mnuStepOver"; + this.mnuStepOver.Size = new System.Drawing.Size(212, 22); + this.mnuStepOver.Text = "Step Over"; + // + // mnuStepOut + // + this.mnuStepOut.Image = global::Mesen.GUI.Properties.Resources.StepOut; + this.mnuStepOut.Name = "mnuStepOut"; + this.mnuStepOut.Size = new System.Drawing.Size(212, 22); + this.mnuStepOut.Text = "Step Out"; + // + // mnuStepBack + // + this.mnuStepBack.Image = global::Mesen.GUI.Properties.Resources.StepBack; + this.mnuStepBack.Name = "mnuStepBack"; + this.mnuStepBack.Size = new System.Drawing.Size(212, 22); + this.mnuStepBack.Text = "Step Back"; + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(209, 6); + // + // mnuReset + // + this.mnuReset.Image = global::Mesen.GUI.Properties.Resources.Refresh; + this.mnuReset.Name = "mnuReset"; + this.mnuReset.Size = new System.Drawing.Size(212, 22); + this.mnuReset.Text = "Reset"; + // + // mnuPowerCycle + // + this.mnuPowerCycle.Image = global::Mesen.GUI.Properties.Resources.PowerCycle; + this.mnuPowerCycle.Name = "mnuPowerCycle"; + this.mnuPowerCycle.Size = new System.Drawing.Size(212, 22); + this.mnuPowerCycle.Text = "Power Cycle"; + // + // toolStripMenuItem24 + // + this.toolStripMenuItem24.Name = "toolStripMenuItem24"; + this.toolStripMenuItem24.Size = new System.Drawing.Size(209, 6); + // + // mnuToggleBreakpoint + // + this.mnuToggleBreakpoint.Image = global::Mesen.GUI.Properties.Resources.Breakpoint; + this.mnuToggleBreakpoint.Name = "mnuToggleBreakpoint"; + this.mnuToggleBreakpoint.Size = new System.Drawing.Size(212, 22); + this.mnuToggleBreakpoint.Text = "Toggle Breakpoint"; + // + // mnuDisableEnableBreakpoint + // + this.mnuDisableEnableBreakpoint.Image = global::Mesen.GUI.Properties.Resources.BreakpointDisabled; + this.mnuDisableEnableBreakpoint.Name = "mnuDisableEnableBreakpoint"; + this.mnuDisableEnableBreakpoint.Size = new System.Drawing.Size(212, 22); + this.mnuDisableEnableBreakpoint.Text = "Disable/Enable Breakpoint"; + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(209, 6); + // + // mnuRunCpuCycle + // + this.mnuRunCpuCycle.Image = global::Mesen.GUI.Properties.Resources.JumpTarget; + this.mnuRunCpuCycle.Name = "mnuRunCpuCycle"; + this.mnuRunCpuCycle.Size = new System.Drawing.Size(212, 22); + this.mnuRunCpuCycle.Text = "Run one CPU cycle"; + // + // mnuRunPpuCycle + // + this.mnuRunPpuCycle.Image = global::Mesen.GUI.Properties.Resources.RunPpuCycle; + this.mnuRunPpuCycle.Name = "mnuRunPpuCycle"; + this.mnuRunPpuCycle.Size = new System.Drawing.Size(212, 22); + this.mnuRunPpuCycle.Text = "Run one PPU cycle"; + // + // mnuRunScanline + // + this.mnuRunScanline.Image = global::Mesen.GUI.Properties.Resources.RunPpuScanline; + this.mnuRunScanline.Name = "mnuRunScanline"; + this.mnuRunScanline.Size = new System.Drawing.Size(212, 22); + this.mnuRunScanline.Text = "Run one scanline"; + // + // mnuRunOneFrame + // + this.mnuRunOneFrame.Image = global::Mesen.GUI.Properties.Resources.RunPpuFrame; + this.mnuRunOneFrame.Name = "mnuRunOneFrame"; + this.mnuRunOneFrame.Size = new System.Drawing.Size(212, 22); + this.mnuRunOneFrame.Text = "Run one frame"; + // + // toolStripMenuItem8 + // + this.toolStripMenuItem8.Name = "toolStripMenuItem8"; + this.toolStripMenuItem8.Size = new System.Drawing.Size(209, 6); + // + // mnuBreakIn + // + this.mnuBreakIn.Name = "mnuBreakIn"; + this.mnuBreakIn.Size = new System.Drawing.Size(212, 22); + this.mnuBreakIn.Text = "Break in..."; + // + // mnuBreakOn + // + this.mnuBreakOn.Name = "mnuBreakOn"; + this.mnuBreakOn.Size = new System.Drawing.Size(212, 22); + this.mnuBreakOn.Text = "Break on..."; + // + // frmDebugger + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(852, 644); + this.Controls.Add(this.ctrlDisassemblyView); + this.Controls.Add(this.ctrlMesenMenuStrip1); + this.Name = "frmDebugger"; + this.Text = "frmDebugger"; + this.ctrlMesenMenuStrip1.ResumeLayout(false); + this.ctrlMesenMenuStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Controls.ctrlDisassemblyView ctrlDisassemblyView; + private GUI.Controls.ctrlMesenMenuStrip ctrlMesenMenuStrip1; + private System.Windows.Forms.ToolStripMenuItem debugToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuContinue; + private System.Windows.Forms.ToolStripMenuItem mnuBreak; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; + private System.Windows.Forms.ToolStripMenuItem mnuStepInto; + private System.Windows.Forms.ToolStripMenuItem mnuStepOver; + private System.Windows.Forms.ToolStripMenuItem mnuStepOut; + private System.Windows.Forms.ToolStripMenuItem mnuStepBack; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem mnuReset; + private System.Windows.Forms.ToolStripMenuItem mnuPowerCycle; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem24; + private System.Windows.Forms.ToolStripMenuItem mnuToggleBreakpoint; + private System.Windows.Forms.ToolStripMenuItem mnuDisableEnableBreakpoint; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem mnuRunCpuCycle; + private System.Windows.Forms.ToolStripMenuItem mnuRunPpuCycle; + private System.Windows.Forms.ToolStripMenuItem mnuRunScanline; + private System.Windows.Forms.ToolStripMenuItem mnuRunOneFrame; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8; + private System.Windows.Forms.ToolStripMenuItem mnuBreakIn; + private System.Windows.Forms.ToolStripMenuItem mnuBreakOn; + } +} \ No newline at end of file diff --git a/UI/Debugger/frmDebugger.cs b/UI/Debugger/frmDebugger.cs new file mode 100644 index 0000000..551f20a --- /dev/null +++ b/UI/Debugger/frmDebugger.cs @@ -0,0 +1,94 @@ +using Mesen.GUI.Config; +using Mesen.GUI.Forms; +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; + +namespace Mesen.GUI.Debugger +{ + public partial class frmDebugger : BaseForm + { + private NotificationListener _notifListener; + + public frmDebugger() + { + InitializeComponent(); + if(DesignMode) { + return; + } + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + _notifListener = new NotificationListener(); + _notifListener.OnNotification += OnNotificationReceived; + + InitShortcuts(); + + DebugApi.Step(10000); + } + + protected override void OnClosing(CancelEventArgs e) + { + base.OnClosing(e); + + if(this._notifListener != null) { + this._notifListener.Dispose(); + this._notifListener = null; + } + } + + private void InitShortcuts() + { + mnuReset.InitShortcut(this, nameof(DebuggerShortcutsConfig.Reset)); + mnuPowerCycle.InitShortcut(this, nameof(DebuggerShortcutsConfig.PowerCycle)); + + mnuContinue.InitShortcut(this, nameof(DebuggerShortcutsConfig.Continue)); + mnuBreak.InitShortcut(this, nameof(DebuggerShortcutsConfig.Break)); + mnuBreakIn.InitShortcut(this, nameof(DebuggerShortcutsConfig.BreakIn)); + mnuBreakOn.InitShortcut(this, nameof(DebuggerShortcutsConfig.BreakOn)); + + mnuStepBack.InitShortcut(this, nameof(DebuggerShortcutsConfig.StepBack)); + mnuStepOut.InitShortcut(this, nameof(DebuggerShortcutsConfig.StepOut)); + mnuStepInto.InitShortcut(this, nameof(DebuggerShortcutsConfig.StepInto)); + mnuStepOver.InitShortcut(this, nameof(DebuggerShortcutsConfig.StepOver)); + + mnuRunCpuCycle.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunCpuCycle)); + mnuRunPpuCycle.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunPpuCycle)); + mnuRunScanline.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunPpuScanline)); + mnuRunOneFrame.InitShortcut(this, nameof(DebuggerShortcutsConfig.RunPpuFrame)); + } + + private void OnNotificationReceived(NotificationEventArgs e) + { + switch(e.NotificationType) { + case ConsoleNotificationType.CodeBreak: + DebugState state = DebugApi.GetState(); + int activeAddress = (int)((state.Cpu.K << 16) | state.Cpu.PC); + + this.BeginInvoke((MethodInvoker)(() => { + ctrlDisassemblyView.SetActiveAddress(activeAddress); + })); + break; + } + } + + private void mnuStepInto_Click(object sender, EventArgs e) + { + DebugApi.Step(1); + } + + private void mnuContinue_Click(object sender, EventArgs e) + { + DebugApi.ResumeExecution(); + } + } +} diff --git a/UI/Debugger/frmDebugger.resx b/UI/Debugger/frmDebugger.resx new file mode 100644 index 0000000..914cde3 --- /dev/null +++ b/UI/Debugger/frmDebugger.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 107, 17 + + \ No newline at end of file diff --git a/UI/Debugger/frmTraceLogger.cs b/UI/Debugger/frmTraceLogger.cs index f22ea9a..27dae9d 100644 --- a/UI/Debugger/frmTraceLogger.cs +++ b/UI/Debugger/frmTraceLogger.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using System.Windows.Forms; using Mesen.GUI.Config; using Mesen.GUI.Controls; +using Mesen.GUI.Debugger.Controls; using Mesen.GUI.Forms; namespace Mesen.GUI.Debugger @@ -316,17 +317,13 @@ namespace Mesen.GUI.Debugger } } lineContent.Add(content.TrimStart()); - indent.Add(10); } this.BeginInvoke((Action)(() => { txtTraceLog.ShowContentNotes = showByteCode; txtTraceLog.ShowSingleContentLineNotes = showByteCode; - txtTraceLog.LineIndentations = indent.ToArray(); - txtTraceLog.LineNumbers = programCounter.ToArray(); - txtTraceLog.TextLineNotes = byteCode.ToArray(); - txtTraceLog.TextLines = lineContent.ToArray(); - + txtTraceLog.DataProvider = new TraceLoggerCodeDataProvider(lineContent, programCounter, byteCode); + if(scrollToBottom) { txtTraceLog.ScrollToLineIndex(txtTraceLog.LineCount - 1); } @@ -477,4 +474,50 @@ namespace Mesen.GUI.Debugger txtTraceLog.SelectAll(); } } + + public class TraceLoggerCodeDataProvider : ICodeDataProvider + { + private List _textLines; + private List _addresses; + private List _byteCode; + + public TraceLoggerCodeDataProvider(List lineContent, List programCounter, List byteCode) + { + _addresses = programCounter; + _byteCode = byteCode; + _textLines = lineContent; + } + + public CodeLineData GetCodeLineData(int lineIndex) + { + return new CodeLineData() { + Address = _addresses[lineIndex], + Text = _textLines[lineIndex], + ByteCode = _byteCode[lineIndex], + EffectiveAddress = -1 + }; + } + + public bool UseOptimizedSearch { get { return false; } } + + public int GetLineAddress(int lineIndex) + { + return _addresses[lineIndex]; + } + + public int GetLineCount() + { + return _textLines.Count; + } + + public int GetLineIndex(uint address) + { + throw new NotImplementedException(); + } + + public int GetNextResult(string searchString, int startPosition, int endPosition, bool searchBackwards) + { + throw new NotImplementedException(); + } + } } diff --git a/UI/Forms/frmMain.Designer.cs b/UI/Forms/frmMain.Designer.cs index a928f23..af11f27 100644 --- a/UI/Forms/frmMain.Designer.cs +++ b/UI/Forms/frmMain.Designer.cs @@ -37,8 +37,8 @@ this.mnuRun100Instructions = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); this.mnuMemoryTools = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); this.mnuMain.SuspendLayout(); this.SuspendLayout(); // @@ -73,7 +73,7 @@ this.mnuOpen.Image = global::Mesen.GUI.Properties.Resources.Folder; this.mnuOpen.Name = "mnuOpen"; this.mnuOpen.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); - this.mnuOpen.Size = new System.Drawing.Size(152, 22); + this.mnuOpen.Size = new System.Drawing.Size(146, 22); this.mnuOpen.Text = "Open"; this.mnuOpen.Click += new System.EventHandler(this.mnuOpen_Click); // @@ -122,19 +122,11 @@ // // mnuDebugger // - this.mnuDebugger.Enabled = false; this.mnuDebugger.Image = global::Mesen.GUI.Properties.Resources.Debugger; this.mnuDebugger.Name = "mnuDebugger"; this.mnuDebugger.Size = new System.Drawing.Size(163, 22); this.mnuDebugger.Text = "Debugger"; - // - // mnuTraceLogger - // - this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow; - this.mnuTraceLogger.Name = "mnuTraceLogger"; - this.mnuTraceLogger.Size = new System.Drawing.Size(163, 22); - this.mnuTraceLogger.Text = "Trace Logger"; - this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); + this.mnuDebugger.Click += new System.EventHandler(this.mnuDebugger_Click); // // mnuMemoryTools // @@ -144,6 +136,14 @@ this.mnuMemoryTools.Text = "Memory Tools"; this.mnuMemoryTools.Click += new System.EventHandler(this.mnuMemoryTools_Click); // + // mnuTraceLogger + // + this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow; + this.mnuTraceLogger.Name = "mnuTraceLogger"; + this.mnuTraceLogger.Size = new System.Drawing.Size(163, 22); + this.mnuTraceLogger.Text = "Trace Logger"; + this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); + // // frmMain // this.AllowDrop = true; diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs index 69e3458..8031ecc 100644 --- a/UI/Forms/frmMain.cs +++ b/UI/Forms/frmMain.cs @@ -54,13 +54,15 @@ namespace Mesen.GUI.Forms { switch(e.NotificationType) { case ConsoleNotificationType.CodeBreak: - this.BeginInvoke((Action)(() => { - DebugWindowManager.OpenDebugWindow(DebugWindow.TraceLogger); - })); break; } } + private void mnuDebugger_Click(object sender, EventArgs e) + { + DebugWindowManager.OpenDebugWindow(DebugWindow.Debugger); + } + private void mnuTraceLogger_Click(object sender, EventArgs e) { DebugWindowManager.OpenDebugWindow(DebugWindow.TraceLogger); diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index d7ae6ed..2e415d5 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -8,6 +8,7 @@ using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Mesen.GUI.Config; +using Mesen.GUI.Debugger; using Mesen.GUI.Forms; namespace Mesen.GUI @@ -25,6 +26,22 @@ namespace Mesen.GUI [DllImport(DllPath)] public static extern void StopTraceLogger(); [DllImport(DllPath)] public static extern void SetTraceOptions(InteropTraceLoggerOptions options); + [DllImport(DllPath, EntryPoint = "GetDisassemblyLineData")] private static extern void GetDisassemblyLineDataWrapper(UInt32 lineIndex, ref InteropCodeLineData lineData); + public static CodeLineData GetDisassemblyLineData(UInt32 lineIndex) + { + InteropCodeLineData data = new InteropCodeLineData(); + data.Comment = new byte[1000]; + data.Text = new byte[1000]; + data.ByteCode = new byte[4]; + + DebugApi.GetDisassemblyLineDataWrapper(lineIndex, ref data); + return new CodeLineData(data); + } + + [DllImport(DllPath)] public static extern UInt32 GetDisassemblyLineCount(); + [DllImport(DllPath)] public static extern UInt32 GetDisassemblyLineIndex(UInt32 cpuAddress); + [DllImport(DllPath)] public static extern int SearchDisassembly([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string searchString, int startPosition, int endPosition, [MarshalAs(UnmanagedType.I1)]bool searchBackwards); + [DllImport(DllPath, EntryPoint = "GetExecutionTrace")] private static extern IntPtr GetExecutionTraceWrapper(UInt32 lineCount); public static string GetExecutionTrace(UInt32 lineCount) { return Utf8Marshaler.PtrToStringUtf8(DebugApi.GetExecutionTraceWrapper(lineCount)); } @@ -62,6 +79,12 @@ namespace Mesen.GUI CGRam, } + public class AddressInfo + { + public Int32 Address; + public SnesMemoryType Type; + } + public struct CpuState { public UInt64 CycleCount; diff --git a/UI/SingleInstance.cs b/UI/SingleInstance.cs index dc43a45..f2518f2 100644 --- a/UI/SingleInstance.cs +++ b/UI/SingleInstance.cs @@ -42,7 +42,7 @@ namespace Mesen.GUI } } catch { } } else { - this._identifier = new Guid("{A46606B7-2D1C-4CC5-A52F-43BCAF094AED}"); + this._identifier = new Guid("{A46606B7-2D1C-4CC5-A52F-43BCAF094AEF}"); this._mutex = new Mutex(true, _identifier.ToString(), out _firstInstance); } } diff --git a/UI/UI.csproj b/UI/UI.csproj index ec58f61..8c9746d 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -279,6 +279,7 @@ Component + Component @@ -288,6 +289,12 @@ ctrlDbgShortcuts.cs + + UserControl + + + ctrlDisassemblyView.cs + UserControl @@ -322,6 +329,12 @@ ctrlScrollableTextbox.cs + + Form + + + frmDebugger.cs + Form @@ -409,6 +422,7 @@ + @@ -436,6 +450,9 @@ ctrlDbgShortcuts.cs + + ctrlDisassemblyView.cs + ctrlHexViewer.cs @@ -448,6 +465,9 @@ ctrlScrollableTextbox.cs + + frmDebugger.cs + frmFadeSpeed.cs diff --git a/UI/Utilities/HexConverter.cs b/UI/Utilities/HexConverter.cs new file mode 100644 index 0000000..7fccdff --- /dev/null +++ b/UI/Utilities/HexConverter.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI +{ + class HexConverter + { + private static byte[] _hexLookup = new byte[256]; + + static HexConverter() + { + _hexLookup['0'] = 0; + _hexLookup['1'] = 1; + _hexLookup['2'] = 2; + _hexLookup['3'] = 3; + _hexLookup['4'] = 4; + _hexLookup['5'] = 5; + _hexLookup['6'] = 6; + _hexLookup['7'] = 7; + _hexLookup['8'] = 8; + _hexLookup['9'] = 9; + _hexLookup['a'] = 10; + _hexLookup['b'] = 11; + _hexLookup['c'] = 12; + _hexLookup['d'] = 13; + _hexLookup['e'] = 14; + _hexLookup['f'] = 15; + _hexLookup['A'] = 10; + _hexLookup['B'] = 11; + _hexLookup['C'] = 12; + _hexLookup['D'] = 13; + _hexLookup['E'] = 14; + _hexLookup['F'] = 15; + } + + public static int FromHex(string hex) + { + int value = 0; + for(int i = 0; i < hex.Length; i++) { + value <<= 4; + value |= _hexLookup[hex[i]]; + } + return value; + } + } +}