diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index b73160d3..b98e20e0 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -576,9 +576,10 @@ void Debugger::GenerateCodeOutput() } } -const char* Debugger::GetCode() +const char* Debugger::GetCode(uint32_t &length) { GenerateCodeOutput(); + length = (uint32_t)_disassemblerOutput.size(); return _disassemblerOutput.c_str(); } @@ -650,12 +651,20 @@ void Debugger::GetCallstack(int32_t* callstackAbsolute, int32_t* callstackRelati callstackRelative[_callstackRelative.size()] = -2; } -void Debugger::GetFunctionEntryPoints(int32_t* entryPoints) +int32_t Debugger::GetFunctionEntryPointCount() +{ + return (uint32_t)_functionEntryPoints.size(); +} + +void Debugger::GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount) { uint32_t i = 0; for(auto itt = _functionEntryPoints.begin(); itt != _functionEntryPoints.end(); itt++) { entryPoints[i] = *itt; i++; + if(i == maxCount - 1) { + break; + } } entryPoints[i] = -1; } @@ -765,11 +774,14 @@ int32_t Debugger::FindSubEntryPoint(uint16_t relativeAddress) int32_t address = relativeAddress; do { GetAbsoluteAddressAndType(address, &info); - if(info.Address < 0 || info.Type != AddressType::PrgRom || !_codeDataLogger->IsCode(info.Address) || _codeDataLogger->IsSubEntryPoint(info.Address)) { + if(info.Address < 0 || info.Type != AddressType::PrgRom || _codeDataLogger->IsData(info.Address)) { break; } address--; + if(_codeDataLogger->IsSubEntryPoint(info.Address)) { + break; + } } while(address >= 0); - return address + 1; + return address > relativeAddress ? relativeAddress : (address + 1); } \ No newline at end of file diff --git a/Core/Debugger.h b/Core/Debugger.h index 6aec6b70..932c9d03 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -119,7 +119,9 @@ public: shared_ptr GetLabelManager(); - void GetFunctionEntryPoints(int32_t* entryPoints); + void GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount); + int32_t GetFunctionEntryPointCount(); + void GetCallstack(int32_t* callstackAbsolute, int32_t* callstackRelative); void GetState(DebugState *state, bool includeMapperInfo = true); @@ -145,7 +147,7 @@ public: bool IsExecutionStopped(); void GenerateCodeOutput(); - const char* GetCode(); + const char* GetCode(uint32_t &length); int32_t GetRelativeAddress(uint32_t addr, AddressType type); int32_t GetAbsoluteAddress(uint32_t addr); diff --git a/Core/Disassembler.cpp b/Core/Disassembler.cpp index 4d8be2c8..0d0ea27e 100644 --- a/Core/Disassembler.cpp +++ b/Core/Disassembler.cpp @@ -253,48 +253,130 @@ vector Disassembler::SplitComment(string input) return result; } -string Disassembler::GetLine(string code, string comment, int32_t cpuAddress, int32_t absoluteAddress, string byteCode, string addressing, bool speculativeCode) +static const char* hexTable[256] = { + "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", + "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", + "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", + "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", + "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", + "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F", + "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F", + "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", + "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F", + "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", + "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", + "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", + "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF", + "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF" +}; + +static string emptyString; +void Disassembler::GetLine(string &out, string code, string comment, int32_t cpuAddress, int32_t absoluteAddress) { - string out; - out.reserve(40); - if(cpuAddress >= 0) { - if(speculativeCode) { - out += "2\x1"; - } else { - out += (_debugger->IsMarkedAsCode(cpuAddress) || absoluteAddress == -1) ? "1\x1" : "0\x1"; - } - out += HexUtilities::ToHex((uint16_t)cpuAddress); - } else { - out += "1\x1"; - } - out += "\x1"; - if(absoluteAddress >= 0) { - out += HexUtilities::ToHex((uint32_t)absoluteAddress); - } - out += "\x1" + byteCode + "\x1" + code + "\x2" + addressing + "\x2"; - if(!comment.empty()) { - out += ";" + comment; - } - out += "\n"; - return out; + GetCodeLine(out, code, comment, cpuAddress, absoluteAddress, emptyString, emptyString, false, false); } -string Disassembler::GetSubHeader(DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector) +void Disassembler::GetCodeLine(string &out, string &code, string &comment, int32_t cpuAddress, int32_t absoluteAddress, string &byteCode, string &addressing, bool speculativeCode, bool isIndented) +{ + char buffer[1000]; + int pos = 0; + char* ptrBuf = buffer; + int* ptrPos = &pos; + + auto writeChar = [=](char c) -> void { + if(*ptrPos < 999) { + ptrBuf[(*ptrPos)++] = c; + } + }; + + auto writeHex = [=](const char* hex) -> void { + if(*ptrPos < 950) { + ptrBuf[(*ptrPos)++] = hex[0]; + ptrBuf[(*ptrPos)++] = hex[1]; + } + }; + + auto writeStr = [=](string &str) -> void { + uint32_t len = (uint32_t)str.size(); + if(*ptrPos + len < 950) { + memcpy(ptrBuf + (*ptrPos), str.c_str(), len); + (*ptrPos) += len; + } else { + len = 950 - *ptrPos; + memcpy(ptrBuf + (*ptrPos), str.c_str(), len); + (*ptrPos) += len; + } + }; + + //Fields: + //Flags | CpuAddress | AbsAddr | ByteCode | Code | Addressing | Comment + if(cpuAddress >= 0) { + if(speculativeCode) { + writeChar(isIndented ? '6' : '2'); + writeChar('\x1'); + } else { + writeChar((_debugger->IsMarkedAsCode(cpuAddress) || absoluteAddress == -1) ? (isIndented ? '5' : '1') : (isIndented ? '4' : '0')); + writeChar('\x1'); + } + writeHex(hexTable[(cpuAddress >> 8) & 0xFF]); + writeHex(hexTable[cpuAddress & 0xFF]); + writeChar('\x1'); + } else { + writeChar('1'); + writeChar('\x1'); + writeChar('\x1'); + } + + if(absoluteAddress >= 0) { + if(absoluteAddress > 0xFFFFFF) { + writeHex(hexTable[(absoluteAddress >> 24) & 0xFF]); + } + if(absoluteAddress > 0xFFFF) { + writeHex(hexTable[(absoluteAddress >> 16) & 0xFF]); + } + writeHex(hexTable[(absoluteAddress >> 8) & 0xFF]); + writeHex(hexTable[absoluteAddress & 0xFF]); + } + + writeChar('\x1'); + writeStr(byteCode); + writeChar('\x1'); + writeStr(code); + writeChar('\x1'); + writeStr(addressing); + writeChar('\x1'); + if(!comment.empty()) { + writeChar(';'); + writeStr(comment); + } + writeChar('\x1'); + ptrBuf[(*ptrPos)++] = 0; + + out.append(buffer, pos - 1); +} + +void Disassembler::GetSubHeader(string &out, DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector) { if(info->IsSubEntryPoint()) { if(label.empty()) { - return GetLine() + GetLine("__sub start__"); + GetLine(out); + GetLine(out, "__sub start__"); } else { - return GetLine() + GetLine("__" + label + "()__"); + GetLine(out); + GetLine(out, "__" + label + "()__"); } } else if(relativeAddr == resetVector) { - return GetLine() + GetLine("--reset--"); + GetLine(out); + GetLine(out, "--reset--"); } else if(relativeAddr == irqVector) { - return GetLine() + GetLine("--irq--"); + GetLine(out); + GetLine(out, "--irq--"); } else if(relativeAddr == nmiVector) { - return GetLine() + GetLine("--nmi--"); + GetLine(out); + GetLine(out, "--nmi--"); } - return ""; } string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memoryAddr, PrgMemoryType memoryType, bool showEffectiveAddresses, bool showOnlyDiassembledCode, State& cpuState, shared_ptr memoryManager, shared_ptr labelManager) @@ -330,51 +412,67 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo uint32_t byteCount = 0; bool skippingCode = false; shared_ptr cdl = _debugger->GetCodeDataLogger(); + string label; + string commentString; + string commentLines; + shared_ptr infoRef; + DisassemblyInfo* info; + bool speculativeCode; + string spaces = " "; + string effAddress; + string code; + string byteCode; while(addr <= endAddr) { - string label = labelManager->GetLabel(memoryAddr, false); - string commentString = labelManager->GetComment(memoryAddr); - string labelLine = label.empty() ? "" : GetLine(label + ":"); - string commentLines = ""; - bool speculativeCode = false; + labelManager->GetLabelAndComment(memoryAddr, label, commentString); + commentLines.clear(); + speculativeCode = false; if(commentString.find_first_of('\n') != string::npos) { for(string &str : SplitComment(commentString)) { - commentLines += GetLine("", str); + GetLine(commentLines, "", str); } - commentString = ""; + commentString.clear(); } - shared_ptr info = (*cache)[addr&mask]; + infoRef = (*cache)[addr&mask]; + + info = infoRef.get(); if(!info && source == _prgRom) { if(_debugger->CheckFlag(DebuggerFlags::DisassembleEverything) || _debugger->CheckFlag(DebuggerFlags::DisassembleEverythingButData) && !cdl->IsData(addr)) { speculativeCode = true; - info = shared_ptr(new DisassemblyInfo(source + addr, false)); + info = new DisassemblyInfo(source + addr, false); } } if(info && addr + info->GetSize() <= endAddr) { if(byteCount > 0) { - output += GetLine(dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr); + GetLine(output, dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr); byteCount = 0; } if(skippingCode) { - output += GetLine(unknownBlockHeader, "", (uint16_t)(memoryAddr - 1), addr - 1); + GetLine(output, unknownBlockHeader, "", (uint16_t)(memoryAddr - 1), addr - 1); skippingCode = false; } - output += GetSubHeader(info.get(), label, memoryAddr, resetVector, nmiVector, irqVector); + GetSubHeader(output, info, label, memoryAddr, resetVector, nmiVector, irqVector); output += commentLines; - output += labelLine; - - char* effectiveAddress = info->GetEffectiveAddressString(cpuState, memoryManager.get(), labelManager.get()); - if(!effectiveAddress) { - effectiveAddress = ""; + if(!label.empty()) { + GetLine(output, label + ":"); } - output += GetLine(" " + string(info->ToString(memoryAddr, memoryManager.get(), labelManager.get())), commentString, memoryAddr, source != _internalRam ? addr : -1, info->GetByteCode(), effectiveAddress, speculativeCode); + + byteCode.clear(); + code.clear(); + effAddress.clear(); + info->GetEffectiveAddressString(effAddress, cpuState, memoryManager.get(), labelManager.get()); + info->ToString(code, memoryAddr, memoryManager.get(), labelManager.get()); + info->GetByteCode(byteCode); + + GetCodeLine(output, code, commentString, memoryAddr, source != _internalRam ? addr : -1, byteCode, effAddress, speculativeCode, true); if(info->IsSubExitPoint()) { - output += GetLine("__sub end__") + GetLine(); + GetLine(output, "__sub end__"); + GetLine(output); } if(speculativeCode) { @@ -394,20 +492,20 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo } } else { if((!label.empty() || !commentString.empty()) && skippingCode) { - output += GetLine(unknownBlockHeader, "", (uint16_t)(memoryAddr - 1), addr - 1); + GetLine(output, unknownBlockHeader, "", (uint16_t)(memoryAddr - 1), addr - 1); skippingCode = false; } if(!skippingCode && showOnlyDiassembledCode) { if(label.empty()) { - output += GetLine("__unknown block__", "", memoryAddr, addr); + GetLine(output, "__unknown block__", "", memoryAddr, addr); if(!commentString.empty()) { - output += GetLine("", commentString); + GetLine(output, "", commentString); } } else { - output += GetLine("__" + label + "__", "", memoryAddr, addr); + GetLine(output, "__" + label + "__", "", memoryAddr, addr); if(!commentString.empty()) { - output += GetLine("", commentString); + GetLine(output, "", commentString); } output += commentLines; } @@ -416,14 +514,16 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo if(!showOnlyDiassembledCode) { if(byteCount >= 8 || ((!label.empty() || !commentString.empty()) && byteCount > 0)) { - output += GetLine(dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr); + GetLine(output, dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr); byteCount = 0; } if(byteCount == 0) { dbBuffer = ".db"; output += commentLines; - output += labelLine; + if(!label.empty()) { + GetLine(output, label + ":"); + } dbRelativeAddr = memoryAddr; dbAbsoluteAddr = addr; @@ -432,7 +532,7 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo dbBuffer += " $" + HexUtilities::ToHex(source[addr&mask]); if(!label.empty() || !commentString.empty()) { - output += GetLine(dbBuffer, commentString, dbRelativeAddr, dbAbsoluteAddr); + GetLine(output, dbBuffer, commentString, dbRelativeAddr, dbAbsoluteAddr); byteCount = 0; } else { byteCount++; @@ -441,14 +541,18 @@ string Disassembler::GetCode(uint32_t startAddr, uint32_t endAddr, uint16_t memo addr++; memoryAddr++; } + + if(speculativeCode) { + delete info; + } } if(byteCount > 0) { - output += GetLine(dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr); + GetLine(output, dbBuffer, "", dbRelativeAddr, dbAbsoluteAddr); } if(skippingCode) { - output += GetLine("----", "", (uint16_t)(memoryAddr - 1), addr - 1); + GetLine(output, "----", "", (uint16_t)(memoryAddr - 1), addr - 1); } return output; diff --git a/Core/Disassembler.h b/Core/Disassembler.h index 11f03ff6..7666bf1f 100644 --- a/Core/Disassembler.h +++ b/Core/Disassembler.h @@ -23,8 +23,9 @@ private: bool IsJump(uint8_t opCode); bool IsUnconditionalJump(uint8_t opCode); vector SplitComment(string input); - string GetLine(string code = "", string comment = "", int32_t cpuAddress = -1, int32_t absoluteAddress = -1, string byteCode = "", string addressing = "", bool speculativeCode = false); - string GetSubHeader(DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector); + void GetLine(string &out, string code = "", string comment = string(), int32_t cpuAddress = -1, int32_t absoluteAddress = -1); + void GetCodeLine(string &out, string &code, string &comment, int32_t cpuAddress, int32_t absoluteAddress, string &byteCode, string &addressing, bool speculativeCode, bool isCode); + void GetSubHeader(string &out, DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector); public: Disassembler(uint8_t* internalRam, uint8_t* prgRom, uint32_t prgSize, uint8_t* prgRam, uint32_t prgRamSize, Debugger* debugger); diff --git a/Core/DisassemblyInfo.cpp b/Core/DisassemblyInfo.cpp index a10f8804..90ac9fac 100644 --- a/Core/DisassemblyInfo.cpp +++ b/Core/DisassemblyInfo.cpp @@ -28,19 +28,16 @@ static const char* hexTable[256] = { "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF" }; -char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager) -{ - uint16_t length; - return ToString(memoryAddr, memoryManager, labelManager, length); -} - -char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t &length) +void DisassemblyInfo::ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager) { + char buffer[500]; uint8_t opCode = *_opPointer; - length = (uint16_t)DisassemblyInfo::OPName[opCode].size(); - memcpy(_toStringBuffer, DisassemblyInfo::OPName[opCode].c_str(), length); + uint16_t length = (uint16_t)DisassemblyInfo::OPName[opCode].size(); + + memcpy(buffer, DisassemblyInfo::OPName[opCode].c_str(), length); uint16_t* ptrPos = &length; + char* ptrBuf = buffer; uint16_t opAddr = GetOpAddr(memoryAddr); @@ -59,19 +56,19 @@ char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManage } auto writeChar = [=](char c) -> void { - _toStringBuffer[(*ptrPos)++] = c; + ptrBuf[(*ptrPos)++] = c; }; auto copyOperand = [=]() -> void { if(labelManager && _opMode != AddrMode::Imm) { string label = labelManager->GetLabel(opAddr, true); if(!label.empty()) { - memcpy(_toStringBuffer + (*ptrPos), label.c_str(), label.size()); + memcpy(ptrBuf + (*ptrPos), label.c_str(), label.size()); (*ptrPos) += (uint16_t)label.size(); return; } } - memcpy(_toStringBuffer + (*ptrPos), opBuffer, operandLength); + memcpy(ptrBuf + (*ptrPos), opBuffer, operandLength); (*ptrPos) += operandLength; }; @@ -79,13 +76,13 @@ char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManage case AddrMode::Acc: writeChar('A'); break; case AddrMode::Imm: writeChar('#'); copyOperand(); break; case AddrMode::Ind: writeChar('('); copyOperand(); writeChar(')'); break; - case AddrMode::IndX: writeChar('('); copyOperand(); memcpy(_toStringBuffer + length, ",X)", 3); length += 3; break; + case AddrMode::IndX: writeChar('('); copyOperand(); memcpy(ptrBuf + length, ",X)", 3); length += 3; break; case AddrMode::IndY: case AddrMode::IndYW: writeChar('('); copyOperand(); - memcpy(_toStringBuffer + length, "),Y", 3); + memcpy(ptrBuf + length, "),Y", 3); length += 3; break; @@ -99,7 +96,7 @@ char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManage case AddrMode::AbsXW: case AddrMode::ZeroX: copyOperand(); - memcpy(_toStringBuffer + length, ",X", 2); + memcpy(ptrBuf + length, ",X", 2); length += 2; break; @@ -107,14 +104,14 @@ char* DisassemblyInfo::ToString(uint32_t memoryAddr, MemoryManager* memoryManage case AddrMode::AbsYW: case AddrMode::ZeroY: copyOperand(); - memcpy(_toStringBuffer + length, ",Y", 2); + memcpy(ptrBuf + length, ",Y", 2); length += 2; break; default: break; } - _toStringBuffer[length] = 0; - return _toStringBuffer; + ptrBuf[length] = 0; + out.append(ptrBuf, length); } uint16_t DisassemblyInfo::GetOpAddr(uint16_t memoryAddr) @@ -149,49 +146,45 @@ void DisassemblyInfo::SetSubEntryPoint() _isSubEntryPoint = true; } -char* DisassemblyInfo::GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager) -{ - uint16_t length; - return GetEffectiveAddressString(cpuState, memoryManager, labelManager, length); -} - -char* DisassemblyInfo::GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t& length) +void DisassemblyInfo::GetEffectiveAddressString(string &out, State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager) { if(_opMode <= AddrMode::Abs) { - length = 0; - return nullptr; + return; } else { int32_t effectiveAddress = GetEffectiveAddress(cpuState, memoryManager); + char buffer[500]; - _effectiveAddressBuffer[0] = ' '; - _effectiveAddressBuffer[1] = '@'; - _effectiveAddressBuffer[2] = ' '; + int length = 0; + buffer[0] = ' '; + buffer[1] = '@'; + buffer[2] = ' '; if(labelManager) { string label = labelManager->GetLabel(effectiveAddress, true); if(!label.empty()) { - memcpy(_effectiveAddressBuffer + 3, label.c_str(), label.size()); + memcpy(buffer + 3, label.c_str(), label.size()); length = (uint16_t)label.size() + 3; - _effectiveAddressBuffer[length] = 0; + buffer[length] = 0; - return _effectiveAddressBuffer; + out.append(buffer, length); + return; } } - _effectiveAddressBuffer[3] = '$'; + buffer[3] = '$'; if(_opMode == AddrMode::ZeroX || _opMode == AddrMode::ZeroY) { - memcpy(_effectiveAddressBuffer + 4, hexTable[effectiveAddress], 2); - _effectiveAddressBuffer[6] = 0; + memcpy(buffer + 4, hexTable[effectiveAddress], 2); + buffer[6] = 0; length = 6; } else { - memcpy(_effectiveAddressBuffer + 4, hexTable[effectiveAddress >> 8], 2); - memcpy(_effectiveAddressBuffer + 6, hexTable[effectiveAddress & 0xFF], 2); - _effectiveAddressBuffer[8] = 0; + memcpy(buffer + 4, hexTable[effectiveAddress >> 8], 2); + memcpy(buffer + 6, hexTable[effectiveAddress & 0xFF], 2); + buffer[8] = 0; length = 8; } - - return _effectiveAddressBuffer; + + out.append(buffer, length); } } @@ -232,19 +225,24 @@ int32_t DisassemblyInfo::GetEffectiveAddress(State& cpuState, MemoryManager* mem return -1; } -string DisassemblyInfo::GetByteCode() +void DisassemblyInfo::GetByteCode(string &out) { //Raw byte code - string byteCode; - byteCode.reserve(12); + char byteCode[12]; + int pos = 1; + byteCode[0] = '$'; for(uint32_t i = 0; i < _opSize; i++) { - if(!byteCode.empty()) { - byteCode += " "; + if(i != 0) { + byteCode[pos++] = ' '; + byteCode[pos++] = '$'; } - byteCode += "$" + HexUtilities::ToHex((uint8_t)*(_opPointer + i)); + + byteCode[pos++] = hexTable[(uint8_t)*(_opPointer + i)][0]; + byteCode[pos++] = hexTable[(uint8_t)*(_opPointer + i)][1]; } - return byteCode; + byteCode[pos] = 0; + out.append(byteCode, pos); } uint32_t DisassemblyInfo::GetSize() diff --git a/Core/DisassemblyInfo.h b/Core/DisassemblyInfo.h index 6d032b92..d15e1c15 100644 --- a/Core/DisassemblyInfo.h +++ b/Core/DisassemblyInfo.h @@ -19,10 +19,7 @@ private: bool _isSubExitPoint = false; uint32_t _opSize = 0; AddrMode _opMode; - - char _toStringBuffer[1000]; - char _effectiveAddressBuffer[1000]; - + public: DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint); @@ -30,12 +27,9 @@ public: int32_t GetEffectiveAddress(State& cpuState, MemoryManager* memoryManager); - char* GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager); - char* GetEffectiveAddressString(State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t &length); - - char* ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager); - char* ToString(uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, uint16_t &length); - string GetByteCode(); + void GetEffectiveAddressString(string &out, State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager); + void ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager); + void GetByteCode(string &out); uint32_t GetSize(); uint16_t GetOpAddr(uint16_t memoryAddr); diff --git a/Core/LabelManager.cpp b/Core/LabelManager.cpp index 8b2eedfe..003fffa2 100644 --- a/Core/LabelManager.cpp +++ b/Core/LabelManager.cpp @@ -98,6 +98,27 @@ string LabelManager::GetComment(uint16_t relativeAddr) return ""; } +void LabelManager::GetLabelAndComment(uint16_t relativeAddr, string &label, string &comment) +{ + int32_t labelAddr = GetLabelAddress(relativeAddr, false); + + if(labelAddr >= 0) { + auto result = _codeLabels.find(labelAddr); + if(result != _codeLabels.end()) { + label = result->second; + } else { + label.clear(); + } + + auto commentResult = _codeComments.find(labelAddr); + if(commentResult != _codeComments.end()) { + comment = commentResult->second; + } else { + comment.clear(); + } + } +} + int32_t LabelManager::GetLabelRelativeAddress(string label) { auto result = _codeLabelReverseLookup.find(label); diff --git a/Core/LabelManager.h b/Core/LabelManager.h index e9d914b1..1c70301b 100644 --- a/Core/LabelManager.h +++ b/Core/LabelManager.h @@ -1,6 +1,7 @@ #pragma once #include "stdafx.h" #include +#include using std::unordered_map; class BaseMapper; @@ -9,9 +10,9 @@ enum class AddressType; class LabelManager { private: - unordered_map _codeLabels; + unordered_map> _codeLabels = unordered_map>(10000, [](const uint32_t & addr) { return addr; }); + unordered_map> _codeComments = unordered_map>(10000, [](const uint32_t & addr) { return addr; }); unordered_map _codeLabelReverseLookup; - unordered_map _codeComments; shared_ptr _mapper; @@ -26,4 +27,5 @@ public: string GetLabel(uint16_t relativeAddr, bool checkRegisters); string GetComment(uint16_t relativeAddr); + void GetLabelAndComment(uint16_t relativeAddr, string &label, string &comment); }; diff --git a/Core/TraceLogger.cpp b/Core/TraceLogger.cpp index 1292f7df..3ac4f458 100644 --- a/Core/TraceLogger.cpp +++ b/Core/TraceLogger.cpp @@ -84,7 +84,9 @@ void TraceLogger::GetTraceRow(string &output, State &cpuState, PPUDebugState &pp output += HexUtilities::ToHex(cpuState.DebugPC) + " "; if(_options.ShowByteCode) { - output += disassemblyInfo->GetByteCode() + std::string(13 - disassemblyInfo->GetByteCode().size(), ' '); + string byteCode; + disassemblyInfo->GetByteCode(byteCode); + output += byteCode + std::string(13 - byteCode.size(), ' '); } int indentLevel = 0; @@ -93,17 +95,12 @@ void TraceLogger::GetTraceRow(string &output, State &cpuState, PPUDebugState &pp output += std::string(indentLevel, ' '); } - uint16_t disassemblyLength = 0; - uint16_t effectiveAddressLength = 0; + string code; LabelManager* labelManager = _options.UseLabels ? _labelManager.get() : nullptr; - char* disassembly = disassemblyInfo->ToString(cpuState.DebugPC, _memoryManager.get(), labelManager, disassemblyLength); - char* effectiveAddress = (_options.ShowEffectiveAddresses ? disassemblyInfo->GetEffectiveAddressString(cpuState, _memoryManager.get(), labelManager, effectiveAddressLength) : nullptr); - - output += disassembly; - if(effectiveAddress) { - output += effectiveAddress; - } - output += std::string(32 - disassemblyLength - effectiveAddressLength, ' '); + disassemblyInfo->ToString(code, cpuState.DebugPC, _memoryManager.get(), labelManager); + disassemblyInfo->GetEffectiveAddressString(code, cpuState, _memoryManager.get(), labelManager); + code += std::string(32 - code.size(), ' '); + output += code; if(_options.ShowRegisters) { output += " A:" + HexUtilities::ToHex(cpuState.A) + diff --git a/GUI.NET/Debugger/Controls/ctrlConsoleStatus.cs b/GUI.NET/Debugger/Controls/ctrlConsoleStatus.cs index 1d13d706..034b03cc 100644 --- a/GUI.NET/Debugger/Controls/ctrlConsoleStatus.cs +++ b/GUI.NET/Debugger/Controls/ctrlConsoleStatus.cs @@ -97,10 +97,14 @@ namespace Mesen.GUI.Debugger private void UpdateStack(UInt16 stackPointer) { + lstStack.BeginUpdate(); lstStack.Items.Clear(); - for(UInt32 i = (UInt32)0x100 + stackPointer; i < 0x200; i++) { - lstStack.Items.Add("$" + InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, i).ToString("X2")); + ListViewItem[] itemsToAdd = new ListViewItem[256 - stackPointer]; + for(UInt32 i = (UInt32)0x100 + stackPointer; i < 0x200; i++) { + itemsToAdd[i-stackPointer-0x100] = new ListViewItem("$" + InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, i).ToString("X2")); } + lstStack.Items.AddRange(itemsToAdd); + lstStack.EndUpdate(); } public void UpdateStatus(ref DebugState state) diff --git a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs index 88892923..4cc82012 100644 --- a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs +++ b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs @@ -21,10 +21,23 @@ namespace Mesen.GUI.Debugger public event AssemblerEventHandler OnEditCode; public event AddressEventHandler OnSetNextStatement; private DebugViewInfo _config; + + List _lineNumbers = new List(10000); + List _lineNumberNotes = new List(10000); + List _codeNotes = new List(10000); + List _codeLines = new List(10000); private HashSet _unexecutedAddresses = new HashSet(); private HashSet _speculativeCodeAddreses = new HashSet(); + Dictionary _codeContent = new Dictionary(10000); + Dictionary _codeComments = new Dictionary(10000); + Dictionary _codeByteCode = new Dictionary(10000); + List _addressing = new List(10000); + List _comments = new List(10000); + List _lineIndentations = new List(10000); + private Color _unexecutedColor = Color.FromArgb(183, 229, 190); private Color _speculativeColor = Color.FromArgb(240, 220, 220); + private UInt32? _currentActiveAddress { get; set; } = null; private frmCodeTooltip _codeTooltip = null; @@ -124,7 +137,6 @@ namespace Mesen.GUI.Debugger } } - private UInt32? _currentActiveAddress = null; public void SelectActiveAddress(UInt32 address) { this.SetActiveAddress(address); @@ -144,30 +156,10 @@ namespace Mesen.GUI.Debugger public void UpdateLineColors() { this.ctrlCodeViewer.BeginUpdate(); - - this.ctrlCodeViewer.ClearLineStyles(); - - if(_currentActiveAddress.HasValue) { - this.ctrlCodeViewer.SetLineColor((int)_currentActiveAddress, Color.Black, Color.Yellow, null, LineSymbol.Arrow); - } - - if(ConfigManager.Config.DebugInfo.HighlightUnexecutedCode) { - foreach(int relativeAddress in _unexecutedAddresses) { - this.ctrlCodeViewer.SetLineColor(relativeAddress, null, _unexecutedColor); - } - } - - foreach(int relativeAddress in _speculativeCodeAddreses) { - this.ctrlCodeViewer.SetLineColor(relativeAddress, null, _speculativeColor); - } - - this.HighlightBreakpoints(); - + this.ctrlCodeViewer.StyleProvider = new LineStyleProvider(this); this.ctrlCodeViewer.EndUpdate(); } - Dictionary _codeContent = new Dictionary(); - Dictionary _codeByteContentLength = new Dictionary(); public List GetCode(out int byteLength, ref int startAddress, int endAddress = -1) { List result = new List(); @@ -184,7 +176,10 @@ namespace Mesen.GUI.Debugger //TODO: Invalidate disassembly info cache / CDL file (not needed?) //TODO: Support .data syntax in assembler for(int i = startAddress; (i <= endAddress || endAddress == -1) && endAddress < 65536; ) { - if(_codeContent.TryGetValue(i, out string code)) { + string code; + if(_codeContent.TryGetValue(i, out code)) { + code = code.Split('\x2')[0].Trim(); + if(code.StartsWith("--") || code.StartsWith("__") || code.StartsWith("[[")) { //Stop adding code when we find a new section (new function, data blocks, etc.) break; @@ -196,6 +191,16 @@ namespace Mesen.GUI.Debugger string comment = codeLabel?.Comment; string label = codeLabel?.Label; + if(code == "STP*" || code == "NOP*") { + //Transform unofficial opcodes that can't be reassembled properly into .byte statements + if(comment != null) { + comment.Insert(1, code + " - "); + } else { + comment = code; + } + code = ".byte " + string.Join(",", _codeByteCode[i].Split(' ').Select((hexByte) => "$" + hexByte)); + } + if(!string.IsNullOrWhiteSpace(comment) && comment.Contains("\n")) { result.AddRange(comment.Replace("\r", "").Split('\n').Select(cmt => ";" + cmt)); comment = null; @@ -205,7 +210,7 @@ namespace Mesen.GUI.Debugger } result.Add(" " + code + (!string.IsNullOrWhiteSpace(comment) ? (" ;" + comment) : "")); - int length = _codeByteContentLength[i]; + int length = _codeByteCode[i].Count(c => c == ' ') + 1; byteLength += length; i += length; @@ -223,46 +228,84 @@ namespace Mesen.GUI.Debugger public bool UpdateCode(bool forceUpdate = false) { if(_codeChanged || forceUpdate) { - List lineNumbers = new List(); - List lineNumberNotes = new List(); - List codeNotes = new List(); - List codeLines = new List(); - _codeContent = new Dictionary(); - _codeByteContentLength = new Dictionary(); - _unexecutedAddresses = new HashSet(); - _speculativeCodeAddreses = new HashSet(); - - int index = -1; - int previousIndex = -1; - while((index = _code.IndexOf('\n', index + 1)) >= 0) { - string line = _code.Substring(previousIndex + 1, index - previousIndex - 1); - string[] lineParts = line.Split('\x1'); + _codeContent.Clear(); + _codeComments.Clear(); + _codeByteCode.Clear(); + _unexecutedAddresses.Clear(); + _speculativeCodeAddreses.Clear(); - if(lineParts.Length >= 5) { - int relativeAddress = ParseHexAddress(lineParts[1]); + string[] token = new string[7]; + int tokenIndex = 0; + int startPos = 0; + int endPos = 0; - if(lineParts[0] == "0" && lineParts[4].StartsWith(" ")) { - _unexecutedAddresses.Add(relativeAddress); - } else if(lineParts[0] == "2" && lineParts[4].StartsWith(" ")) { - _speculativeCodeAddreses.Add(relativeAddress); - } + Action readToken = () => { + endPos = _code.IndexOf('\x1', endPos) + 1; + token[tokenIndex++] = _code.Substring(startPos, endPos - startPos - 1); + startPos = endPos; + }; + + Action readLine = () => { + tokenIndex = 0; + readToken(); readToken(); readToken(); readToken(); readToken(); readToken(); readToken(); + }; - lineNumbers.Add(relativeAddress); - lineNumberNotes.Add(string.IsNullOrWhiteSpace(lineParts[2]) ? "" : lineParts[2].TrimStart('0').PadLeft(4, '0')); - codeNotes.Add(lineParts[3]); - codeLines.Add(lineParts[4]); - _codeByteContentLength[relativeAddress] = lineParts[3].Count(c => c == ' ') + 1; + Func processLine = () => { + readLine(); - _codeContent[relativeAddress] = lineParts[4].Split('\x2')[0].Trim(); + int relativeAddress = ParseHexAddress(token[1]); + + //Flags: + //1: Executed code + //2: Speculative Code + //4: Indented line + if(token[0] == "4") { + _unexecutedAddresses.Add(relativeAddress); + _lineIndentations.Add(20); + } else if(token[0] == "6") { + _speculativeCodeAddreses.Add(relativeAddress); + _lineIndentations.Add(20); + } else if(token[0] == "5") { + _lineIndentations.Add(20); + } else { + _lineIndentations.Add(0); } - previousIndex = index; - } + _lineNumbers.Add(relativeAddress); + _lineNumberNotes.Add(string.IsNullOrWhiteSpace(token[2]) ? "" : (token[2].Length > 5 ? token[2].TrimStart('0').PadLeft(4, '0') : token[2])); + _codeNotes.Add(token[3]); + _codeLines.Add(token[4]); - ctrlCodeViewer.TextLines = codeLines.ToArray(); - ctrlCodeViewer.LineNumbers = lineNumbers.ToArray(); - ctrlCodeViewer.TextLineNotes = codeNotes.ToArray(); - ctrlCodeViewer.LineNumberNotes = lineNumberNotes.ToArray(); + _addressing.Add(token[5]); + _comments.Add(token[6]); + + //Used by assembler + _codeByteCode[relativeAddress] = token[3]; + _codeContent[relativeAddress] = token[4]; + _codeComments[relativeAddress] = token[6]; + + return endPos < _code.Length; + }; + + while(processLine()); + + ctrlCodeViewer.LineIndentations = _lineIndentations.ToArray(); + ctrlCodeViewer.Addressing = _addressing.ToArray(); + ctrlCodeViewer.Comments = _comments.ToArray(); + + ctrlCodeViewer.TextLines = _codeLines.ToArray(); + ctrlCodeViewer.LineNumbers = _lineNumbers.ToArray(); + ctrlCodeViewer.TextLineNotes = _codeNotes.ToArray(); + ctrlCodeViewer.LineNumberNotes = _lineNumberNotes.ToArray(); + + //These are all temporary and can be cleared right away + _lineNumbers.Clear(); + _lineNumberNotes.Clear(); + _codeNotes.Clear(); + _codeLines.Clear(); + _addressing.Clear(); + _comments.Clear(); + _lineIndentations.Clear(); _codeChanged = false; UpdateLineColors(); @@ -281,37 +324,6 @@ namespace Mesen.GUI.Debugger } } - private void HighlightBreakpoints() - { - foreach(Breakpoint breakpoint in BreakpointManager.Breakpoints) { - Color? fgColor = Color.White; - Color? bgColor = null; - Color? outlineColor = Color.FromArgb(140, 40, 40); - LineSymbol symbol; - if(breakpoint.Enabled) { - bgColor = Color.FromArgb(140, 40, 40); - symbol = LineSymbol.Circle; - } else { - fgColor = Color.Black; - symbol = LineSymbol.CircleOutline; - } - - if(breakpoint.Address == (UInt32)(_currentActiveAddress.HasValue ? (int)_currentActiveAddress.Value : -1)) { - fgColor = Color.Black; - bgColor = Color.Yellow; - symbol |= LineSymbol.Arrow; - } else if(_unexecutedAddresses.Contains((Int32)breakpoint.Address)) { - fgColor = Color.Black; - bgColor = _unexecutedColor; - } else if(_speculativeCodeAddreses.Contains((Int32)breakpoint.Address)) { - fgColor = Color.Black; - bgColor = _speculativeColor; - } - - ctrlCodeViewer.SetLineColor((int)breakpoint.Address, fgColor, bgColor, outlineColor, symbol); - } - } - #region Events private Point _previousLocation; private bool _preventCloseTooltip = false; @@ -766,6 +778,58 @@ namespace Mesen.GUI.Debugger this.OnEditCode?.Invoke(new AssemblerEventArgs() { Code = string.Join(Environment.NewLine, code), StartAddress = (UInt16)startAddress, BlockLength = (UInt16)byteLength }); } } + + + class LineStyleProvider : ctrlTextbox.ILineStyleProvider + { + ctrlDebuggerCode _code; + public LineStyleProvider(ctrlDebuggerCode code) + { + _code = code; + } + + public LineProperties GetLineStyle(int cpuAddress) + { + foreach(Breakpoint breakpoint in BreakpointManager.Breakpoints) { + if(!breakpoint.IsAbsoluteAddress && breakpoint.Address == cpuAddress) { + Color? fgColor = Color.White; + Color? bgColor = null; + Color? outlineColor = Color.FromArgb(140, 40, 40); + LineSymbol symbol; + if(breakpoint.Enabled) { + bgColor = Color.FromArgb(140, 40, 40); + symbol = LineSymbol.Circle; + } else { + fgColor = Color.Black; + symbol = LineSymbol.CircleOutline; + } + + if(breakpoint.Address == (UInt32)(_code._currentActiveAddress.HasValue ? (int)_code._currentActiveAddress.Value : -1)) { + fgColor = Color.Black; + bgColor = Color.Yellow; + symbol |= LineSymbol.Arrow; + } else if(_code._unexecutedAddresses.Contains((Int32)breakpoint.Address)) { + fgColor = Color.Black; + bgColor = _code._unexecutedColor; + } else if(_code._speculativeCodeAddreses.Contains((Int32)breakpoint.Address)) { + fgColor = Color.Black; + bgColor = _code._speculativeColor; + } + + return new LineProperties() { FgColor = fgColor, BgColor = bgColor, OutlineColor = outlineColor, Symbol = symbol }; + } + } + + if(_code._currentActiveAddress.HasValue && cpuAddress == _code._currentActiveAddress) { + return new LineProperties() { FgColor = Color.Black, BgColor = Color.Yellow, OutlineColor = null, Symbol = LineSymbol.Arrow }; + } else if(ConfigManager.Config.DebugInfo.HighlightUnexecutedCode && _code._unexecutedAddresses.Contains(cpuAddress)) { + return new LineProperties() { FgColor = null, BgColor = _code._unexecutedColor, OutlineColor = null, Symbol = LineSymbol.None }; + } else if(_code._speculativeCodeAddreses.Contains(cpuAddress)) { + return new LineProperties() { FgColor = null, BgColor = _code._speculativeColor, OutlineColor = null, Symbol = LineSymbol.None }; + } + return null; + } + } } public class WatchEventArgs : EventArgs diff --git a/GUI.NET/Debugger/Controls/ctrlFunctionList.cs b/GUI.NET/Debugger/Controls/ctrlFunctionList.cs index 624f6cf1..3e3b13c4 100644 --- a/GUI.NET/Debugger/Controls/ctrlFunctionList.cs +++ b/GUI.NET/Debugger/Controls/ctrlFunctionList.cs @@ -59,7 +59,7 @@ namespace Mesen.GUI.Debugger.Controls Int32[] entryPoints = InteropEmu.DebugGetFunctionEntryPoints(); bool updating = false; - for(int i = 0; entryPoints[i] >= 0; i++) { + for(int i = 0; entryPoints[i] >= 0 && i < entryPoints.Length; i++) { Int32 entryPoint = entryPoints[i]; ListViewItem item; if(!_functions.TryGetValue(entryPoint, out item)) { diff --git a/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.cs b/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.cs index f7f7e405..2e28e86b 100644 --- a/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.cs +++ b/GUI.NET/Debugger/Controls/ctrlMemoryAccessCounters.cs @@ -32,8 +32,6 @@ namespace Mesen.GUI.Debugger.Controls public void RefreshData() { - ctrlScrollableTextbox.ClearLineStyles(); - int[] readCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Read, false); int[] writeCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Write, false); int[] execCounts = InteropEmu.DebugGetMemoryAccessCounts(_memoryType, MemoryOperationType.Exec, false); @@ -74,16 +72,31 @@ namespace Mesen.GUI.Debugger.Controls content[i] = data[i].Content; } + if(chkHighlightUninitRead.Checked) { + ctrlScrollableTextbox.StyleProvider = new LineStyleProvider(new HashSet(data.Where((e) => e.UninitRead).Select((e) => e.Address))); + } else { + ctrlScrollableTextbox.StyleProvider = null; + } ctrlScrollableTextbox.Header = "Read".PadRight(12) + "Write".PadRight(12) + "Execute".PadRight(12); ctrlScrollableTextbox.LineNumbers = addresses; ctrlScrollableTextbox.TextLines = content; + } - if(chkHighlightUninitRead.Checked) { - ctrlScrollableTextbox.BeginUpdate(); - foreach(int address in data.Where((e) => e.UninitRead).Select((e) => e.Address)) { - ctrlScrollableTextbox.SetLineColor(address, null, Color.LightCoral); + private class LineStyleProvider : ctrlTextbox.ILineStyleProvider + { + HashSet _addresses = new HashSet(); + + public LineStyleProvider(HashSet addresses) + { + _addresses = addresses; + } + + public LineProperties GetLineStyle(int cpuAddress) + { + if(_addresses.Contains(cpuAddress)) { + return new LineProperties() { BgColor = Color.LightCoral }; } - ctrlScrollableTextbox.EndUpdate(); + return null; } } diff --git a/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs b/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs index 13f718d3..6577ed9b 100644 --- a/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs +++ b/GUI.NET/Debugger/Controls/ctrlScrollableTextbox.cs @@ -105,11 +105,6 @@ namespace Mesen.GUI.Debugger this.hScrollBar.Maximum = newMax; } - public void ClearLineStyles() - { - this.ctrlTextbox.ClearLineStyles(); - } - public void BeginUpdate() { this.ctrlTextbox.BeginUpdate(); @@ -120,10 +115,7 @@ namespace Mesen.GUI.Debugger this.ctrlTextbox.EndUpdate(); } - public void SetLineColor(int lineNumber, Color? fgColor = null, Color? bgColor = null, Color? outlineColor = null, LineSymbol symbol = LineSymbol.None) - { - this.ctrlTextbox.SetLineColor(lineNumber, fgColor, bgColor, outlineColor, symbol); - } + public ctrlTextbox.ILineStyleProvider StyleProvider { set { this.ctrlTextbox.StyleProvider = value; } } public int GetLineIndex(int lineNumber) { @@ -267,6 +259,10 @@ namespace Mesen.GUI.Debugger 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 diff --git a/GUI.NET/Debugger/Controls/ctrlTextbox.cs b/GUI.NET/Debugger/Controls/ctrlTextbox.cs index e71916c1..1fd52536 100644 --- a/GUI.NET/Debugger/Controls/ctrlTextbox.cs +++ b/GUI.NET/Debugger/Controls/ctrlTextbox.cs @@ -79,6 +79,10 @@ namespace Mesen.GUI.Debugger this.DoubleBuffered = true; } + public string[] Comments; + public string[] Addressing; + public int[] LineIndentations; + public string[] TextLines { set @@ -86,18 +90,17 @@ namespace Mesen.GUI.Debugger int maxLength = 0; _maxLineWidthIndex = 0; - - _contents = new string[value.Length]; - _lineMargins = new int[value.Length]; + + _contents = value; for(int i = 0, len = value.Length; i < len; i++) { - _contents[i] = value[i].TrimStart(); - - string stringToMeasure = GetFullWidthString(_contents[i]); - if(stringToMeasure.Length > maxLength) { - maxLength = stringToMeasure.Length; + 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; } - _lineMargins[i] = (value[i].Length - _contents[i].Length) * 10; } UpdateHorizontalScrollWidth(); @@ -243,9 +246,8 @@ namespace Mesen.GUI.Debugger } for(int i = 0, len = _contents.Length; i < len; i++) { - if(Regex.IsMatch(_contents[i], regex, matchCase ? RegexOptions.None : RegexOptions.IgnoreCase)) { - string line = _contents[i].Replace("\x2", "\t").Trim(); - + string line = _contents[i] + Addressing?[i] + (Comments != null ? ("\t" + Comments[i]) : null); + if(Regex.IsMatch(line, regex, matchCase ? RegexOptions.None : RegexOptions.IgnoreCase)) { if(line.StartsWith("__") && line.EndsWith("__") || line.StartsWith("[[") && line.EndsWith("]]")) { line = "Block: " + line.Substring(2, line.Length - 4); } @@ -335,22 +337,20 @@ namespace Mesen.GUI.Debugger this.Invalidate(); } - public void SetLineColor(int lineNumber, Color? fgColor = null, Color? bgColor = null, Color? outlineColor = null, LineSymbol symbol = LineSymbol.None) + public interface ILineStyleProvider { - if(lineNumber != -1) { - if(_lineNumberIndex.ContainsKey(lineNumber)) { - LineProperties properties = new LineProperties() { - BgColor = bgColor, - FgColor = fgColor, - OutlineColor = outlineColor, - Symbol = symbol - }; + LineProperties GetLineStyle(int cpuAddress); + } - _lineProperties[_lineNumberIndex[lineNumber]] = properties; - if(!_updating) { - this.Invalidate(); - } - } + + public ILineStyleProvider StyleProvider { get; set; } + + public LineProperties GetLineStyle(int lineNumber) + { + if(StyleProvider != null && _lineNumbers[lineNumber] >= 0) { + return StyleProvider.GetLineStyle(_lineNumbers[lineNumber]); + } else { + return null; } } @@ -419,7 +419,7 @@ namespace Mesen.GUI.Debugger private int GetMargin(Graphics g, bool getExtendedMargin) { int marginWidth = getExtendedMargin && this.ShowContentNotes && this.ShowSingleContentLineNotes ? _marginWidth + _extendedMarginWidth : _marginWidth; - return this.ShowLineNumbers ? (int)(g.MeasureString("".PadLeft(marginWidth, 'W'), this.Font).Width) : 0; + return (this.ShowLineNumbers ? (int)(g.MeasureString("".PadLeft(marginWidth, 'W'), this.Font).Width) : 0) - 1; } public int GetLineIndexAtPosition(int yPos) @@ -430,14 +430,12 @@ namespace Mesen.GUI.Debugger return lineIndex; } - private string GetFullWidthString(string lineContent) + private string GetFullWidthString(int lineIndex) { - string text = lineContent.Replace("\x2", ""); - int commentIndex = text.IndexOf(";"); - if(commentIndex > 0 && commentIndex < CommentSpacingCharCount) { - text = text.Insert(commentIndex, "".PadLeft(CommentSpacingCharCount - commentIndex)); + string text = _contents[lineIndex] + Addressing?[lineIndex]; + if(Comments?[lineIndex].Length > 0) { + return text.PadRight(text.Length > 0 ? CommentSpacingCharCount : 0) + Comments[lineIndex]; } - return text; } @@ -453,9 +451,9 @@ namespace Mesen.GUI.Debugger } if(positionX >= 0 && lineIndex < _contents.Length) { - string text = this.GetFullWidthString(_contents[lineIndex]); + string text = this.GetFullWidthString(lineIndex); //Adjust background color highlights based on number of spaces in front of content - positionX -= _lineMargins[lineIndex]; + positionX -= (LineIndentations != null ? LineIndentations[lineIndex] : 0); int previousWidth = 0; for(int i = 0, len = text.Length; i < len; i++) { @@ -476,7 +474,7 @@ namespace Mesen.GUI.Debugger int charIndex; int lineIndex; if(this.GetCharIndex(position, out charIndex, out lineIndex)) { - string text = (useCompareText && _compareContents != null) ? _compareContents[lineIndex] : this.GetFullWidthString(_contents[lineIndex]); + string text = (useCompareText && _compareContents != null) ? _compareContents[lineIndex] : this.GetFullWidthString(lineIndex); List wordDelimiters = new List(new char[] { ' ', ',', '|', ';', '(', ')', '.', '-', ':' }); if(wordDelimiters.Contains(text[charIndex])) { return string.Empty; @@ -656,7 +654,7 @@ namespace Mesen.GUI.Debugger { if(_contents.Length > _maxLineWidthIndex) { using(Graphics g = this.CreateGraphics()) { - _maxLineWidth = _lineMargins[_maxLineWidthIndex] + g.MeasureString(GetFullWidthString(_contents[_maxLineWidthIndex]), this.Font).Width; + _maxLineWidth = (LineIndentations != null ? LineIndentations[_maxLineWidthIndex] : 0) + g.MeasureString(GetFullWidthString(_maxLineWidthIndex), this.Font).Width; HorizontalScrollWidth = (int)(Math.Max(0, HorizontalScrollFactor + _maxLineWidth - (this.Width - GetMargin(g, true))) / HorizontalScrollFactor); } } @@ -738,10 +736,9 @@ namespace Mesen.GUI.Debugger private void DrawLine(Graphics g, int currentLine, int marginLeft, int positionY, int lineHeight) { - string[] lineContent = _contents[currentLine].Split('\x2'); - string codeString = lineContent[0].TrimStart(); - string addressString = lineContent.Length > 1 ? lineContent[1] : ""; - string commentString = lineContent.Length > 2 ? lineContent[2] : ""; + string codeString = _contents[currentLine]; + string addressString = this.Addressing?[currentLine]; + string commentString = this.Comments?[currentLine]; float codeStringLength = g.MeasureString(codeString, this.Font).Width; float addressStringLength = g.MeasureString(addressString, this.Font).Width; @@ -758,12 +755,12 @@ namespace Mesen.GUI.Debugger } //Adjust background color highlights based on number of spaces in front of content - marginLeft += _lineMargins[currentLine]; + marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0); Color textColor = Color.Black; - if(_lineProperties.ContainsKey(currentLine)) { + LineProperties lineProperties = GetLineStyle(currentLine); + if(lineProperties != null) { //Process background, foreground, outline color and line symbol - LineProperties lineProperties = _lineProperties[currentLine]; textColor = lineProperties.FgColor ?? Color.Black; if(lineProperties.BgColor.HasValue) { @@ -963,10 +960,11 @@ namespace Mesen.GUI.Debugger } //Adjust background color highlights based on number of spaces in front of content - marginLeft += _lineMargins[currentLine]; + marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0); - if(_lineProperties.ContainsKey(currentLine)) { - this.DrawLineSymbols(g, positionY, _lineProperties[currentLine], lineHeight); + LineProperties lineProperties = GetLineStyle(currentLine); + if(lineProperties != null) { + this.DrawLineSymbols(g, positionY, lineProperties, lineHeight); } } diff --git a/GUI.NET/Debugger/frmEditLabel.cs b/GUI.NET/Debugger/frmEditLabel.cs index 01ee1211..06b48dce 100644 --- a/GUI.NET/Debugger/frmEditLabel.cs +++ b/GUI.NET/Debugger/frmEditLabel.cs @@ -47,7 +47,7 @@ namespace Mesen.GUI.Debugger (sameLabel == null || sameLabel == _originalLabel) && (sameAddress == null || sameAddress == _originalLabel) && (_originalLabel != null || txtLabel.Text.Length > 0 || txtComment.Text.Length > 0) - && !txtComment.Text.Contains('\x1') && !txtComment.Text.Contains('\x2') + && !txtComment.Text.Contains('\x1') && (txtLabel.Text.Length == 0 || Regex.IsMatch(txtLabel.Text, "^[@_a-zA-Z]+[@_a-zA-Z0-9]*$")); } diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index 79ff288f..89ab0890 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -544,6 +544,8 @@ namespace Mesen.GUI.Forms InteropEmu.SetFlag(EmulationFlags.InBackground, !hasFocus); } + Image _pauseButton = Mesen.GUI.Properties.Resources.Pause; + Image _playButton = Mesen.GUI.Properties.Resources.Play; private void UpdateMenus() { try { @@ -570,7 +572,7 @@ namespace Mesen.GUI.Forms mnuPause.Enabled &= !InteropEmu.DebugIsDebuggerRunning(); mnuPause.Text = InteropEmu.IsPaused() ? ResourceHelper.GetMessage("Resume") : ResourceHelper.GetMessage("Pause"); - mnuPause.Image = InteropEmu.IsPaused() ? Mesen.GUI.Properties.Resources.Play : Mesen.GUI.Properties.Resources.Pause; + mnuPause.Image = InteropEmu.IsPaused() ? _playButton : _pauseButton; bool netPlay = InteropEmu.IsServerRunning() || isNetPlayClient; diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 0331c42d..4b1a0317 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -211,8 +211,12 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void DebugSaveRomToDisk([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filename); - [DllImport(DLLPath, EntryPoint = "DebugGetCode")] private static extern IntPtr DebugGetCodeWrapper(); - public static string DebugGetCode() { return PtrToStringUtf8(InteropEmu.DebugGetCodeWrapper()); } + [DllImport(DLLPath, EntryPoint = "DebugGetCode")] private static extern IntPtr DebugGetCodeWrapper(out UInt32 length); + public static string DebugGetCode() + { + UInt32 length; + return PtrToStringUtf8(InteropEmu.DebugGetCodeWrapper(out length), length); + } [DllImport(DLLPath, EntryPoint="DebugAssembleCode")] private static extern UInt32 DebugAssembleCodeWrapper([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string code, UInt16 startAddress, IntPtr assembledCodeBuffer); public static Int16[] DebugAssembleCode(string code, UInt16 startAddress) @@ -436,16 +440,16 @@ namespace Mesen.GUI hRelative.Free(); } } - - [DllImport(DLLPath, EntryPoint = "DebugGetFunctionEntryPoints")] - private static extern void DebugGetFunctionEntryPointsWrapper(IntPtr callstackAbsolute); + [DllImport(DLLPath)] private static extern Int32 DebugGetFunctionEntryPointCount(); + [DllImport(DLLPath, EntryPoint = "DebugGetFunctionEntryPoints")] private static extern void DebugGetFunctionEntryPointsWrapper(IntPtr callstackAbsolute, Int32 maxCount); public static Int32[] DebugGetFunctionEntryPoints() { - Int32[] entryPoints = new Int32[32768]; + int maxCount = DebugGetFunctionEntryPointCount(); + Int32[] entryPoints = new Int32[maxCount+1]; GCHandle hEntryPoints = GCHandle.Alloc(entryPoints, GCHandleType.Pinned); try { - InteropEmu.DebugGetFunctionEntryPointsWrapper(hEntryPoints.AddrOfPinnedObject()); + InteropEmu.DebugGetFunctionEntryPointsWrapper(hEntryPoints.AddrOfPinnedObject(), maxCount+1); } finally { hEntryPoints.Free(); } @@ -521,24 +525,38 @@ namespace Mesen.GUI return new List(PtrToStringUtf8(InteropEmu.GetAudioDevicesWrapper()).Split(new string[1] { "||" }, StringSplitOptions.RemoveEmptyEntries )); } - private static string PtrToStringUtf8(IntPtr ptr) + private static byte[] _codeByteArray = new byte[0]; + private static string PtrToStringUtf8(IntPtr ptr, UInt32 length = 0) { if(ptr == IntPtr.Zero) { return ""; } - + int len = 0; - while(System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) { - len++; + if(length == 0) { + while(System.Runtime.InteropServices.Marshal.ReadByte(ptr, len) != 0) { + len++; + } + } else { + len = (int)length; } if(len == 0) { return ""; } - byte[] array = new byte[len]; - System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, len); - return System.Text.Encoding.UTF8.GetString(array); + if(length == 0) { + byte[] array = new byte[len]; + System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, len); + return System.Text.Encoding.UTF8.GetString(array); + } else { + //For the code window, reuse the same buffer to reduce allocations + if(_codeByteArray.Length < len) { + Array.Resize(ref _codeByteArray, len); + } + System.Runtime.InteropServices.Marshal.Copy(ptr, _codeByteArray, 0, len); + return System.Text.Encoding.UTF8.GetString(_codeByteArray, 0, len); + } } public enum ConsoleNotificationType diff --git a/InteropDLL/DebugWrapper.cpp b/InteropDLL/DebugWrapper.cpp index ebf94a23..b757ad43 100644 --- a/InteropDLL/DebugWrapper.cpp +++ b/InteropDLL/DebugWrapper.cpp @@ -46,7 +46,7 @@ extern "C" DllExport void __stdcall DebugStepOver() { GetDebugger()->StepOver(); } DllExport void __stdcall DebugStepOut() { GetDebugger()->StepOut(); } DllExport void __stdcall DebugPpuStep(uint32_t count) { GetDebugger()->PpuStep(count); } - DllExport const char* __stdcall DebugGetCode() { return GetDebugger()->GetCode(); } + DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); } DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(scanline, cycle); } @@ -60,7 +60,8 @@ extern "C" DllExport void __stdcall DebugGetPalette(uint32_t *frameBuffer) { GetDebugger()->GetMemoryDumper()->GetPalette(frameBuffer); } DllExport void __stdcall DebugGetCallstack(int32_t *callstackAbsolute, int32_t *callstackRelative) { GetDebugger()->GetCallstack(callstackAbsolute, callstackRelative); } - DllExport void __stdcall DebugGetFunctionEntryPoints(int32_t *entryPoints) { GetDebugger()->GetFunctionEntryPoints(entryPoints); } + DllExport int32_t __stdcall DebugGetFunctionEntryPointCount() { return GetDebugger()->GetFunctionEntryPointCount(); } + DllExport void __stdcall DebugGetFunctionEntryPoints(int32_t *entryPoints, int32_t maxCount) { GetDebugger()->GetFunctionEntryPoints(entryPoints, maxCount); } DllExport int32_t __stdcall DebugGetRelativeAddress(uint32_t addr, AddressType type) { return GetDebugger()->GetRelativeAddress(addr, type); } DllExport int32_t __stdcall DebugGetAbsoluteAddress(uint32_t addr) { return GetDebugger()->GetAbsoluteAddress(addr); }