diff --git a/.gitignore b/.gitignore index 42ae6b49..c732649f 100644 --- a/.gitignore +++ b/.gitignore @@ -163,4 +163,6 @@ $RECYCLE.BIN/ *.rar *.VC.opendb -*.VC.db \ No newline at end of file +*.VC.db +*.VC.db-wal +*.VC.db-shm diff --git a/Core/Assembler.cpp b/Core/Assembler.cpp new file mode 100644 index 00000000..b4e33edf --- /dev/null +++ b/Core/Assembler.cpp @@ -0,0 +1,368 @@ +#include "stdafx.h" +#include +#include +#include "../Utilities/HexUtilities.h" +#include "Assembler.h" +#include "CPU.h" +#include "DisassemblyInfo.h" +#include "LabelManager.h" + +static const std::regex instRegex = std::regex("^\\s*([a-z]{3})[*]{0,1}[\\s]*([#]{0,1})([(]{0,1})[\\s]*([$]{0,1})([^,)(;:]*)[\\s]*((,x\\)|\\),y|,x|,y|\\)){0,1})\\s*(;*)(.*)", std::regex_constants::icase); +static const std::regex isCommentOrBlank = std::regex("^\\s*([;]+.*$|\\s*$)", std::regex_constants::icase); +static const std::regex labelRegex = std::regex("^\\s*([@_a-z][@_a-z0-9]*):(.*)", std::regex_constants::icase); + +static string opName[256] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + "BRK", "ORA", "STP", "SLO", "NOP", "ORA", "ASL", "SLO", "PHP", "ORA", "ASL", "ANC", "NOP", "ORA", "ASL", "SLO", //0 + "BPL", "ORA", "ST1", "SLO", "NOP", "ORA", "ASL", "SLO", "CLC", "ORA", "NOP", "SLO", "NOP", "ORA", "ASL", "SLO", //1 + "JSR", "AND", "ST2", "RLA", "BIT", "AND", "ROL", "RLA", "PLP", "AND", "ROL", "ANC", "BIT", "AND", "ROL", "RLA", //2 + "BMI", "AND", "ST3", "RLA", "NOP", "AND", "ROL", "RLA", "SEC", "AND", "NOP", "RLA", "NOP", "AND", "ROL", "RLA", //3 + "RTI", "EOR", "ST4", "SRE", "NOP", "EOR", "LSR", "SRE", "PHA", "EOR", "LSR", "ALR", "JMP", "EOR", "LSR", "SRE", //4 + "BVC", "EOR", "ST5", "SRE", "NOP", "EOR", "LSR", "SRE", "CLI", "EOR", "NOP", "SRE", "NOP", "EOR", "LSR", "SRE", //5 + "RTS", "ADC", "ST6", "RRA", "NOP", "ADC", "ROR", "RRA", "PLA", "ADC", "ROR", "ARR", "JMP", "ADC", "ROR", "RRA", //6 + "BVS", "ADC", "ST7", "RRA", "NOP", "ADC", "ROR", "RRA", "SEI", "ADC", "NOP", "RRA", "NOP", "ADC", "ROR", "RRA", //7 + "NOP", "STA", "NOP", "SAX", "STY", "STA", "STX", "SAX", "DEY", "NOP", "TXA", "XAA", "STY", "STA", "STX", "SAX", //8 + "BCC", "STA", "ST8", "AHX", "STY", "STA", "STX", "SAX", "TYA", "STA", "TXS", "TAS", "SHY", "STA", "SHX", "AXA", //9 + "LDY", "LDA", "LDX", "LAX", "LDY", "LDA", "LDX", "LAX", "TAY", "LDA", "TAX", "LAX", "LDY", "LDA", "LDX", "LAX", //A + "BCS", "LDA", "ST9", "LAX", "LDY", "LDA", "LDX", "LAX", "CLV", "LDA", "TSX", "LAS", "LDY", "LDA", "LDX", "LAX", //B + "CPY", "CMP", "NOP", "DCP", "CPY", "CMP", "DEC", "DCP", "INY", "CMP", "DEX", "AXS", "CPY", "CMP", "DEC", "DCP", //C + "BNE", "CMP", "HLT", "DCP", "NOP", "CMP", "DEC", "DCP", "CLD", "CMP", "NOP", "DCP", "NOP", "CMP", "DEC", "DCP", //D + "CPX", "SBC", "NOP", "ISC", "CPX", "SBC", "INC", "ISC", "INX", "SBC", "NOP", "SBC", "CPX", "SBC", "INC", "ISC", //E + "BEQ", "SBC", "HL2", "ISC", "NOP", "SBC", "INC", "ISC", "SED", "SBC", "NOP", "ISC", "NOP", "SBC", "INC", "ISC" //F +}; + +void Assembler::ProcessLine(string code, uint16_t &instructionAddress, vector& output, std::unordered_map &labels) +{ + //Remove extra spaces as part of processing + size_t offset = code.find_first_of(',', 0); + if(offset != string::npos) { + code.erase(std::remove(code.begin() + offset + 1, code.end(), ' '), code.end()); + } + offset = code.find_first_of(')', 0); + if(offset != string::npos) { + code.erase(std::remove(code.begin() + offset + 1, code.end(), ' '), code.end()); + } + + //Determine if the line is blank, a comment, a label or code + std::smatch match; + if(std::regex_search(code, match, labelRegex)) { + string label = match.str(1); + string afterLabel = match.str(2); + int32_t addr = _labelManager->GetLabelRelativeAddress(label); + if(addr >= 0 && addr != instructionAddress) { + //Label already exists, at another address + output.push_back(AssemblerSpecialCodes::LabelRedefinition); + return; + } else { + labels[match.str(1)] = instructionAddress; + ProcessLine(afterLabel, instructionAddress, output, labels); + return; + } + } else if(std::regex_search(code, match, isCommentOrBlank)) { + output.push_back(AssemblerSpecialCodes::EndOfLine); + return; + } else if(std::regex_search(code, match, instRegex) && match.size() > 1) { + LineData lineData; + AssemblerSpecialCodes result = GetLineData(match, lineData, labels); + if(result == AssemblerSpecialCodes::OK) { + AssembleInstruction(lineData, instructionAddress, output); + } else { + output.push_back(result); + } + } else { + output.push_back(AssemblerSpecialCodes::ParsingError); + } +} + +AssemblerSpecialCodes Assembler::GetLineData(std::smatch match, LineData &lineData, std::unordered_map &labels) +{ + lineData.OpCode = match.str(1); + lineData.IsImmediate = !match.str(2).empty(); + lineData.IsHex = !match.str(4).empty(); + lineData.HasComment = !match.str(8).empty(); + lineData.OperandSuffix = match.str(6); + lineData.HasOpeningParenthesis = !match.str(3).empty(); + + std::transform(lineData.OperandSuffix.begin(), lineData.OperandSuffix.end(), lineData.OperandSuffix.begin(), ::toupper); + std::transform(lineData.OpCode.begin(), lineData.OpCode.end(), lineData.OpCode.begin(), ::toupper); + + bool foundSpace = false; + for(char c : match.str(5)) { + if(c != ' ') { + if(foundSpace) { + //can't have spaces in operands (except at the very end) + return AssemblerSpecialCodes::InvalidSpaces; + } else if(lineData.IsHex && !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) { + //invalid hex + return AssemblerSpecialCodes::InvalidHex; + } + + lineData.Operand.push_back(c); + } else { + foundSpace = true; + } + } + + if(!lineData.HasComment && !match.str(9).empty()) { + //something is trailing at the end of the line, and it's not a comment + return AssemblerSpecialCodes::TrailingText; + } + + if(!lineData.IsHex) { + bool allNumeric = true; + for(int i = 0; i < lineData.Operand.size(); i++) { + if(lineData.Operand[i] == '-' && i == 0 && lineData.Operand.size() > 1) { + //First char is a minus sign, and more characters follow, continue + continue; + } + + if((lineData.Operand[i] < '0' || lineData.Operand[i] > '9')) { + allNumeric = false; + break; + } + } + + if(allNumeric && !lineData.Operand.empty()) { + //Operand is not empty, and it only contains decimal values + lineData.IsDecimal = true; + } else { + lineData.IsDecimal = false; + } + } + + return GetAddrModeAndOperandSize(lineData, labels); +} + +AssemblerSpecialCodes Assembler::GetAddrModeAndOperandSize(LineData &lineData, std::unordered_map &labels) +{ + int opSize = 0; + bool invalid = false; + string operand = lineData.Operand; + + if(lineData.IsHex) { + if(operand.size() == 0) { + return AssemblerSpecialCodes::MissingOperand; + } else if(operand.size() <= 2) { + opSize = 1; + } else if(operand.size() <= 4) { + opSize = 2; + } else { + return AssemblerSpecialCodes::OperandOutOfRange; + } + } else if(lineData.IsDecimal) { + int value = std::stoi(operand.c_str()); + if(value < -32768) { + //< -32768 is invalid + return AssemblerSpecialCodes::OperandOutOfRange; + } else if(value < -128) { + //-32768 to -129 is 2 bytes + opSize = 2; + } else if(value <= 255) { + //-128 to 255 is 2 bytes + opSize = 1; + } else if(value <= 65535) { + //256 to 65535 is 2 bytes + opSize = 2; + } else { + //> 65535 is invalid + return AssemblerSpecialCodes::OperandOutOfRange; + } + } else if(!operand.empty()) { + //Check if the operand is a known label + int32_t addr = _labelManager->GetLabelRelativeAddress(operand); + if(addr >= 256) { + lineData.Operand = HexUtilities::ToHex((uint16_t)addr); + lineData.IsHex = true; + opSize = 2; + } else if(addr >= 0) { + lineData.Operand = HexUtilities::ToHex((uint8_t)addr); + lineData.IsHex = true; + opSize = 1; + } else { + auto findResult = labels.find(operand); + if(findResult != labels.end()) { + lineData.Operand = HexUtilities::ToHex((uint16_t)findResult->second); + lineData.IsHex = true; + opSize = 2; + } else if(operand.size() == 1 && (operand[0] == 'A' || operand[0] == 'a') && lineData.OperandSuffix.empty() && !lineData.IsHex && !lineData.IsImmediate && !lineData.HasOpeningParenthesis) { + //Allow optional "A" after AddrMode == Accumulator instructions + lineData.Mode = AddrMode::Acc; + opSize = 0; + } else { + return AssemblerSpecialCodes::UnknownLabel; + } + } + } else { + //No operand + opSize = 0; + } + + if(lineData.Mode == AddrMode::None) { + if(lineData.IsImmediate) { + if(lineData.HasOpeningParenthesis || opSize == 0) { + invalid = true; + } + lineData.Mode = AddrMode::Imm; //or Rel + } else if(lineData.HasOpeningParenthesis) { + if(lineData.OperandSuffix.compare(")") == 0) { + opSize = 2; + lineData.Mode = AddrMode::Ind; + } else if(lineData.OperandSuffix.compare(",X)") == 0) { + opSize = 1; + lineData.Mode = AddrMode::IndX; + } else if(lineData.OperandSuffix.compare("),Y") == 0) { + opSize = 1; + lineData.Mode = AddrMode::IndY; + } else { + invalid = true; + } + } else { + if(lineData.OperandSuffix.compare(",X") == 0) { + if(opSize == 2) { + lineData.Mode = AddrMode::AbsX; + } else if(opSize == 1) { + lineData.Mode = AddrMode::ZeroX; + } else { + invalid = true; + } + } else if(lineData.OperandSuffix.compare(",Y") == 0) { + if(opSize == 2) { + lineData.Mode = AddrMode::AbsY; + } else if(opSize == 1) { + lineData.Mode = AddrMode::ZeroY; + } else { + invalid = true; + } + } else if(lineData.OperandSuffix.empty()) { + if(opSize == 0) { + lineData.Mode = AddrMode::Imp; //or Acc + } else if(opSize == 2) { + lineData.Mode = AddrMode::Abs; + } else if(opSize == 1) { + lineData.Mode = AddrMode::Zero; + } else { + invalid = true; + } + } else { + invalid = true; + } + } + } + + if(lineData.Mode == AddrMode::None) { + invalid = true; + } + + lineData.OperandSize = opSize; + + return invalid ? AssemblerSpecialCodes::ParsingError : AssemblerSpecialCodes::OK; +} + +void Assembler::AssembleInstruction(LineData &lineData, uint16_t &instructionAddress, vector& output) +{ + bool foundMatch = false; + if(lineData.Mode == AddrMode::Imp && lineData.OpCode.compare("NOP") == 0) { + //NOP has multiple name+addressing type collisions, the "official" NOP is 0xEA + output.push_back(0xEA); + instructionAddress++; + foundMatch = true; + } else { + for(uint8_t i = 0; i < 255; i++) { + AddrMode opMode = DisassemblyInfo::OPMode[i]; + if(lineData.OpCode.compare(opName[i]) == 0) { + bool modeMatch = opMode == lineData.Mode; + if(!modeMatch) { + if(lineData.Mode == AddrMode::Imp && opMode == AddrMode::Acc || + lineData.Mode == AddrMode::AbsY && opMode == AddrMode::AbsYW || + lineData.Mode == AddrMode::AbsX && opMode == AddrMode::AbsXW) { + modeMatch = true; + } else if(lineData.Mode == AddrMode::Abs && opMode == AddrMode::Rel || + lineData.Mode == AddrMode::Imm && opMode == AddrMode::Rel) { + + if(lineData.OperandSize == 2) { + if(lineData.Mode == AddrMode::Imm) { + //Hardcoded jump values must be 1-byte + output.push_back(AssemblerSpecialCodes::OutOfRangeJump); + return; + } else { + modeMatch = true; + + //Convert "absolute" jump to a relative jump + int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand); + + int16_t addressGap = value - (instructionAddress + 2); + if(addressGap > 127 || addressGap < -128) { + //Gap too long, can't jump that far + output.push_back(AssemblerSpecialCodes::OutOfRangeJump); + return; + } + + //Update data to match relative jump + lineData.OperandSize = 1; + lineData.IsHex = true; + lineData.Operand = HexUtilities::ToHex((uint8_t)addressGap); + } + } else { + //Accept 1-byte relative jumps + modeMatch = true; + } + } + } + + if(modeMatch) { + output.push_back(i); + instructionAddress += (lineData.OperandSize + 1); + + if(lineData.OperandSize == 1) { + int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand); + output.push_back(value & 0xFF); + } else if(lineData.OperandSize == 2) { + int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand); + output.push_back(value & 0xFF); + output.push_back((value >> 8) & 0xFF); + } + + foundMatch = true; + break; + } + } + } + } + + if(!foundMatch) { + output.push_back(AssemblerSpecialCodes::InvalidInstruction); + } else { + output.push_back(AssemblerSpecialCodes::EndOfLine); + } +} + +Assembler::Assembler(shared_ptr labelManager) +{ + _labelManager = labelManager; +} + +uint32_t Assembler::AssembleCode(string code, uint16_t startAddress, int16_t * assembledCode) +{ + std::unordered_map temporaryLabels; + + size_t i = 0; + vector output; + output.reserve(1000); + + while(i < code.size()) { + size_t offset = code.find_first_of('\n', i); + string line; + if(offset != string::npos) { + line = code.substr(i, offset - i); + i = offset + 1; + } else { + line = code.substr(i); + i = code.size(); + } + + ProcessLine(line, startAddress, output, temporaryLabels); + } + + memcpy(assembledCode, output.data(), output.size() * sizeof(uint16_t)); + return (uint32_t)output.size(); +} diff --git a/Core/Assembler.h b/Core/Assembler.h new file mode 100644 index 00000000..6e616106 --- /dev/null +++ b/Core/Assembler.h @@ -0,0 +1,51 @@ +#pragma once +#include "stdafx.h" +#include +#include "CPU.h" + +class LabelManager; + +struct LineData +{ + string OpCode; + string Operand; + string OperandSuffix; + AddrMode Mode = AddrMode::None; + int OperandSize = 0; + bool IsHex = false; + bool IsDecimal = false; + bool IsImmediate = false; + bool HasComment = false; + bool HasOpeningParenthesis = false; +}; + +enum AssemblerSpecialCodes +{ + OK = 0, + EndOfLine = -1, + ParsingError = -2, + OutOfRangeJump = -3, + LabelRedefinition = -4, + MissingOperand = -5, + OperandOutOfRange = -6, + InvalidHex = -7, + InvalidSpaces = -8, + TrailingText = -9, + UnknownLabel = -10, + InvalidInstruction = -11, +}; + +class Assembler +{ +private: + shared_ptr _labelManager; + void ProcessLine(string code, uint16_t &instructionAddress, vector& output, std::unordered_map &labels); + AssemblerSpecialCodes GetLineData(std::smatch match, LineData &lineData, std::unordered_map &labels); + AssemblerSpecialCodes GetAddrModeAndOperandSize(LineData &lineData, std::unordered_map &labels); + void AssembleInstruction(LineData &lineData, uint16_t &instructionAddress, vector& output); + +public: + Assembler(shared_ptr labelManager); + + uint32_t AssembleCode(string code, uint16_t startAddress, int16_t* assembledCode); +}; \ No newline at end of file diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index d92b1021..789701bb 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -409,6 +409,7 @@ + @@ -753,6 +754,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 50cde7ec..2666c517 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1123,6 +1123,9 @@ Nes + + Debugger + @@ -1317,5 +1320,8 @@ Misc + + Debugger + \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index cc8b695e..d6768a43 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -14,6 +14,7 @@ #include "MemoryDumper.h" #include "MemoryAccessCounter.h" #include "Profiler.h" +#include "Assembler.h" Debugger* Debugger::Instance = nullptr; const int Debugger::BreakpointTypeCount; @@ -28,6 +29,7 @@ Debugger::Debugger(shared_ptr console, shared_ptr cpu, shared_ptr< _mapper = mapper; _labelManager.reset(new LabelManager(_mapper)); + _assembler.reset(new Assembler(_labelManager)); _disassembler.reset(new Disassembler(memoryManager->GetInternalRAM(), mapper->GetPrgRom(), mapper->GetMemorySize(DebugMemoryType::PrgRom), mapper->GetWorkRam(), mapper->GetMemorySize(DebugMemoryType::WorkRam), this)); _codeDataLogger.reset(new CodeDataLogger(mapper->GetMemorySize(DebugMemoryType::PrgRom), mapper->GetMemorySize(DebugMemoryType::ChrRom))); _memoryDumper.reset(new MemoryDumper(_ppu, _memoryManager, _mapper, _codeDataLogger, this)); @@ -538,28 +540,17 @@ void Debugger::Run() _ppuStepCount = -1; } -bool Debugger::IsCodeChanged() -{ - string output = GenerateOutput(); - if(_outputCache.compare(output) == 0) { - return false; - } else { - _outputCache = output; - return true; - } -} - -string Debugger::GenerateOutput() +void Debugger::GenerateCodeOutput() { State cpuState = _cpu->GetState(); - string output; - output.reserve(10000); + _disassemblerOutput.clear(); + _disassemblerOutput.reserve(10000); bool showEffectiveAddresses = CheckFlag(DebuggerFlags::ShowEffectiveAddresses); bool showOnlyDiassembledCode = CheckFlag(DebuggerFlags::ShowOnlyDisassembledCode); //Get code in internal RAM - output = _disassembler->GetCode(0x0000, 0x1FFF, 0x0000, PrgMemoryType::PrgRom, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _labelManager); + _disassemblerOutput = _disassembler->GetCode(0x0000, 0x1FFF, 0x0000, PrgMemoryType::PrgRom, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _labelManager); for(uint32_t i = 0x2000; i < 0x10000; i += 0x100) { //Merge all sequential ranges into 1 chunk @@ -580,16 +571,15 @@ string Debugger::GenerateOutput() addr += 0x100; i+=0x100; } - output += _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, memoryType, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _labelManager); + _disassemblerOutput += _disassembler->GetCode(startAddr, endAddr, startMemoryAddr, memoryType, showEffectiveAddresses, showOnlyDiassembledCode, cpuState, _memoryManager, _labelManager); } } - - return output; } -string* Debugger::GetCode() +const char* Debugger::GetCode() { - return &_outputCache; + GenerateCodeOutput(); + return _disassemblerOutput.c_str(); } int32_t Debugger::GetRelativeAddress(uint32_t addr, AddressType type) @@ -670,6 +660,11 @@ void Debugger::GetFunctionEntryPoints(int32_t* entryPoints) entryPoints[i] = -1; } +shared_ptr Debugger::GetAssembler() +{ + return _assembler; +} + shared_ptr Debugger::GetTraceLogger() { return _traceLogger; diff --git a/Core/Debugger.h b/Core/Debugger.h index 22f39952..a56e0be1 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -19,6 +19,7 @@ class CPU; class PPU; class MemoryManager; class Console; +class Assembler; class Disassembler; class LabelManager; class MemoryDumper; @@ -33,6 +34,7 @@ private: const static int BreakpointTypeCount = 6; unique_ptr _disassembler; + shared_ptr _assembler; shared_ptr _memoryDumper; shared_ptr _codeDataLogger; shared_ptr _memoryAccessCounter; @@ -77,7 +79,7 @@ private: uint32_t _flags; string _romName; - string _outputCache; + string _disassemblerOutput; atomic _stepCount; atomic _ppuStepCount; atomic _stepCycleCount; @@ -142,9 +144,8 @@ public: bool IsExecutionStopped(); - bool IsCodeChanged(); - string GenerateOutput(); - string* GetCode(); + void GenerateCodeOutput(); + const char* GetCode(); int32_t GetRelativeAddress(uint32_t addr, AddressType type); int32_t GetAbsoluteAddress(uint32_t addr); @@ -154,6 +155,7 @@ public: void StopTraceLogger(); shared_ptr GetProfiler(); + shared_ptr GetAssembler(); shared_ptr GetTraceLogger(); shared_ptr GetMemoryDumper(); shared_ptr GetMemoryAccessCounter(); diff --git a/Core/DisassemblyInfo.cpp b/Core/DisassemblyInfo.cpp index edc60640..160f4735 100644 --- a/Core/DisassemblyInfo.cpp +++ b/Core/DisassemblyInfo.cpp @@ -210,7 +210,7 @@ int32_t DisassemblyInfo::GetEffectiveAddress(State& cpuState, MemoryManager* mem case AddrMode::IndYW: { uint8_t zeroAddr = *(_opPointer + 1); uint16_t addr = memoryManager->DebugRead(zeroAddr) | memoryManager->DebugRead((uint8_t)(zeroAddr + 1)) << 8; - return addr + cpuState.Y; + return (uint16_t)(addr + cpuState.Y); } case AddrMode::Ind: { @@ -220,12 +220,12 @@ int32_t DisassemblyInfo::GetEffectiveAddress(State& cpuState, MemoryManager* mem case AddrMode::AbsX: case AddrMode::AbsXW: { - return (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.X; + return (uint16_t)((*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.X) & 0xFFFF; } case AddrMode::AbsY: case AddrMode::AbsYW: { - return (*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.Y; + return (uint16_t)((*(_opPointer + 1) | (*(_opPointer + 2) << 8)) + cpuState.Y) & 0xFFFF; } } diff --git a/Core/MessageManager.cpp b/Core/MessageManager.cpp index b15f8fd5..1aefe2c4 100644 --- a/Core/MessageManager.cpp +++ b/Core/MessageManager.cpp @@ -1,4 +1,4 @@ -#include "stdafx.h" +#include "stdafx.h" #include #include "MessageManager.h" diff --git a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs index 05419b67..4837a190 100644 --- a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs +++ b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.Designer.cs @@ -29,19 +29,21 @@ { this.components = new System.ComponentModel.Container(); this.contextMenuCode = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mnuEditSubroutine = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator(); this.mnuShowNextStatement = new System.Windows.Forms.ToolStripMenuItem(); this.mnuSetNextStatement = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuShowLineNotes = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPrgAddressReplace = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuPrgAddressBelow = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); - this.mnuHidePrgAddresses = new System.Windows.Forms.ToolStripMenuItem(); this.mnuShowCodeNotes = new System.Windows.Forms.ToolStripMenuItem(); this.mnuShowByteCodeOnLeft = new System.Windows.Forms.ToolStripMenuItem(); this.mnuShowByteCodeBelow = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); this.mnuHideByteCode = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuShowLineNotes = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPrgAddressReplace = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPrgAddressBelow = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuHidePrgAddresses = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); this.mnuEditLabel = new System.Windows.Forms.ToolStripMenuItem(); this.mnuToggleBreakpoint = new System.Windows.Forms.ToolStripMenuItem(); @@ -80,6 +82,8 @@ // contextMenuCode // this.contextMenuCode.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuEditSubroutine, + this.toolStripMenuItem7, this.mnuShowNextStatement, this.mnuSetNextStatement, this.toolStripMenuItem1, @@ -96,9 +100,24 @@ this.mnuNavigateBackward, this.mnuNavigateForward}); this.contextMenuCode.Name = "contextMenuWatch"; - this.contextMenuCode.Size = new System.Drawing.Size(259, 270); + this.contextMenuCode.Size = new System.Drawing.Size(259, 320); + this.contextMenuCode.Closed += new System.Windows.Forms.ToolStripDropDownClosedEventHandler(this.contextMenuCode_Closed); this.contextMenuCode.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuCode_Opening); // + // mnuEditSubroutine + // + this.mnuEditSubroutine.Image = global::Mesen.GUI.Properties.Resources.Edit; + this.mnuEditSubroutine.Name = "mnuEditSubroutine"; + this.mnuEditSubroutine.ShortcutKeys = System.Windows.Forms.Keys.F4; + this.mnuEditSubroutine.Size = new System.Drawing.Size(258, 22); + this.mnuEditSubroutine.Text = "Edit subroutine"; + this.mnuEditSubroutine.Click += new System.EventHandler(this.mnuEditSubroutine_Click); + // + // toolStripMenuItem7 + // + this.toolStripMenuItem7.Name = "toolStripMenuItem7"; + this.toolStripMenuItem7.Size = new System.Drawing.Size(255, 6); + // // mnuShowNextStatement // this.mnuShowNextStatement.Name = "mnuShowNextStatement"; @@ -122,6 +141,43 @@ this.toolStripMenuItem1.Name = "toolStripMenuItem1"; this.toolStripMenuItem1.Size = new System.Drawing.Size(255, 6); // + // mnuShowCodeNotes + // + this.mnuShowCodeNotes.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuShowByteCodeOnLeft, + this.mnuShowByteCodeBelow, + this.toolStripMenuItem5, + this.mnuHideByteCode}); + this.mnuShowCodeNotes.Name = "mnuShowCodeNotes"; + this.mnuShowCodeNotes.Size = new System.Drawing.Size(258, 22); + this.mnuShowCodeNotes.Text = "Byte Code Display"; + // + // mnuShowByteCodeOnLeft + // + this.mnuShowByteCodeOnLeft.Name = "mnuShowByteCodeOnLeft"; + this.mnuShowByteCodeOnLeft.Size = new System.Drawing.Size(130, 22); + this.mnuShowByteCodeOnLeft.Text = "On the left"; + this.mnuShowByteCodeOnLeft.Click += new System.EventHandler(this.mnuShowByteCodeOnLeft_Click); + // + // mnuShowByteCodeBelow + // + this.mnuShowByteCodeBelow.Name = "mnuShowByteCodeBelow"; + this.mnuShowByteCodeBelow.Size = new System.Drawing.Size(130, 22); + this.mnuShowByteCodeBelow.Text = "Below"; + this.mnuShowByteCodeBelow.Click += new System.EventHandler(this.mnuShowByteCodeBelow_Click); + // + // toolStripMenuItem5 + // + this.toolStripMenuItem5.Name = "toolStripMenuItem5"; + this.toolStripMenuItem5.Size = new System.Drawing.Size(127, 6); + // + // mnuHideByteCode + // + this.mnuHideByteCode.Name = "mnuHideByteCode"; + this.mnuHideByteCode.Size = new System.Drawing.Size(130, 22); + this.mnuHideByteCode.Text = "Hidden"; + this.mnuHideByteCode.Click += new System.EventHandler(this.mnuHideByteCode_Click); + // // mnuShowLineNotes // this.mnuShowLineNotes.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -160,43 +216,6 @@ this.mnuHidePrgAddresses.Text = "Hidden"; this.mnuHidePrgAddresses.Click += new System.EventHandler(this.mnuHidePrgAddresses_Click); // - // mnuShowCodeNotes - // - this.mnuShowCodeNotes.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.mnuShowByteCodeOnLeft, - this.mnuShowByteCodeBelow, - this.toolStripMenuItem5, - this.mnuHideByteCode}); - this.mnuShowCodeNotes.Name = "mnuShowCodeNotes"; - this.mnuShowCodeNotes.Size = new System.Drawing.Size(258, 22); - this.mnuShowCodeNotes.Text = "Byte Code Display"; - // - // mnuShowByteCodeOnLeft - // - this.mnuShowByteCodeOnLeft.Name = "mnuShowByteCodeOnLeft"; - this.mnuShowByteCodeOnLeft.Size = new System.Drawing.Size(152, 22); - this.mnuShowByteCodeOnLeft.Text = "On the left"; - this.mnuShowByteCodeOnLeft.Click += new System.EventHandler(this.mnuShowByteCodeOnLeft_Click); - // - // mnuShowByteCodeBelow - // - this.mnuShowByteCodeBelow.Name = "mnuShowByteCodeBelow"; - this.mnuShowByteCodeBelow.Size = new System.Drawing.Size(152, 22); - this.mnuShowByteCodeBelow.Text = "Below"; - this.mnuShowByteCodeBelow.Click += new System.EventHandler(this.mnuShowByteCodeBelow_Click); - // - // toolStripMenuItem5 - // - this.toolStripMenuItem5.Name = "toolStripMenuItem5"; - this.toolStripMenuItem5.Size = new System.Drawing.Size(149, 6); - // - // mnuHideByteCode - // - this.mnuHideByteCode.Name = "mnuHideByteCode"; - this.mnuHideByteCode.Size = new System.Drawing.Size(152, 22); - this.mnuHideByteCode.Text = "Hidden"; - this.mnuHideByteCode.Click += new System.EventHandler(this.mnuHideByteCode_Click); - // // toolStripMenuItem2 // this.toolStripMenuItem2.Name = "toolStripMenuItem2"; @@ -488,5 +507,7 @@ private System.Windows.Forms.ToolStripMenuItem mnuHidePrgAddresses; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem6; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem5; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem7; + private System.Windows.Forms.ToolStripMenuItem mnuEditSubroutine; } } diff --git a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs index 6e4c80ae..69c70ccb 100644 --- a/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs +++ b/GUI.NET/Debugger/Controls/ctrlDebuggerCode.cs @@ -17,6 +17,8 @@ namespace Mesen.GUI.Debugger { public delegate void AddressEventHandler(AddressEventArgs args); public delegate void WatchEventHandler(WatchEventArgs args); + public delegate void AssemblerEventHandler(AssemblerEventArgs args); + public event AssemblerEventHandler OnEditCode; public event AddressEventHandler OnSetNextStatement; private DebugViewInfo _config; private HashSet _unexecutedAddresses = new HashSet(); @@ -164,6 +166,46 @@ namespace Mesen.GUI.Debugger this.ctrlCodeViewer.EndUpdate(); } + Dictionary> _codeContent = new Dictionary>(); + Dictionary _codeByteContentLength = new Dictionary(); + public List GetCode(out int byteLength, int startAddress, int endAddress = -1) + { + List result = new List(); + byteLength = 0; + + //TODO: Handle multi-line comments + //TODO: Find start of function + //TODO: Display labels in code window + //TODO: Status bar to show cursor's current line + //TODO: Bind "Start Address" field + //TODO: Byte code cache doesn't update + //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; ) { + Tuple codeRow; + if(_codeContent.TryGetValue(i, out codeRow)) { + string code = codeRow.Item1; + string comment = codeRow.Item2; + if(code.StartsWith("--") || code.StartsWith("__") || code.StartsWith("[[")) { + break; + } + + result.Add(code + (comment ?? "")); + int length = _codeByteContentLength[i]; + byteLength += length; + i += length; + + if(endAddress == -1 && (string.Compare(code, "RTI", true) == 0 || string.Compare(code, "RTS", true) == 0)) { + break; + } + } else { + break; + } + } + + return result; + } + public bool UpdateCode(bool forceUpdate = false) { if(_codeChanged || forceUpdate) { @@ -171,6 +213,8 @@ namespace Mesen.GUI.Debugger List lineNumberNotes = new List(); List codeNotes = new List(); List codeLines = new List(); + _codeContent = new Dictionary>(); + _codeByteContentLength = new Dictionary(); _unexecutedAddresses = new HashSet(); _speculativeCodeAddreses = new HashSet(); @@ -193,6 +237,10 @@ namespace Mesen.GUI.Debugger 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; + + string[] codeRow = lineParts[4].Split('\x2'); + _codeContent[relativeAddress] = new Tuple(codeRow[0].Trim(), codeRow.Length > 2 ? " " + codeRow[2].Replace(Environment.NewLine, " | ") : null); } previousIndex = index; @@ -516,6 +564,12 @@ namespace Mesen.GUI.Debugger { mnuShowNextStatement.Enabled = _currentActiveAddress.HasValue; mnuSetNextStatement.Enabled = _currentActiveAddress.HasValue; + mnuEditSubroutine.Enabled = InteropEmu.DebugIsExecutionStopped(); + } + + private void contextMenuCode_Closed(object sender, ToolStripDropDownClosedEventArgs e) + { + mnuEditSubroutine.Enabled = true; } private void mnuShowNextStatement_Click(object sender, EventArgs e) @@ -678,6 +732,16 @@ namespace Mesen.GUI.Debugger _config.PrgAddressPosition = PrgAddressPosition.Hidden; this.UpdateConfig(); } + + private void mnuEditSubroutine_Click(object sender, EventArgs e) + { + int currentLine = this.GetCurrentLine(); + if(currentLine != -1 && InteropEmu.DebugIsExecutionStopped()) { + int byteLength; + List code = this.GetCode(out byteLength, currentLine); + this.OnEditCode?.Invoke(new AssemblerEventArgs() { Code = string.Join(Environment.NewLine, code), StartAddress = (UInt16)currentLine, BlockLength = (UInt16)byteLength }); + } + } } public class WatchEventArgs : EventArgs @@ -689,4 +753,11 @@ namespace Mesen.GUI.Debugger { public UInt32 Address { get; set; } } + + public class AssemblerEventArgs : EventArgs + { + public string Code { get; set; } + public UInt16 StartAddress { get; set; } + public UInt16 BlockLength { get; set; } + } } diff --git a/GUI.NET/Debugger/frmAssembler.Designer.cs b/GUI.NET/Debugger/frmAssembler.Designer.cs new file mode 100644 index 00000000..18627b3a --- /dev/null +++ b/GUI.NET/Debugger/frmAssembler.Designer.cs @@ -0,0 +1,320 @@ +namespace Mesen.GUI.Debugger +{ + partial class frmAssembler + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnOk = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.ctrlHexBox = new Be.Windows.Forms.HexBox(); + this.lstErrors = new System.Windows.Forms.ListBox(); + this.grpSettings = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.label1 = new System.Windows.Forms.Label(); + this.txtStartAddress = new System.Windows.Forms.TextBox(); + this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + this.label2 = new System.Windows.Forms.Label(); + this.lblByteUsage = new System.Windows.Forms.Label(); + this.picSizeWarning = new System.Windows.Forms.PictureBox(); + this.flowLayoutPanel3 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblNoChanges = new System.Windows.Forms.Label(); + this.panel1 = new System.Windows.Forms.Panel(); + this.txtCode = new System.Windows.Forms.RichTextBox(); + this.tableLayoutPanel1.SuspendLayout(); + this.grpSettings.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.flowLayoutPanel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picSizeWarning)).BeginInit(); + this.flowLayoutPanel3.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // btnOk + // + this.btnOk.Location = new System.Drawing.Point(279, 3); + this.btnOk.Name = "btnOk"; + this.btnOk.Size = new System.Drawing.Size(75, 23); + this.btnOk.TabIndex = 0; + this.btnOk.Text = "Apply"; + this.btnOk.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; + this.btnOk.UseVisualStyleBackColor = true; + this.btnOk.Click += new System.EventHandler(this.btnOk_Click); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(360, 3); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.ctrlHexBox, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.lstErrors, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.grpSettings, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 141F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(999, 534); + this.tableLayoutPanel1.TabIndex = 2; + // + // ctrlHexBox + // + this.ctrlHexBox.ByteColorProvider = null; + this.ctrlHexBox.ColumnInfoVisible = true; + this.ctrlHexBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlHexBox.Font = new System.Drawing.Font("Segoe UI", 9F); + this.ctrlHexBox.InfoBackColor = System.Drawing.Color.DarkGray; + this.ctrlHexBox.LineInfoVisible = true; + this.ctrlHexBox.Location = new System.Drawing.Point(552, 3); + this.ctrlHexBox.Name = "ctrlHexBox"; + this.ctrlHexBox.ReadOnly = true; + this.ctrlHexBox.ShadowSelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(60)))), ((int)(((byte)(188)))), ((int)(((byte)(255))))); + this.ctrlHexBox.Size = new System.Drawing.Size(444, 387); + this.ctrlHexBox.TabIndex = 1; + this.ctrlHexBox.UseFixedBytesPerLine = true; + this.ctrlHexBox.VScrollBarVisible = true; + // + // lstErrors + // + this.lstErrors.FormattingEnabled = true; + this.lstErrors.Location = new System.Drawing.Point(3, 396); + this.lstErrors.Name = "lstErrors"; + this.lstErrors.Size = new System.Drawing.Size(543, 134); + this.lstErrors.TabIndex = 2; + // + // grpSettings + // + this.grpSettings.Controls.Add(this.tableLayoutPanel2); + this.grpSettings.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpSettings.Location = new System.Drawing.Point(552, 396); + this.grpSettings.Name = "grpSettings"; + this.grpSettings.Size = new System.Drawing.Size(444, 135); + this.grpSettings.TabIndex = 3; + this.grpSettings.TabStop = false; + this.grpSettings.Text = "Settings"; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 1; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel1, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel2, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel3, 0, 2); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 16); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 3; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(438, 116); + this.tableLayoutPanel2.TabIndex = 0; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Controls.Add(this.label1); + this.flowLayoutPanel1.Controls.Add(this.txtStartAddress); + this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(438, 25); + this.flowLayoutPanel1.TabIndex = 3; + // + // label1 + // + this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 6); + this.label1.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(81, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Start address: $"; + // + // txtStartAddress + // + this.txtStartAddress.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.txtStartAddress.Location = new System.Drawing.Point(84, 3); + this.txtStartAddress.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3); + this.txtStartAddress.Name = "txtStartAddress"; + this.txtStartAddress.Size = new System.Drawing.Size(65, 20); + this.txtStartAddress.TabIndex = 1; + // + // flowLayoutPanel2 + // + this.flowLayoutPanel2.Controls.Add(this.label2); + this.flowLayoutPanel2.Controls.Add(this.lblByteUsage); + this.flowLayoutPanel2.Controls.Add(this.picSizeWarning); + this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel2.Location = new System.Drawing.Point(0, 25); + this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel2.Name = "flowLayoutPanel2"; + this.flowLayoutPanel2.Size = new System.Drawing.Size(438, 25); + this.flowLayoutPanel2.TabIndex = 4; + // + // label2 + // + this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(3, 6); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(63, 13); + this.label2.TabIndex = 2; + this.label2.Text = "Byte usage:"; + // + // lblByteUsage + // + this.lblByteUsage.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblByteUsage.AutoSize = true; + this.lblByteUsage.Location = new System.Drawing.Point(72, 6); + this.lblByteUsage.Name = "lblByteUsage"; + this.lblByteUsage.Size = new System.Drawing.Size(30, 13); + this.lblByteUsage.TabIndex = 3; + this.lblByteUsage.Text = "0 / 0"; + // + // picSizeWarning + // + this.picSizeWarning.Image = global::Mesen.GUI.Properties.Resources.Warning; + this.picSizeWarning.Location = new System.Drawing.Point(108, 5); + this.picSizeWarning.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3); + this.picSizeWarning.Name = "picSizeWarning"; + this.picSizeWarning.Size = new System.Drawing.Size(18, 18); + this.picSizeWarning.TabIndex = 10; + this.picSizeWarning.TabStop = false; + this.picSizeWarning.Visible = false; + // + // flowLayoutPanel3 + // + this.flowLayoutPanel3.Controls.Add(this.btnCancel); + this.flowLayoutPanel3.Controls.Add(this.btnOk); + this.flowLayoutPanel3.Controls.Add(this.lblNoChanges); + this.flowLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Bottom; + this.flowLayoutPanel3.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; + this.flowLayoutPanel3.Location = new System.Drawing.Point(0, 86); + this.flowLayoutPanel3.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel3.Name = "flowLayoutPanel3"; + this.flowLayoutPanel3.Size = new System.Drawing.Size(438, 30); + this.flowLayoutPanel3.TabIndex = 5; + // + // lblNoChanges + // + this.lblNoChanges.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblNoChanges.AutoSize = true; + this.lblNoChanges.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128))); + this.lblNoChanges.ForeColor = System.Drawing.SystemColors.GrayText; + this.lblNoChanges.Location = new System.Drawing.Point(99, 8); + this.lblNoChanges.Name = "lblNoChanges"; + this.lblNoChanges.Size = new System.Drawing.Size(174, 13); + this.lblNoChanges.TabIndex = 2; + this.lblNoChanges.Text = "Current code matches original code"; + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.ControlDark; + this.panel1.Controls.Add(this.txtCode); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(3, 3); + this.panel1.Name = "panel1"; + this.panel1.Padding = new System.Windows.Forms.Padding(1); + this.panel1.Size = new System.Drawing.Size(543, 387); + this.panel1.TabIndex = 4; + // + // txtCode + // + this.txtCode.AcceptsTab = true; + this.txtCode.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.txtCode.DetectUrls = false; + this.txtCode.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtCode.Location = new System.Drawing.Point(1, 1); + this.txtCode.Name = "txtCode"; + this.txtCode.Size = new System.Drawing.Size(541, 385); + this.txtCode.TabIndex = 4; + this.txtCode.Text = ""; + this.txtCode.WordWrap = false; + this.txtCode.TextChanged += new System.EventHandler(this.txtCode_TextChanged); + this.txtCode.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txtCode_KeyDown); + // + // frmAssembler + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(999, 534); + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "frmAssembler"; + this.Text = "Assembler"; + this.tableLayoutPanel1.ResumeLayout(false); + this.grpSettings.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.flowLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel1.PerformLayout(); + this.flowLayoutPanel2.ResumeLayout(false); + this.flowLayoutPanel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picSizeWarning)).EndInit(); + this.flowLayoutPanel3.ResumeLayout(false); + this.flowLayoutPanel3.PerformLayout(); + this.panel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private Be.Windows.Forms.HexBox ctrlHexBox; + private System.Windows.Forms.ListBox lstErrors; + private System.Windows.Forms.GroupBox grpSettings; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtStartAddress; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label lblByteUsage; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel3; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnOk; + private System.Windows.Forms.PictureBox picSizeWarning; + private System.Windows.Forms.RichTextBox txtCode; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Label lblNoChanges; + } +} \ No newline at end of file diff --git a/GUI.NET/Debugger/frmAssembler.cs b/GUI.NET/Debugger/frmAssembler.cs new file mode 100644 index 00000000..bbae461b --- /dev/null +++ b/GUI.NET/Debugger/frmAssembler.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; +using Be.Windows.Forms; +using Mesen.GUI.Controls; +using Mesen.GUI.Forms; + +namespace Mesen.GUI.Debugger +{ + public partial class frmAssembler : BaseForm + { + private UInt16 _startAddress; + private UInt16 _blockLength; + private bool _hasParsingErrors = false; + private bool _containedRtiRts = false; + private bool _isEditMode = false; + + public frmAssembler(string code = "", UInt16 startAddress = 0, UInt16 blockLength = 0) + { + InitializeComponent(); + + if(string.IsNullOrWhiteSpace(code)) { + btnCancel.Text = "Close"; + btnOk.Enabled = false; + btnOk.Visible = false; + } else { + _isEditMode = true; + _containedRtiRts = ContainsRtiOrRts(code); + } + + _startAddress = startAddress; + _blockLength = blockLength; + + txtCode.Font = new Font(BaseControl.MonospaceFontFamily, 10); + ctrlHexBox.Font = new Font(BaseControl.MonospaceFontFamily, 10, FontStyle.Regular); + ctrlHexBox.SelectionForeColor = Color.White; + ctrlHexBox.SelectionBackColor = Color.FromArgb(31, 123, 205); + ctrlHexBox.InfoBackColor = Color.FromArgb(235, 235, 235); + ctrlHexBox.InfoForeColor = Color.Gray; + ctrlHexBox.Width = ctrlHexBox.RequiredWidth; + ctrlHexBox.ByteProvider = new StaticByteProvider(new byte[0]); + + txtStartAddress.Text = _startAddress.ToString("X4"); + txtCode.Text = code; + txtCode.Select(0, 0); + + toolTip.SetToolTip(picSizeWarning, "Warning: The new code exceeds the original code's length." + Environment.NewLine + "Applying this modification will overwrite other portions of the code and potentially cause problems."); + + UpdateWindow(); + } + + private bool ContainsRtiOrRts(string code) + { + return Regex.IsMatch(code, "^\\s*(RTI|RTS)", RegexOptions.IgnoreCase | RegexOptions.Multiline); + } + + private bool SizeExceeded + { + get { return ctrlHexBox.ByteProvider.Length > _blockLength; } + } + + private bool NeedRtiRtsWarning + { + get { return _containedRtiRts && !ContainsRtiOrRts(txtCode.Text); } + } + + private bool IsIdentical + { + get + { + if(ctrlHexBox.ByteProvider.Length != _blockLength) { + return false; + } else { + for(int i = 0; i < ctrlHexBox.ByteProvider.Length; i++) { + if(InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, (UInt32)(_startAddress + i)) != ctrlHexBox.ByteProvider.ReadByte(i)) { + return false; + } + } + return true; + } + } + } + + private void UpdateWindow() + { + short[] byteCode = InteropEmu.DebugAssembleCode(txtCode.Text, _startAddress); + + List convertedByteCode = new List(); + List errorList = new List(); + string[] codeLines = txtCode.Text.Replace("\r", "").Split('\n'); + int line = 1; + foreach(short s in byteCode) { + if(s >= 0) { + convertedByteCode.Add((byte)s); + } else if(s == (int)AssemblerSpecialCodes.EndOfLine) { + line++; + } else if(s < (int)AssemblerSpecialCodes.EndOfLine) { + string message = "unknown error"; + switch((AssemblerSpecialCodes)s) { + case AssemblerSpecialCodes.ParsingError: message = "Invalid syntax"; break; + case AssemblerSpecialCodes.OutOfRangeJump: message = "Relative jump is out of range (-128 to 127)"; break; + case AssemblerSpecialCodes.LabelRedefinition: message = "Cannot redefine an existing label"; break; + case AssemblerSpecialCodes.MissingOperand: message = "Operand is missing"; break; + case AssemblerSpecialCodes.OperandOutOfRange: message = "Operand is out of range (invalid value)"; break; + case AssemblerSpecialCodes.InvalidHex: message = "Hexadecimal string is invalid"; break; + case AssemblerSpecialCodes.InvalidSpaces: message = "Operand cannot contain spaces"; break; + case AssemblerSpecialCodes.TrailingText: message = "Invalid text trailing at the end of line"; break; + case AssemblerSpecialCodes.UnknownLabel: message = "Unknown label"; break; + case AssemblerSpecialCodes.InvalidInstruction: message = "Invalid instruction"; break; + } + errorList.Add("Line " + line.ToString() + ": " + message + " - " + codeLines[line-1]); + line++; + } + } + + _hasParsingErrors = errorList.Count > 0; + + lstErrors.BeginUpdate(); + lstErrors.Items.Clear(); + lstErrors.Items.AddRange(errorList.ToArray()); + lstErrors.EndUpdate(); + + ctrlHexBox.ByteProvider = new StaticByteProvider(convertedByteCode.ToArray()); + + if(_isEditMode) { + lblByteUsage.Text = ctrlHexBox.ByteProvider.Length.ToString() + " / " + _blockLength.ToString(); + lblByteUsage.ForeColor = SizeExceeded ? Color.Red : Color.Black; + picSizeWarning.Visible = SizeExceeded; + btnOk.Image = _hasParsingErrors || NeedRtiRtsWarning || SizeExceeded ? Properties.Resources.Warning : null; + + bool isIdentical = IsIdentical; + lblNoChanges.Visible = isIdentical; + btnOk.Enabled = !isIdentical; + } else { + lblByteUsage.Text = ctrlHexBox.ByteProvider.Length.ToString(); + } + } + + private void txtCode_TextChanged(object sender, EventArgs e) + { + UpdateWindow(); + } + + private void txtCode_KeyDown(object sender, KeyEventArgs e) + { + if(e.Control && e.KeyCode == Keys.V || e.Shift && e.KeyCode == Keys.Insert) { + txtCode.Paste(DataFormats.GetFormat("Text")); + e.Handled = true; + } + } + + private void btnOk_Click(object sender, EventArgs e) + { + List warningMessages = new List(); + if(_hasParsingErrors) { + warningMessages.Add("Warning: The code contains parsing errors - lines with errors will be ignored."); + } + if(SizeExceeded) { + warningMessages.Add("Warning: The new code exceeds the original code's length." + Environment.NewLine + "Applying this modification will overwrite other portions of the code and potentially cause problems."); + } + if(NeedRtiRtsWarning) { + warningMessages.Add("Warning: The code originally contained an RTI/RTS instruction and it no longer does - this will probably cause problems."); + } + + if(warningMessages.Count == 0 || MessageBox.Show(string.Join(Environment.NewLine+Environment.NewLine, warningMessages.ToArray()) + Environment.NewLine + Environment.NewLine + "OK?", "Warning", MessageBoxButtons.OKCancel) == DialogResult.OK) { + byte lastByte = ctrlHexBox.ByteProvider.ReadByte(ctrlHexBox.ByteProvider.Length - 1); + bool endsWithRtiRts = lastByte == 0x40 || lastByte == 0x60; + int byteGap = (int)(_blockLength - ctrlHexBox.ByteProvider.Length); + List bytes = new List(((StaticByteProvider)ctrlHexBox.ByteProvider).Bytes); + if(byteGap > 0) { + //Pad data with NOPs as needed + int insertPoint = endsWithRtiRts ? bytes.Count - 2 : bytes.Count - 1; + for(int i = 0; i < byteGap; i++) { + bytes.Insert(insertPoint, 0xEA); //0xEA = NOP + } + } + + for(int i = 0; i < bytes.Count; i++) { + InteropEmu.DebugSetMemoryValue(DebugMemoryType.CpuMemory, (UInt32)(_startAddress + i), bytes[i]); + } + + this.DialogResult = DialogResult.OK; + this.Close(); + } + } + + private void btnCancel_Click(object sender, EventArgs e) + { + this.Close(); + } + + enum AssemblerSpecialCodes + { + OK = 0, + EndOfLine = -1, + ParsingError = -2, + OutOfRangeJump = -3, + LabelRedefinition = -4, + MissingOperand = -5, + OperandOutOfRange = -6, + InvalidHex = -7, + InvalidSpaces = -8, + TrailingText = -9, + UnknownLabel = -10, + InvalidInstruction = -11, + } + } +} diff --git a/GUI.NET/Debugger/frmAssembler.resx b/GUI.NET/Debugger/frmAssembler.resx new file mode 100644 index 00000000..8766f298 --- /dev/null +++ b/GUI.NET/Debugger/frmAssembler.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/GUI.NET/Debugger/frmDebugger.Designer.cs b/GUI.NET/Debugger/frmDebugger.Designer.cs index 1bdd9986..6965f714 100644 --- a/GUI.NET/Debugger/frmDebugger.Designer.cs +++ b/GUI.NET/Debugger/frmDebugger.Designer.cs @@ -147,6 +147,7 @@ namespace Mesen.GUI.Debugger this.lblChrAnalysisResult = new System.Windows.Forms.ToolStripStatusLabel(); this.ctrlPpuMemoryMapping = new Mesen.GUI.Debugger.Controls.ctrlMemoryMapping(); this.ctrlCpuMemoryMapping = new Mesen.GUI.Debugger.Controls.ctrlMemoryMapping(); + this.mnuAssembler = new System.Windows.Forms.ToolStripMenuItem(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit(); this.splitContainer.Panel1.SuspendLayout(); this.splitContainer.Panel2.SuspendLayout(); @@ -215,7 +216,7 @@ namespace Mesen.GUI.Debugger this.ctrlSplitContainerTop.Panel2.Controls.Add(this.tlpFunctionLabelLists); this.ctrlSplitContainerTop.Panel2MinSize = 150; this.ctrlSplitContainerTop.Size = new System.Drawing.Size(1260, 390); - this.ctrlSplitContainerTop.SplitterDistance = 927; + this.ctrlSplitContainerTop.SplitterDistance = 924; this.ctrlSplitContainerTop.SplitterWidth = 7; this.ctrlSplitContainerTop.TabIndex = 3; this.ctrlSplitContainerTop.PanelCollapsed += new System.EventHandler(this.ctrlSplitContainerTop_PanelCollapsed); @@ -236,7 +237,7 @@ namespace Mesen.GUI.Debugger this.tlpTop.Name = "tlpTop"; this.tlpTop.RowCount = 1; this.tlpTop.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tlpTop.Size = new System.Drawing.Size(927, 390); + this.tlpTop.Size = new System.Drawing.Size(924, 390); this.tlpTop.TabIndex = 2; // // ctrlDebuggerCode @@ -245,15 +246,16 @@ namespace Mesen.GUI.Debugger this.ctrlDebuggerCode.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlDebuggerCode.Location = new System.Drawing.Point(3, 3); this.ctrlDebuggerCode.Name = "ctrlDebuggerCode"; - this.ctrlDebuggerCode.Size = new System.Drawing.Size(489, 384); + this.ctrlDebuggerCode.Size = new System.Drawing.Size(486, 384); this.ctrlDebuggerCode.TabIndex = 2; + this.ctrlDebuggerCode.OnEditCode += new Mesen.GUI.Debugger.ctrlDebuggerCode.AssemblerEventHandler(this.ctrlDebuggerCode_OnEditCode); this.ctrlDebuggerCode.OnSetNextStatement += new Mesen.GUI.Debugger.ctrlDebuggerCode.AddressEventHandler(this.ctrlDebuggerCode_OnSetNextStatement); this.ctrlDebuggerCode.Enter += new System.EventHandler(this.ctrlDebuggerCode_Enter); // // ctrlConsoleStatus // this.ctrlConsoleStatus.Dock = System.Windows.Forms.DockStyle.Fill; - this.ctrlConsoleStatus.Location = new System.Drawing.Point(495, 0); + this.ctrlConsoleStatus.Location = new System.Drawing.Point(492, 0); this.ctrlConsoleStatus.Margin = new System.Windows.Forms.Padding(0); this.ctrlConsoleStatus.Name = "ctrlConsoleStatus"; this.ctrlConsoleStatus.Size = new System.Drawing.Size(432, 390); @@ -264,11 +266,12 @@ namespace Mesen.GUI.Debugger // this.ctrlDebuggerCodeSplit.Code = null; this.ctrlDebuggerCodeSplit.Dock = System.Windows.Forms.DockStyle.Fill; - this.ctrlDebuggerCodeSplit.Location = new System.Drawing.Point(498, 3); + this.ctrlDebuggerCodeSplit.Location = new System.Drawing.Point(495, 3); this.ctrlDebuggerCodeSplit.Name = "ctrlDebuggerCodeSplit"; this.ctrlDebuggerCodeSplit.Size = new System.Drawing.Size(1, 384); this.ctrlDebuggerCodeSplit.TabIndex = 4; this.ctrlDebuggerCodeSplit.Visible = false; + this.ctrlDebuggerCodeSplit.OnEditCode += new Mesen.GUI.Debugger.ctrlDebuggerCode.AssemblerEventHandler(this.ctrlDebuggerCode_OnEditCode); this.ctrlDebuggerCodeSplit.OnSetNextStatement += new Mesen.GUI.Debugger.ctrlDebuggerCode.AddressEventHandler(this.ctrlDebuggerCode_OnSetNextStatement); this.ctrlDebuggerCodeSplit.Enter += new System.EventHandler(this.ctrlDebuggerCodeSplit_Enter); // @@ -285,7 +288,7 @@ namespace Mesen.GUI.Debugger this.tlpFunctionLabelLists.RowCount = 2; this.tlpFunctionLabelLists.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tlpFunctionLabelLists.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); - this.tlpFunctionLabelLists.Size = new System.Drawing.Size(326, 390); + this.tlpFunctionLabelLists.Size = new System.Drawing.Size(329, 390); this.tlpFunctionLabelLists.TabIndex = 5; // // grpLabels @@ -294,7 +297,7 @@ namespace Mesen.GUI.Debugger this.grpLabels.Dock = System.Windows.Forms.DockStyle.Fill; this.grpLabels.Location = new System.Drawing.Point(3, 198); this.grpLabels.Name = "grpLabels"; - this.grpLabels.Size = new System.Drawing.Size(320, 189); + this.grpLabels.Size = new System.Drawing.Size(323, 189); this.grpLabels.TabIndex = 6; this.grpLabels.TabStop = false; this.grpLabels.Text = "Labels"; @@ -304,7 +307,7 @@ namespace Mesen.GUI.Debugger this.ctrlLabelList.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlLabelList.Location = new System.Drawing.Point(3, 16); this.ctrlLabelList.Name = "ctrlLabelList"; - this.ctrlLabelList.Size = new System.Drawing.Size(314, 170); + this.ctrlLabelList.Size = new System.Drawing.Size(317, 170); this.ctrlLabelList.TabIndex = 0; this.ctrlLabelList.OnFindOccurrence += new System.EventHandler(this.ctrlLabelList_OnFindOccurrence); this.ctrlLabelList.OnLabelSelected += new System.EventHandler(this.ctrlLabelList_OnLabelSelected); @@ -315,7 +318,7 @@ namespace Mesen.GUI.Debugger this.grpFunctions.Dock = System.Windows.Forms.DockStyle.Fill; this.grpFunctions.Location = new System.Drawing.Point(3, 3); this.grpFunctions.Name = "grpFunctions"; - this.grpFunctions.Size = new System.Drawing.Size(320, 189); + this.grpFunctions.Size = new System.Drawing.Size(323, 189); this.grpFunctions.TabIndex = 5; this.grpFunctions.TabStop = false; this.grpFunctions.Text = "Functions"; @@ -325,7 +328,7 @@ namespace Mesen.GUI.Debugger this.ctrlFunctionList.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlFunctionList.Location = new System.Drawing.Point(3, 16); this.ctrlFunctionList.Name = "ctrlFunctionList"; - this.ctrlFunctionList.Size = new System.Drawing.Size(314, 170); + this.ctrlFunctionList.Size = new System.Drawing.Size(317, 170); this.ctrlFunctionList.TabIndex = 0; this.ctrlFunctionList.OnFindOccurrence += new System.EventHandler(this.ctrlFunctionList_OnFindOccurrence); this.ctrlFunctionList.OnFunctionSelected += new System.EventHandler(this.ctrlFunctionList_OnFunctionSelected); @@ -986,6 +989,7 @@ namespace Mesen.GUI.Debugger this.mnuMemoryViewer, this.mnuPpuViewer, this.mnuTraceLogger, + this.mnuAssembler, this.toolStripMenuItem13, this.mnuCodeDataLogger}); this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem"; @@ -1154,6 +1158,13 @@ namespace Mesen.GUI.Debugger this.ctrlCpuMemoryMapping.Text = "ctrlMemoryMapping1"; this.ctrlCpuMemoryMapping.Visible = false; // + // mnuAssembler + // + this.mnuAssembler.Name = "mnuAssembler"; + this.mnuAssembler.Size = new System.Drawing.Size(196, 22); + this.mnuAssembler.Text = "Assembler"; + this.mnuAssembler.Click += new System.EventHandler(this.mnuAssembler_Click); + // // frmDebugger // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -1305,5 +1316,6 @@ namespace Mesen.GUI.Debugger private System.Windows.Forms.ToolStripMenuItem mnuBreakOnBrk; private System.Windows.Forms.ToolStripMenuItem mnuSaveRom; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem14; + private System.Windows.Forms.ToolStripMenuItem mnuAssembler; } } \ No newline at end of file diff --git a/GUI.NET/Debugger/frmDebugger.cs b/GUI.NET/Debugger/frmDebugger.cs index f5f46417..1ea26eb1 100644 --- a/GUI.NET/Debugger/frmDebugger.cs +++ b/GUI.NET/Debugger/frmDebugger.cs @@ -295,10 +295,8 @@ namespace Mesen.GUI.Debugger UpdateDebuggerFlags(); UpdateVectorAddresses(); - if(InteropEmu.DebugIsCodeChanged()) { - _previousCode = InteropEmu.DebugGetCode(); - ctrlDebuggerCode.Code = _previousCode; - } + _previousCode = InteropEmu.DebugGetCode(); + ctrlDebuggerCode.Code = _previousCode; DebugState state = new DebugState(); InteropEmu.DebugGetState(ref state); @@ -358,6 +356,9 @@ namespace Mesen.GUI.Debugger frm.FormClosed += (obj, args) => { this._childForms.Remove((Form)obj); }; + frm.StartPosition = FormStartPosition.Manual; + frm.Left = this.Left + this.Width / 2 - frm.Width / 2; + frm.Top = this.Top + this.Height / 2 - frm.Height / 2; frm.Show(); } @@ -437,6 +438,17 @@ namespace Mesen.GUI.Debugger this.UpdateDebugger(); } + private void ctrlDebuggerCode_OnEditCode(AssemblerEventArgs args) + { + frmAssembler assembler = new frmAssembler(args.Code, args.StartAddress, args.BlockLength); + assembler.FormClosed += (s, e) => { + if(assembler.DialogResult == DialogResult.OK) { + this.UpdateDebugger(false); + } + }; + OpenChildForm(assembler); + } + private void mnuFind_Click(object sender, EventArgs e) { _lastCodeWindow.OpenSearchBox(); @@ -853,5 +865,10 @@ namespace Mesen.GUI.Debugger } } } + + private void mnuAssembler_Click(object sender, EventArgs e) + { + OpenChildForm(new frmAssembler()); + } } } diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index 2bacbe10..b9a6472e 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -375,6 +375,12 @@ ctrlWatch.cs + + Form + + + frmAssembler.cs + Form @@ -722,6 +728,9 @@ ctrlWatch.cs + + frmAssembler.cs + frmCodeTooltip.cs @@ -938,6 +947,7 @@ Always + diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 54f2f724..8439389a 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -182,7 +182,6 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void DebugStepOver(); [DllImport(DLLPath)] public static extern void DebugRun(); [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugIsExecutionStopped(); - [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool DebugIsCodeChanged(); [DllImport(DLLPath)] public static extern Int32 DebugGetRelativeAddress(UInt32 absoluteAddr, AddressType type); [DllImport(DLLPath)] public static extern Int32 DebugGetAbsoluteAddress(UInt32 relativeAddr); [DllImport(DLLPath)] public static extern Int32 DebugGetMemorySize(DebugMemoryType type); @@ -214,6 +213,26 @@ namespace Mesen.GUI [DllImport(DLLPath, EntryPoint = "DebugGetCode")] private static extern IntPtr DebugGetCodeWrapper(); public static string DebugGetCode() { return PtrToStringUtf8(InteropEmu.DebugGetCodeWrapper()); } + [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) + { + code = code.Replace(Environment.NewLine, "\n"); + int lineCount = code.Count(c => c == '\n'); + + Int16[] assembledCode = new Int16[(lineCount + 1) * 4]; + UInt32 size = 0; + + GCHandle hAssembledCode = GCHandle.Alloc(assembledCode, GCHandleType.Pinned); + try { + size = InteropEmu.DebugAssembleCodeWrapper(code, startAddress, hAssembledCode.AddrOfPinnedObject()); + } finally { + hAssembledCode.Free(); + } + + Array.Resize(ref assembledCode, (int)size); + return assembledCode; + } + [DllImport(DLLPath, EntryPoint="DebugGetMemoryState")] private static extern UInt32 DebugGetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer); public static byte[] DebugGetMemoryState(DebugMemoryType type) { diff --git a/GUI.NET/Properties/Resources.Designer.cs b/GUI.NET/Properties/Resources.Designer.cs index 764620d0..63f48c00 100644 --- a/GUI.NET/Properties/Resources.Designer.cs +++ b/GUI.NET/Properties/Resources.Designer.cs @@ -200,6 +200,16 @@ namespace Mesen.GUI.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Edit { + get { + object obj = ResourceManager.GetObject("Edit", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/GUI.NET/Properties/Resources.resx b/GUI.NET/Properties/Resources.resx index 9008c908..a52dae3c 100644 --- a/GUI.NET/Properties/Resources.resx +++ b/GUI.NET/Properties/Resources.resx @@ -256,4 +256,7 @@ ..\Resources\pipette.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\Edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/GUI.NET/Resources/Edit.png b/GUI.NET/Resources/Edit.png new file mode 100644 index 00000000..c86f0325 Binary files /dev/null and b/GUI.NET/Resources/Edit.png differ diff --git a/InteropDLL/DebugWrapper.cpp b/InteropDLL/DebugWrapper.cpp index 61818092..30fa174f 100644 --- a/InteropDLL/DebugWrapper.cpp +++ b/InteropDLL/DebugWrapper.cpp @@ -6,6 +6,7 @@ #include "../Core/MemoryDumper.h" #include "../Core/MemoryAccessCounter.h" #include "../Core/Profiler.h" +#include "../Core/Assembler.h" shared_ptr GetDebugger() { @@ -45,8 +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 bool __stdcall DebugIsCodeChanged() { return GetDebugger()->IsCodeChanged(); } - DllExport const char* __stdcall DebugGetCode() { return GetDebugger()->GetCode()->c_str(); } + DllExport const char* __stdcall DebugGetCode() { return GetDebugger()->GetCode(); } DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(scanline, cycle); } @@ -94,5 +94,7 @@ extern "C" DllExport uint32_t __stdcall DebugGetPpuScroll() { return GetDebugger()->GetPpuScroll(); } + DllExport uint32_t __stdcall DebugAssembleCode(char* code, uint16_t startAddress, int16_t* assembledOutput) { return GetDebugger()->GetAssembler()->AssembleCode(code, startAddress, assembledOutput); } + DllExport void __stdcall DebugSaveRomToDisk(char* filename) { GetDebugger()->SaveRomToDisk(filename); } };