Debugger: Added assembler tool + allow editing of existing code by using assembler (wip)

This commit is contained in:
Souryo 2017-03-07 17:51:14 -05:00
parent acbd7f183d
commit 09347d85b7
22 changed files with 1338 additions and 89 deletions

2
.gitignore vendored
View file

@ -164,3 +164,5 @@ $RECYCLE.BIN/
*.VC.opendb
*.VC.db
*.VC.db-wal
*.VC.db-shm

368
Core/Assembler.cpp Normal file
View file

@ -0,0 +1,368 @@
#include "stdafx.h"
#include <regex>
#include <unordered_map>
#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<int16_t>& output, std::unordered_map<string, uint16_t> &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<string, uint16_t> &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<string, uint16_t> &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<int16_t>& 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 = labelManager;
}
uint32_t Assembler::AssembleCode(string code, uint16_t startAddress, int16_t * assembledCode)
{
std::unordered_map<string, uint16_t> temporaryLabels;
size_t i = 0;
vector<int16_t> 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();
}

51
Core/Assembler.h Normal file
View file

@ -0,0 +1,51 @@
#pragma once
#include "stdafx.h"
#include <regex>
#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> _labelManager;
void ProcessLine(string code, uint16_t &instructionAddress, vector<int16_t>& output, std::unordered_map<string, uint16_t> &labels);
AssemblerSpecialCodes GetLineData(std::smatch match, LineData &lineData, std::unordered_map<string, uint16_t> &labels);
AssemblerSpecialCodes GetAddrModeAndOperandSize(LineData &lineData, std::unordered_map<string, uint16_t> &labels);
void AssembleInstruction(LineData &lineData, uint16_t &instructionAddress, vector<int16_t>& output);
public:
Assembler(shared_ptr<LabelManager> labelManager);
uint32_t AssembleCode(string code, uint16_t startAddress, int16_t* assembledCode);
};

View file

@ -409,6 +409,7 @@
<ClInclude Include="ActionEnterprises.h" />
<ClInclude Include="APU.h" />
<ClInclude Include="ArkanoidController.h" />
<ClInclude Include="Assembler.h" />
<ClInclude Include="AutoRomTest.h" />
<ClInclude Include="AutoSaveManager.h" />
<ClInclude Include="AviRecorder.h" />
@ -753,6 +754,7 @@
<ClCompile Include="APU.cpp" />
<ClCompile Include="ApuLengthCounter.cpp" />
<ClCompile Include="ArkanoidController.cpp" />
<ClCompile Include="Assembler.cpp" />
<ClCompile Include="AutoRomTest.cpp" />
<ClCompile Include="AutoSaveManager.cpp" />
<ClCompile Include="AviRecorder.cpp" />

View file

@ -1123,6 +1123,9 @@
<ClInclude Include="CpuState.h">
<Filter>Nes</Filter>
</ClInclude>
<ClInclude Include="Assembler.h">
<Filter>Debugger</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -1317,5 +1320,8 @@
<ClCompile Include="AviRecorder.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="Assembler.cpp">
<Filter>Debugger</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -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> console, shared_ptr<CPU> 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<Assembler> Debugger::GetAssembler()
{
return _assembler;
}
shared_ptr<TraceLogger> Debugger::GetTraceLogger()
{
return _traceLogger;

View file

@ -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> _disassembler;
shared_ptr<Assembler> _assembler;
shared_ptr<MemoryDumper> _memoryDumper;
shared_ptr<CodeDataLogger> _codeDataLogger;
shared_ptr<MemoryAccessCounter> _memoryAccessCounter;
@ -77,7 +79,7 @@ private:
uint32_t _flags;
string _romName;
string _outputCache;
string _disassemblerOutput;
atomic<int32_t> _stepCount;
atomic<int32_t> _ppuStepCount;
atomic<int32_t> _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<Profiler> GetProfiler();
shared_ptr<Assembler> GetAssembler();
shared_ptr<TraceLogger> GetTraceLogger();
shared_ptr<MemoryDumper> GetMemoryDumper();
shared_ptr<MemoryAccessCounter> GetMemoryAccessCounter();

View file

@ -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;
}
}

View file

@ -1,4 +1,4 @@
#include "stdafx.h"
#include "stdafx.h"
#include <algorithm>
#include "MessageManager.h"

View file

@ -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;
}
}

View file

@ -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<int> _unexecutedAddresses = new HashSet<int>();
@ -164,6 +166,46 @@ namespace Mesen.GUI.Debugger
this.ctrlCodeViewer.EndUpdate();
}
Dictionary<int, Tuple<string, string>> _codeContent = new Dictionary<int, Tuple<string, string>>();
Dictionary<int, int> _codeByteContentLength = new Dictionary<int, int>();
public List<string> GetCode(out int byteLength, int startAddress, int endAddress = -1)
{
List<string> result = new List<string>();
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<string, string> 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<string> lineNumberNotes = new List<string>();
List<string> codeNotes = new List<string>();
List<string> codeLines = new List<string>();
_codeContent = new Dictionary<int, Tuple<string, string>>();
_codeByteContentLength = new Dictionary<int, int>();
_unexecutedAddresses = new HashSet<int>();
_speculativeCodeAddreses = new HashSet<int>();
@ -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<string, string>(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<string> 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; }
}
}

320
GUI.NET/Debugger/frmAssembler.Designer.cs generated Normal file
View file

@ -0,0 +1,320 @@
namespace Mesen.GUI.Debugger
{
partial class frmAssembler
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View file

@ -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<byte> convertedByteCode = new List<byte>();
List<string> errorList = new List<string>();
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<string> warningMessages = new List<string>();
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<byte> bytes = new List<byte>(((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,
}
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -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;
}
}

View file

@ -295,10 +295,8 @@ namespace Mesen.GUI.Debugger
UpdateDebuggerFlags();
UpdateVectorAddresses();
if(InteropEmu.DebugIsCodeChanged()) {
_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());
}
}
}

View file

@ -375,6 +375,12 @@
<DependentUpon>ctrlWatch.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\DbgImporter.cs" />
<Compile Include="Debugger\frmAssembler.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\frmAssembler.Designer.cs">
<DependentUpon>frmAssembler.cs</DependentUpon>
</Compile>
<Compile Include="Debugger\frmCodeTooltip.cs">
<SubType>Form</SubType>
</Compile>
@ -722,6 +728,9 @@
<EmbeddedResource Include="Debugger\Controls\ctrlWatch.resx">
<DependentUpon>ctrlWatch.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\frmAssembler.resx">
<DependentUpon>frmAssembler.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\frmCodeTooltip.resx">
<DependentUpon>frmCodeTooltip.cs</DependentUpon>
</EmbeddedResource>
@ -938,6 +947,7 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Icon.ico" />
<None Include="Resources\Edit.png" />
<None Include="Resources\pencil.png" />
<None Include="Resources\pipette.png" />
<None Include="Resources\VideoRecorder.png" />

View file

@ -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)
{

View file

@ -200,6 +200,16 @@ namespace Mesen.GUI.Properties {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap Edit {
get {
object obj = ResourceManager.GetObject("Edit", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>

View file

@ -256,4 +256,7 @@
<data name="Pipette" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\pipette.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

BIN
GUI.NET/Resources/Edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

View file

@ -6,6 +6,7 @@
#include "../Core/MemoryDumper.h"
#include "../Core/MemoryAccessCounter.h"
#include "../Core/Profiler.h"
#include "../Core/Assembler.h"
shared_ptr<Debugger> 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); }
};