Debugger: Improved CA/CC65 integration
-Auto-detect all function entry points -Auto-detect all jump targets -Fixed a bug where jump targets were being disassembled when loading a CDL file, potentially causing data to be shown as code -Fixed some valid data block being shown as unidentified data
This commit is contained in:
parent
a3a91ede59
commit
2c9d6aabd7
14 changed files with 277 additions and 150 deletions
|
@ -151,6 +151,11 @@ bool CodeDataLogger::IsCode(uint32_t absoluteAddr)
|
||||||
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::Code) == (uint8_t)CdlPrgFlags::Code;
|
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::Code) == (uint8_t)CdlPrgFlags::Code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CodeDataLogger::IsJumpTarget(uint32_t absoluteAddr)
|
||||||
|
{
|
||||||
|
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::JumpTarget) == (uint8_t)CdlPrgFlags::JumpTarget;
|
||||||
|
}
|
||||||
|
|
||||||
bool CodeDataLogger::IsSubEntryPoint(uint32_t absoluteAddr)
|
bool CodeDataLogger::IsSubEntryPoint(uint32_t absoluteAddr)
|
||||||
{
|
{
|
||||||
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::SubEntryPoint) == (uint8_t)CdlPrgFlags::SubEntryPoint;
|
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::SubEntryPoint) == (uint8_t)CdlPrgFlags::SubEntryPoint;
|
||||||
|
|
|
@ -11,9 +11,16 @@ enum class CdlPrgFlags
|
||||||
None = 0x00,
|
None = 0x00,
|
||||||
Code = 0x01,
|
Code = 0x01,
|
||||||
Data = 0x02,
|
Data = 0x02,
|
||||||
IndirectCode = 0x10,
|
|
||||||
|
//Bit 0x10 is used for "indirectly accessed as code" in FCEUX
|
||||||
|
//Repurposed to mean the address is the target of a jump instruction
|
||||||
|
JumpTarget = 0x10,
|
||||||
|
|
||||||
IndirectData = 0x20,
|
IndirectData = 0x20,
|
||||||
PcmData = 0x40,
|
PcmData = 0x40,
|
||||||
|
|
||||||
|
//Unused bit in original CDL spec
|
||||||
|
//Used to denote that the byte is the start of function (sub)
|
||||||
SubEntryPoint = 0x80
|
SubEntryPoint = 0x80
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,6 +81,7 @@ public:
|
||||||
CdlRatios GetRatios();
|
CdlRatios GetRatios();
|
||||||
|
|
||||||
bool IsCode(uint32_t absoluteAddr);
|
bool IsCode(uint32_t absoluteAddr);
|
||||||
|
bool IsJumpTarget(uint32_t absoluteAddr);
|
||||||
bool IsSubEntryPoint(uint32_t absoluteAddr);
|
bool IsSubEntryPoint(uint32_t absoluteAddr);
|
||||||
bool IsData(uint32_t absoluteAddr);
|
bool IsData(uint32_t absoluteAddr);
|
||||||
bool IsRead(uint32_t absoluteAddr);
|
bool IsRead(uint32_t absoluteAddr);
|
||||||
|
|
|
@ -44,5 +44,5 @@ void CodeRunner::WriteRAM(uint16_t addr, uint8_t value)
|
||||||
|
|
||||||
DisassemblyInfo CodeRunner::GetDisassemblyInfo(uint16_t cpuAddress)
|
DisassemblyInfo CodeRunner::GetDisassemblyInfo(uint16_t cpuAddress)
|
||||||
{
|
{
|
||||||
return DisassemblyInfo(_byteCode.data() + cpuAddress - CodeRunner::BaseAddress, false, false);
|
return DisassemblyInfo(_byteCode.data() + cpuAddress - CodeRunner::BaseAddress, false);
|
||||||
}
|
}
|
|
@ -765,14 +765,18 @@ bool Debugger::ProcessRamOperation(MemoryOperationType type, uint16_t &addr, uin
|
||||||
|
|
||||||
bool isSubEntryPoint = _lastInstruction == 0x20; //Previous instruction was a JSR
|
bool isSubEntryPoint = _lastInstruction == 0x20; //Previous instruction was a JSR
|
||||||
if(absoluteAddr >= 0) {
|
if(absoluteAddr >= 0) {
|
||||||
|
bool isJumpTarget = _disassembler->IsJump(_lastInstruction);
|
||||||
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::Code);
|
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::Code);
|
||||||
if(isSubEntryPoint) {
|
if(isSubEntryPoint) {
|
||||||
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::SubEntryPoint);
|
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::SubEntryPoint);
|
||||||
_functionEntryPoints.emplace(absoluteAddr);
|
_functionEntryPoints.emplace(absoluteAddr);
|
||||||
|
} else if(isJumpTarget) {
|
||||||
|
//Only mark as jump target if not marked as sub entry point
|
||||||
|
_codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::JumpTarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_disassembler->BuildCache(addressInfo, addr, isSubEntryPoint, false);
|
_disassembler->BuildCache(addressInfo, addr, isSubEntryPoint, true);
|
||||||
|
|
||||||
ProcessStepConditions(addr);
|
ProcessStepConditions(addr);
|
||||||
|
|
||||||
|
@ -1142,11 +1146,6 @@ const char* Debugger::GetCode(uint32_t &length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::GetJumpTargets(bool* jumpTargets)
|
|
||||||
{
|
|
||||||
_disassembler->GetJumpTargets(jumpTargets);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t Debugger::GetRelativeAddress(uint32_t addr, AddressType type)
|
int32_t Debugger::GetRelativeAddress(uint32_t addr, AddressType type)
|
||||||
{
|
{
|
||||||
switch(type) {
|
switch(type) {
|
||||||
|
|
|
@ -170,8 +170,6 @@ public:
|
||||||
|
|
||||||
void SetBreakpoints(Breakpoint breakpoints[], uint32_t length);
|
void SetBreakpoints(Breakpoint breakpoints[], uint32_t length);
|
||||||
|
|
||||||
void ProcessMarkedBreakpoints(BreakpointType type, OperationInfo &operationInfo);
|
|
||||||
|
|
||||||
shared_ptr<LabelManager> GetLabelManager();
|
shared_ptr<LabelManager> GetLabelManager();
|
||||||
|
|
||||||
void GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount);
|
void GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount);
|
||||||
|
@ -222,8 +220,6 @@ public:
|
||||||
void GenerateCodeOutput();
|
void GenerateCodeOutput();
|
||||||
const char* GetCode(uint32_t &length);
|
const char* GetCode(uint32_t &length);
|
||||||
|
|
||||||
void GetJumpTargets(bool* jumpTargets);
|
|
||||||
|
|
||||||
int32_t GetRelativeAddress(uint32_t addr, AddressType type);
|
int32_t GetRelativeAddress(uint32_t addr, AddressType type);
|
||||||
int32_t GetAbsoluteAddress(uint32_t addr);
|
int32_t GetAbsoluteAddress(uint32_t addr);
|
||||||
int32_t GetAbsoluteChrAddress(uint32_t addr);
|
int32_t GetAbsoluteChrAddress(uint32_t addr);
|
||||||
|
|
|
@ -146,12 +146,30 @@ bool Disassembler::IsUnofficialOpCode(uint8_t opCode)
|
||||||
|
|
||||||
bool Disassembler::IsJump(uint8_t opCode)
|
bool Disassembler::IsJump(uint8_t opCode)
|
||||||
{
|
{
|
||||||
return opCode == 0x10 || opCode == 0x30|| opCode == 0x50 || opCode == 0x70 || opCode == 0x90 || opCode == 0xB0 || opCode == 0xD0 || opCode == 0xF0 || opCode == 0x4C || opCode == 0x20;
|
return (
|
||||||
|
opCode == 0x10 || //BPL
|
||||||
|
opCode == 0x30 || //BMI
|
||||||
|
opCode == 0x50 || //BVC
|
||||||
|
opCode == 0x70 || //BVS
|
||||||
|
opCode == 0x90 || //BCC
|
||||||
|
opCode == 0xB0 || //BCS
|
||||||
|
opCode == 0xD0 || //BNE
|
||||||
|
opCode == 0xF0 || //BEQ
|
||||||
|
opCode == 0x4C || //JMP (Absolute)
|
||||||
|
opCode == 0x6C || //JMP (Indirect)
|
||||||
|
opCode == 0x20 //JSR
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Disassembler::IsUnconditionalJump(uint8_t opCode)
|
bool Disassembler::IsUnconditionalJump(uint8_t opCode)
|
||||||
{
|
{
|
||||||
return opCode == 0x40 || opCode == 0x60 || opCode == 0x6C || opCode == 0x4C || opCode == 0x20;
|
return (
|
||||||
|
opCode == 0x40 || //RTI
|
||||||
|
opCode == 0x60 || //RTS
|
||||||
|
opCode == 0x6C || //JMP (Indirect)
|
||||||
|
opCode == 0x4C || //JMP (Absolute)
|
||||||
|
opCode == 0x20 //JSR
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Disassembler::GetInfo(AddressTypeInfo &info, uint8_t** source, uint32_t &size, vector<shared_ptr<DisassemblyInfo>> **cache)
|
void Disassembler::GetInfo(AddressTypeInfo &info, uint8_t** source, uint32_t &size, vector<shared_ptr<DisassemblyInfo>> **cache)
|
||||||
|
@ -188,7 +206,7 @@ void Disassembler::GetInfo(AddressTypeInfo &info, uint8_t** source, uint32_t &si
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bool isSubEntryPoint, bool isJumpTarget)
|
uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bool isSubEntryPoint, bool processJumps)
|
||||||
{
|
{
|
||||||
uint32_t mask = info.Type == AddressType::InternalRam ? 0x7FF : 0xFFFFFFFF;
|
uint32_t mask = info.Type == AddressType::InternalRam ? 0x7FF : 0xFFFFFFFF;
|
||||||
|
|
||||||
|
@ -203,9 +221,8 @@ uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bo
|
||||||
if(!disInfo) {
|
if(!disInfo) {
|
||||||
while(absoluteAddr < (int32_t)size && !(*cache)[absoluteAddr]) {
|
while(absoluteAddr < (int32_t)size && !(*cache)[absoluteAddr]) {
|
||||||
bool isJump = IsUnconditionalJump(source[absoluteAddr]);
|
bool isJump = IsUnconditionalJump(source[absoluteAddr]);
|
||||||
disInfo = new DisassemblyInfo(source+absoluteAddr, isSubEntryPoint, isJumpTarget);
|
disInfo = new DisassemblyInfo(source+absoluteAddr, isSubEntryPoint);
|
||||||
isSubEntryPoint = false;
|
isSubEntryPoint = false;
|
||||||
isJumpTarget = false;
|
|
||||||
|
|
||||||
(*cache)[absoluteAddr] = shared_ptr<DisassemblyInfo>(disInfo);
|
(*cache)[absoluteAddr] = shared_ptr<DisassemblyInfo>(disInfo);
|
||||||
|
|
||||||
|
@ -219,22 +236,17 @@ uint32_t Disassembler::BuildCache(AddressTypeInfo &info, uint16_t cpuAddress, bo
|
||||||
if(isSubEntryPoint) {
|
if(isSubEntryPoint) {
|
||||||
disInfo->SetSubEntryPoint();
|
disInfo->SetSubEntryPoint();
|
||||||
}
|
}
|
||||||
if(isJumpTarget) {
|
|
||||||
disInfo->SetJumpTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t opCode = source[absoluteAddr];
|
uint8_t opCode = source[absoluteAddr];
|
||||||
if(IsJump(opCode)) {
|
if(processJumps && IsJump(opCode)) {
|
||||||
uint16_t jumpDest = disInfo->GetOpAddr(cpuAddress);
|
uint16_t jumpDest = disInfo->GetJumpDestination(cpuAddress, _memoryManager);
|
||||||
if(jumpDest != cpuAddress) {
|
if(jumpDest != cpuAddress) {
|
||||||
AddressTypeInfo addressInfo;
|
AddressTypeInfo addressInfo;
|
||||||
_debugger->GetAbsoluteAddressAndType(jumpDest, &addressInfo);
|
_debugger->GetAbsoluteAddressAndType(jumpDest, &addressInfo);
|
||||||
|
|
||||||
const uint8_t jsrCode = 0x20;
|
constexpr uint8_t jsrCode = 0x20;
|
||||||
if(addressInfo.Address >= 0) {
|
if(addressInfo.Address >= 0) {
|
||||||
BuildCache(addressInfo, jumpDest, opCode == jsrCode, true);
|
BuildCache(addressInfo, jumpDest, opCode == jsrCode, false);
|
||||||
} else {
|
|
||||||
disInfo->SetJumpTarget();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -539,7 +551,7 @@ string Disassembler::GetCode(AddressTypeInfo &addressInfo, uint32_t endAddr, uin
|
||||||
isVerifiedData = addressInfo.Type == AddressType::PrgRom && cdl->IsData(addr&mask);
|
isVerifiedData = addressInfo.Type == AddressType::PrgRom && cdl->IsData(addr&mask);
|
||||||
if(!info && ((disassembleUnidentifiedData && !isVerifiedData) || (disassembleVerifiedData && isVerifiedData))) {
|
if(!info && ((disassembleUnidentifiedData && !isVerifiedData) || (disassembleVerifiedData && isVerifiedData))) {
|
||||||
dataType = isVerifiedData ? DataType::VerifiedData : DataType::UnidentifiedData;
|
dataType = isVerifiedData ? DataType::VerifiedData : DataType::UnidentifiedData;
|
||||||
tmpInfo->Initialize(source + (addr & mask), false, false);
|
tmpInfo->Initialize(source + (addr & mask), false);
|
||||||
info = tmpInfo;
|
info = tmpInfo;
|
||||||
} else if(info) {
|
} else if(info) {
|
||||||
dataType = DataType::VerifiedCode;
|
dataType = DataType::VerifiedCode;
|
||||||
|
@ -686,14 +698,3 @@ DisassemblyInfo Disassembler::GetDisassemblyInfo(AddressTypeInfo &info)
|
||||||
return DisassemblyInfo();
|
return DisassemblyInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Disassembler::GetJumpTargets(bool* jumpTargets)
|
|
||||||
{
|
|
||||||
memset(jumpTargets, 0, _disassembleCache.size());
|
|
||||||
for(size_t i = 0, len = _disassembleCache.size(); i < len; i++) {
|
|
||||||
shared_ptr<DisassemblyInfo> disInfo = _disassembleCache[i];
|
|
||||||
if(disInfo && disInfo->IsJumpTarget()) {
|
|
||||||
jumpTargets[i] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,8 +28,6 @@ private:
|
||||||
vector<shared_ptr<DisassemblyInfo>> _disassembleSaveRamCache;
|
vector<shared_ptr<DisassemblyInfo>> _disassembleSaveRamCache;
|
||||||
vector<shared_ptr<DisassemblyInfo>> _disassembleMemoryCache;
|
vector<shared_ptr<DisassemblyInfo>> _disassembleMemoryCache;
|
||||||
|
|
||||||
bool IsJump(uint8_t opCode);
|
|
||||||
bool IsUnconditionalJump(uint8_t opCode);
|
|
||||||
void GetLine(string &out, string code = "", string comment = string(), int32_t cpuAddress = -1, int32_t absoluteAddress = -1, DataType dataType = DataType::VerifiedCode, char memoryType = ' ');
|
void GetLine(string &out, string code = "", string comment = string(), int32_t cpuAddress = -1, int32_t absoluteAddress = -1, DataType dataType = DataType::VerifiedCode, char memoryType = ' ');
|
||||||
void GetCodeLine(string &out, string &code, string &comment, int32_t cpuAddress, int32_t absoluteAddress, string &byteCode, string &addressing, DataType dataType, bool isIndented, char memoryType = ' ');
|
void GetCodeLine(string &out, string &code, string &comment, int32_t cpuAddress, int32_t absoluteAddress, string &byteCode, string &addressing, DataType dataType, bool isIndented, char memoryType = ' ');
|
||||||
void GetSubHeader(string &out, DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector);
|
void GetSubHeader(string &out, DisassemblyInfo *info, string &label, uint16_t relativeAddr, uint16_t resetVector, uint16_t nmiVector, uint16_t irqVector);
|
||||||
|
@ -43,16 +41,16 @@ public:
|
||||||
void BuildOpCodeTables(bool useLowerCase);
|
void BuildOpCodeTables(bool useLowerCase);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
uint32_t BuildCache(AddressTypeInfo &info, uint16_t memoryAddr, bool isSubEntryPoint, bool isJumpTarget);
|
uint32_t BuildCache(AddressTypeInfo &info, uint16_t memoryAddr, bool isSubEntryPoint, bool processJumps);
|
||||||
void InvalidateCache(AddressTypeInfo &info);
|
void InvalidateCache(AddressTypeInfo &info);
|
||||||
|
|
||||||
bool IsUnofficialOpCode(uint8_t opCode);
|
bool IsUnofficialOpCode(uint8_t opCode);
|
||||||
|
bool IsJump(uint8_t opCode);
|
||||||
|
bool IsUnconditionalJump(uint8_t opCode);
|
||||||
|
|
||||||
string GetCode(AddressTypeInfo &addressInfo, uint32_t endAddr, uint16_t memoryAddr, State& cpuState, shared_ptr<MemoryManager> memoryManager, shared_ptr<LabelManager> labelManager);
|
string GetCode(AddressTypeInfo &addressInfo, uint32_t endAddr, uint16_t memoryAddr, State& cpuState, shared_ptr<MemoryManager> memoryManager, shared_ptr<LabelManager> labelManager);
|
||||||
|
|
||||||
DisassemblyInfo GetDisassemblyInfo(AddressTypeInfo &info);
|
DisassemblyInfo GetDisassemblyInfo(AddressTypeInfo &info);
|
||||||
|
|
||||||
void GetJumpTargets(bool* jumpTargets);
|
|
||||||
|
|
||||||
void RebuildPrgRomCache(uint32_t absoluteAddr, int32_t length);
|
void RebuildPrgRomCache(uint32_t absoluteAddr, int32_t length);
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,9 +33,9 @@ DisassemblyInfo::DisassemblyInfo()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint, bool isJumpTarget)
|
DisassemblyInfo::DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint)
|
||||||
{
|
{
|
||||||
Initialize(opPointer, isSubEntryPoint, isJumpTarget);
|
Initialize(opPointer, isSubEntryPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblyInfo::ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage)
|
void DisassemblyInfo::ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage)
|
||||||
|
@ -148,10 +148,9 @@ uint16_t DisassemblyInfo::GetOpAddr(uint16_t memoryAddr)
|
||||||
return opAddr;
|
return opAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblyInfo::Initialize(uint8_t* opPointer, bool isSubEntryPoint, bool isJumpTarget)
|
void DisassemblyInfo::Initialize(uint8_t* opPointer, bool isSubEntryPoint)
|
||||||
{
|
{
|
||||||
_isSubEntryPoint = isSubEntryPoint;
|
_isSubEntryPoint = isSubEntryPoint;
|
||||||
_isJumpTarget = isJumpTarget;
|
|
||||||
|
|
||||||
uint8_t opCode = *opPointer;
|
uint8_t opCode = *opPointer;
|
||||||
_opSize = DisassemblyInfo::OPSize[opCode];
|
_opSize = DisassemblyInfo::OPSize[opCode];
|
||||||
|
@ -169,11 +168,6 @@ void DisassemblyInfo::SetSubEntryPoint()
|
||||||
_isSubEntryPoint = true;
|
_isSubEntryPoint = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisassemblyInfo::SetJumpTarget()
|
|
||||||
{
|
|
||||||
_isJumpTarget = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t DisassemblyInfo::GetMemoryValue(State& cpuState, MemoryManager* memoryManager)
|
int32_t DisassemblyInfo::GetMemoryValue(State& cpuState, MemoryManager* memoryManager)
|
||||||
{
|
{
|
||||||
int32_t address = -1;
|
int32_t address = -1;
|
||||||
|
@ -192,6 +186,22 @@ int32_t DisassemblyInfo::GetMemoryValue(State& cpuState, MemoryManager* memoryMa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t DisassemblyInfo::GetJumpDestination(uint16_t pc, MemoryManager* memoryManager)
|
||||||
|
{
|
||||||
|
int32_t address = -1;
|
||||||
|
if(_opMode == AddrMode::Rel || _opMode == AddrMode::Abs) {
|
||||||
|
address = GetOpAddr(pc);
|
||||||
|
} else if(_opMode == AddrMode::Ind) {
|
||||||
|
address = GetIndirectJumpDestination(memoryManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(address >= 0 && address <= 0xFFFF) {
|
||||||
|
return memoryManager->DebugRead(address);
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t DisassemblyInfo::GetIndirectJumpDestination(MemoryManager* memoryManager)
|
uint16_t DisassemblyInfo::GetIndirectJumpDestination(MemoryManager* memoryManager)
|
||||||
{
|
{
|
||||||
uint16_t addr = _byteCode[1] | (_byteCode[2] << 8);
|
uint16_t addr = _byteCode[1] | (_byteCode[2] << 8);
|
||||||
|
@ -319,8 +329,3 @@ bool DisassemblyInfo::IsSubExitPoint()
|
||||||
{
|
{
|
||||||
return _isSubExitPoint;
|
return _isSubExitPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DisassemblyInfo::IsJumpTarget()
|
|
||||||
{
|
|
||||||
return _isJumpTarget;
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t _byteCode[3];
|
uint8_t _byteCode[3];
|
||||||
bool _isJumpTarget = false;
|
|
||||||
bool _isSubEntryPoint = false;
|
bool _isSubEntryPoint = false;
|
||||||
bool _isSubExitPoint = false;
|
bool _isSubExitPoint = false;
|
||||||
uint32_t _opSize = 0;
|
uint32_t _opSize = 0;
|
||||||
|
@ -25,18 +24,17 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DisassemblyInfo();
|
DisassemblyInfo();
|
||||||
DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint, bool isJumpTarget);
|
DisassemblyInfo(uint8_t* opPointer, bool isSubEntryPoint);
|
||||||
|
|
||||||
void Initialize(uint8_t * opPointer, bool isSubEntryPoint, bool isJumpTarget);
|
void Initialize(uint8_t * opPointer, bool isSubEntryPoint);
|
||||||
|
|
||||||
void SetSubEntryPoint();
|
void SetSubEntryPoint();
|
||||||
|
|
||||||
void SetJumpTarget();
|
|
||||||
|
|
||||||
int32_t GetEffectiveAddress(State& cpuState, MemoryManager* memoryManager);
|
int32_t GetEffectiveAddress(State& cpuState, MemoryManager* memoryManager);
|
||||||
|
|
||||||
void GetEffectiveAddressString(string &out, State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager);
|
void GetEffectiveAddressString(string &out, State& cpuState, MemoryManager* memoryManager, LabelManager* labelManager);
|
||||||
int32_t GetMemoryValue(State& cpuState, MemoryManager* memoryManager);
|
int32_t GetMemoryValue(State& cpuState, MemoryManager* memoryManager);
|
||||||
|
int32_t GetJumpDestination(uint16_t pc, MemoryManager* memoryManager);
|
||||||
uint16_t GetIndirectJumpDestination(MemoryManager* memoryManager);
|
uint16_t GetIndirectJumpDestination(MemoryManager* memoryManager);
|
||||||
void ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage);
|
void ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, LabelManager* labelManager, bool extendZeroPage);
|
||||||
void GetByteCode(string &out);
|
void GetByteCode(string &out);
|
||||||
|
@ -45,6 +43,5 @@ public:
|
||||||
|
|
||||||
bool IsSubEntryPoint();
|
bool IsSubEntryPoint();
|
||||||
bool IsSubExitPoint();
|
bool IsSubExitPoint();
|
||||||
bool IsJumpTarget();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace Mesen.GUI.Debugger
|
||||||
|
|
||||||
private static Regex _segmentRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+)", RegexOptions.Compiled);
|
private static Regex _segmentRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+)", RegexOptions.Compiled);
|
||||||
private static Regex _segmentPrgRomRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+),.*ooffs=([0-9]+)", RegexOptions.Compiled);
|
private static Regex _segmentPrgRomRegex = new Regex("^seg\tid=([0-9]+),.*start=0x([0-9a-fA-F]+),.*size=0x([0-9A-Fa-f]+),.*ooffs=([0-9]+)", RegexOptions.Compiled);
|
||||||
private static Regex _lineRegex = new Regex("^line\tid=([0-9]+),.*file=([0-9]+),.*line=([0-9]+)(,.*type=([0-9]+)){0,1}(,.*span=([0-9]+)){0,1}", RegexOptions.Compiled);
|
private static Regex _lineRegex = new Regex("^line\tid=([0-9]+),.*file=([0-9]+),.*line=([0-9]+)(,.*type=([0-9]+)){0,1}(,.*span=([0-9+]+)){0,1}", RegexOptions.Compiled);
|
||||||
private static Regex _fileRegex = new Regex("^file\tid=([0-9]+),.*name=\"([^\"]+)\"", RegexOptions.Compiled);
|
private static Regex _fileRegex = new Regex("^file\tid=([0-9]+),.*name=\"([^\"]+)\"", RegexOptions.Compiled);
|
||||||
private static Regex _spanRegex = new Regex("^span\tid=([0-9]+),.*seg=([0-9]+),.*start=([0-9]+),.*size=([0-9]+)(,.*type=([0-9]+)){0,1}", RegexOptions.Compiled);
|
private static Regex _spanRegex = new Regex("^span\tid=([0-9]+),.*seg=([0-9]+),.*start=([0-9]+),.*size=([0-9]+)(,.*type=([0-9]+)){0,1}", RegexOptions.Compiled);
|
||||||
private static Regex _symbolRegex = new Regex("^sym\tid=([0-9]+).*name=\"([0-9a-zA-Z@_-]+)\"(,.*def=([0-9+]+)){0,1}(,.*ref=([0-9+]+)){0,1}(,.*val=0x([0-9a-fA-F]+)){0,1}(,.*seg=([0-9]+)){0,1}(,.*exp=([0-9]+)){0,1}", RegexOptions.Compiled);
|
private static Regex _symbolRegex = new Regex("^sym\tid=([0-9]+).*name=\"([0-9a-zA-Z@_-]+)\"(,.*def=([0-9+]+)){0,1}(,.*ref=([0-9+]+)){0,1}(,.*val=0x([0-9a-fA-F]+)){0,1}(,.*seg=([0-9]+)){0,1}(,.*exp=([0-9]+)){0,1}", RegexOptions.Compiled);
|
||||||
|
@ -51,6 +51,7 @@ namespace Mesen.GUI.Debugger
|
||||||
private static Regex _cPreviousLinesRegex = new Regex("^\\s*//(.*)", RegexOptions.Compiled);
|
private static Regex _cPreviousLinesRegex = new Regex("^\\s*//(.*)", RegexOptions.Compiled);
|
||||||
|
|
||||||
private Dictionary<int, LineInfo> _linesByPrgAddress = new Dictionary<int, LineInfo>();
|
private Dictionary<int, LineInfo> _linesByPrgAddress = new Dictionary<int, LineInfo>();
|
||||||
|
private Dictionary<int, LineInfo[]> _linesByFile = new Dictionary<int, LineInfo[]>();
|
||||||
private Dictionary<string, int> _prgAddressByLine = new Dictionary<string, int>();
|
private Dictionary<string, int> _prgAddressByLine = new Dictionary<string, int>();
|
||||||
|
|
||||||
public Dictionary<int, FileInfo> Files { get { return _files; } }
|
public Dictionary<int, FileInfo> Files { get { return _files; } }
|
||||||
|
@ -66,6 +67,17 @@ namespace Mesen.GUI.Debugger
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int GetPrgAddress(SpanInfo span)
|
||||||
|
{
|
||||||
|
SegmentInfo segment;
|
||||||
|
if(_segments.TryGetValue(span.SegmentID, out segment)) {
|
||||||
|
if(!segment.IsRam && span.Size != segment.Size) {
|
||||||
|
return span.Offset + segment.FileOffset - _headerSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
public LineInfo GetSourceCodeLineInfo(int prgRomAddress)
|
public LineInfo GetSourceCodeLineInfo(int prgRomAddress)
|
||||||
{
|
{
|
||||||
LineInfo line;
|
LineInfo line;
|
||||||
|
@ -110,15 +122,39 @@ namespace Mesen.GUI.Debugger
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SpanInfo GetSymbolDefinitionSpan(SymbolInfo symbol)
|
||||||
|
{
|
||||||
|
foreach(int definition in symbol.Definitions) {
|
||||||
|
LineInfo definitionLine;
|
||||||
|
FileInfo file;
|
||||||
|
if(_lines.TryGetValue(definition, out definitionLine)) {
|
||||||
|
if(_files.TryGetValue(definitionLine.FileID, out file)) {
|
||||||
|
int lineNumber = definitionLine.LineNumber;
|
||||||
|
while(!(definitionLine?.SpanIDs.Count > 0) && lineNumber < file.Data.Length - 1) {
|
||||||
|
//Definition line contains no code, try the next line
|
||||||
|
lineNumber++;
|
||||||
|
definitionLine = _linesByFile[file.ID][lineNumber];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(definitionLine != null && definitionLine.SpanIDs.Count > 0) {
|
||||||
|
SpanInfo span;
|
||||||
|
if(_spans.TryGetValue(definitionLine.SpanIDs[0], out span)) {
|
||||||
|
return span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private SymbolInfo GetMatchingSymbol(SymbolInfo symbol, int rangeStart, int rangeEnd)
|
private SymbolInfo GetMatchingSymbol(SymbolInfo symbol, int rangeStart, int rangeEnd)
|
||||||
{
|
{
|
||||||
foreach(int reference in symbol.References) {
|
foreach(int reference in symbol.References) {
|
||||||
LineInfo line = _lines[reference];
|
LineInfo line = _lines[reference];
|
||||||
if(line.SpanID == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpanInfo span = _spans[line.SpanID.Value];
|
foreach(int spanID in line.SpanIDs) {
|
||||||
|
SpanInfo span = _spans[spanID];
|
||||||
SegmentInfo seg = _segments[span.SegmentID];
|
SegmentInfo seg = _segments[span.SegmentID];
|
||||||
|
|
||||||
if(!seg.IsRam) {
|
if(!seg.IsRam) {
|
||||||
|
@ -132,6 +168,7 @@ namespace Mesen.GUI.Debugger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,9 +283,23 @@ namespace Mesen.GUI.Debugger
|
||||||
FileID = Int32.Parse(match.Groups[2].Value),
|
FileID = Int32.Parse(match.Groups[2].Value),
|
||||||
LineNumber = Int32.Parse(match.Groups[3].Value) - 1,
|
LineNumber = Int32.Parse(match.Groups[3].Value) - 1,
|
||||||
Type = match.Groups[5].Success ? (LineType)Int32.Parse(match.Groups[5].Value) : LineType.Assembly,
|
Type = match.Groups[5].Success ? (LineType)Int32.Parse(match.Groups[5].Value) : LineType.Assembly,
|
||||||
SpanID = match.Groups[7].Success ? (int?)Int32.Parse(match.Groups[7].Value) : null
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if(line.LineNumber < 0) {
|
||||||
|
line.LineNumber = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(match.Groups[7].Success) {
|
||||||
|
string[] spanIDs = match.Groups[7].Value.Split('+');
|
||||||
|
line.SpanIDs = new List<int>(spanIDs.Length);
|
||||||
|
for(int i = spanIDs.Length - 1; i >= 0; i--) {
|
||||||
|
//Read them backwards to get them in order
|
||||||
|
line.SpanIDs.Add(Int32.Parse(spanIDs[i]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
line.SpanIDs = new List<int>();
|
||||||
|
}
|
||||||
|
|
||||||
_usedFileIds.Add(line.FileID);
|
_usedFileIds.Add(line.FileID);
|
||||||
_lines.Add(line.ID, line);
|
_lines.Add(line.ID, line);
|
||||||
return true;
|
return true;
|
||||||
|
@ -402,12 +453,15 @@ namespace Mesen.GUI.Debugger
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int address = GetSymbolAddressInfo(symbol).Address;
|
AddressTypeInfo addressInfo = GetSymbolAddressInfo(symbol);
|
||||||
|
if(addressInfo != null) {
|
||||||
|
int address = addressInfo.Address;
|
||||||
CodeLabel label = this.CreateLabel(address, segment.IsRam);
|
CodeLabel label = this.CreateLabel(address, segment.IsRam);
|
||||||
if(label != null) {
|
if(label != null) {
|
||||||
label.Label = newName;
|
label.Label = newName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
_errorCount++;
|
_errorCount++;
|
||||||
}
|
}
|
||||||
|
@ -419,11 +473,11 @@ namespace Mesen.GUI.Debugger
|
||||||
foreach(KeyValuePair<int, LineInfo> kvp in _lines) {
|
foreach(KeyValuePair<int, LineInfo> kvp in _lines) {
|
||||||
try {
|
try {
|
||||||
LineInfo line = kvp.Value;
|
LineInfo line = kvp.Value;
|
||||||
if(line.SpanID == null) {
|
if(line.SpanIDs.Count == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpanInfo span = _spans[line.SpanID.Value];
|
SpanInfo span = _spans[line.SpanIDs[0]];
|
||||||
SegmentInfo segment = _segments[span.SegmentID];
|
SegmentInfo segment = _segments[span.SegmentID];
|
||||||
|
|
||||||
if(_files[line.FileID].Data == null) {
|
if(_files[line.FileID].Data == null) {
|
||||||
|
@ -479,6 +533,19 @@ namespace Mesen.GUI.Debugger
|
||||||
|
|
||||||
private void LoadFileData(string path)
|
private void LoadFileData(string path)
|
||||||
{
|
{
|
||||||
|
Dictionary<int, int> maxLineCountByFile = new Dictionary<int, int>();
|
||||||
|
|
||||||
|
foreach(LineInfo line in _lines.Values) {
|
||||||
|
int currentMax;
|
||||||
|
if(maxLineCountByFile.TryGetValue(line.FileID, out currentMax)) {
|
||||||
|
if(currentMax < line.LineNumber) {
|
||||||
|
maxLineCountByFile[line.FileID] = line.LineNumber;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
maxLineCountByFile[line.FileID] = line.LineNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach(FileInfo file in _files.Values) {
|
foreach(FileInfo file in _files.Values) {
|
||||||
if(_usedFileIds.Contains(file.ID)) {
|
if(_usedFileIds.Contains(file.ID)) {
|
||||||
try {
|
try {
|
||||||
|
@ -497,6 +564,14 @@ namespace Mesen.GUI.Debugger
|
||||||
if(File.Exists(sourceFile)) {
|
if(File.Exists(sourceFile)) {
|
||||||
file.Data = File.ReadAllLines(sourceFile);
|
file.Data = File.ReadAllLines(sourceFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LineInfo[] fileInfos = new LineInfo[maxLineCountByFile[file.ID] + 1];
|
||||||
|
foreach(LineInfo line in _lines.Values) {
|
||||||
|
if(line.FileID == file.ID) {
|
||||||
|
fileInfos[line.LineNumber] = line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_linesByFile[file.ID] = fileInfos;
|
||||||
} catch {
|
} catch {
|
||||||
_errorCount++;
|
_errorCount++;
|
||||||
}
|
}
|
||||||
|
@ -504,6 +579,74 @@ namespace Mesen.GUI.Debugger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BuildCdlData()
|
||||||
|
{
|
||||||
|
int prgSize = InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom);
|
||||||
|
if(prgSize <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] cdlFile = new byte[prgSize];
|
||||||
|
|
||||||
|
//Mark data/code regions
|
||||||
|
foreach(SpanInfo span in _spans.Values) {
|
||||||
|
int prgAddress = GetPrgAddress(span);
|
||||||
|
if(prgAddress >= 0 && prgAddress < prgSize) {
|
||||||
|
for(int i = 0; i < span.Size; i++) {
|
||||||
|
if(cdlFile[prgAddress + i] != (byte)CdlPrgFlags.Data && !span.IsData && span.Size <= 3) {
|
||||||
|
cdlFile[prgAddress + i] = (byte)CdlPrgFlags.Code;
|
||||||
|
} else if(span.IsData) {
|
||||||
|
cdlFile[prgAddress + i] = (byte)CdlPrgFlags.Data;
|
||||||
|
} else if(cdlFile[prgAddress + i] == 0) {
|
||||||
|
//Mark bytes as tentative data, until we know that the bytes are actually code
|
||||||
|
cdlFile[prgAddress + i] = 0x04;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(int i = 0; i < cdlFile.Length; i++) {
|
||||||
|
if(cdlFile[i] == 0x04) {
|
||||||
|
//Mark all bytes marked as tentative data as data
|
||||||
|
cdlFile[i] = (byte)CdlPrgFlags.Data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Find/identify functions and jump targets
|
||||||
|
byte[] prgRomContent = InteropEmu.DebugGetMemoryState(DebugMemoryType.PrgRom);
|
||||||
|
foreach(SymbolInfo symbol in _symbols.Values) {
|
||||||
|
LineInfo line;
|
||||||
|
if(!symbol.SegmentID.HasValue) {
|
||||||
|
//This is a constant, ignore it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(int reference in symbol.References) {
|
||||||
|
if(_lines.TryGetValue(reference, out line) && line.SpanIDs.Count > 0) {
|
||||||
|
SpanInfo span;
|
||||||
|
if(_spans.TryGetValue(line.SpanIDs[0], out span) && !span.IsData && span.Size <= 3) {
|
||||||
|
int referencePrgAddr = GetPrgAddress(span);
|
||||||
|
if(referencePrgAddr >= 0 && referencePrgAddr < prgRomContent.Length) {
|
||||||
|
byte opCode = prgRomContent[referencePrgAddr];
|
||||||
|
if(opCode == 0x20 || opCode == 0x10 || opCode == 0x30 || opCode == 0x50 || opCode == 0x70 || opCode == 0x90 || opCode == 0xB0 || opCode == 0xD0 || opCode == 0xF0 || opCode == 0x4C || opCode == 0x20 || opCode == 0x4C || opCode == 0x6C) {
|
||||||
|
//This symbol is used with a JSR/jump instruction, so it's either a function or jump target
|
||||||
|
bool isJsr = opCode == 0x20;
|
||||||
|
SpanInfo definitionSpan = GetSymbolDefinitionSpan(symbol);
|
||||||
|
if(definitionSpan != null) {
|
||||||
|
int definitionPrgAddr = GetPrgAddress(definitionSpan);
|
||||||
|
if(definitionPrgAddr >= 0 && definitionPrgAddr < prgRomContent.Length) {
|
||||||
|
cdlFile[definitionPrgAddr] |= (byte)(isJsr ? CdlPrgFlags.SubEntryPoint : CdlPrgFlags.JumpTarget);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InteropEmu.DebugSetCdlData(cdlFile);
|
||||||
|
}
|
||||||
|
|
||||||
public void Import(string path, bool silent = false)
|
public void Import(string path, bool silent = false)
|
||||||
{
|
{
|
||||||
RomInfo romInfo = InteropEmu.GetRomInfo();
|
RomInfo romInfo = InteropEmu.GetRomInfo();
|
||||||
|
@ -537,39 +680,14 @@ namespace Mesen.GUI.Debugger
|
||||||
|
|
||||||
LoadFileData(basePath);
|
LoadFileData(basePath);
|
||||||
|
|
||||||
int prgSize = InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom);
|
BuildCdlData();
|
||||||
if(prgSize > 0) {
|
|
||||||
byte[] cdlFile = new byte[prgSize];
|
|
||||||
foreach(KeyValuePair<int, SpanInfo> kvp in _spans) {
|
|
||||||
SegmentInfo segment;
|
|
||||||
if(_segments.TryGetValue(kvp.Value.SegmentID, out segment)) {
|
|
||||||
if(!segment.IsRam && kvp.Value.Size != segment.Size) {
|
|
||||||
int prgAddress = kvp.Value.Offset + segment.FileOffset - _headerSize;
|
|
||||||
|
|
||||||
if(prgAddress >= 0 && prgAddress < prgSize) {
|
|
||||||
for(int i = 0; i < kvp.Value.Size; i++) {
|
|
||||||
if(cdlFile[prgAddress + i] == 0 && !kvp.Value.IsData && kvp.Value.Size <= 3) {
|
|
||||||
cdlFile[prgAddress + i] = (byte)0x01;
|
|
||||||
} else if(kvp.Value.IsData) {
|
|
||||||
cdlFile[prgAddress + i] = (byte)0x02;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InteropEmu.DebugSetCdlData(cdlFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(LineInfo line in _lines.Values) {
|
foreach(LineInfo line in _lines.Values) {
|
||||||
if(line.SpanID == null) {
|
foreach(int spanID in line.SpanIDs) {
|
||||||
continue;
|
SpanInfo span;
|
||||||
}
|
if(_spans.TryGetValue(spanID, out span)) {
|
||||||
|
SegmentInfo segment;
|
||||||
FileInfo file = _files[line.FileID];
|
if(_segments.TryGetValue(span.SegmentID, out segment) && !segment.IsRam) {
|
||||||
SpanInfo span = _spans[line.SpanID.Value];
|
|
||||||
SegmentInfo segment = _segments[span.SegmentID];
|
|
||||||
if(!segment.IsRam) {
|
|
||||||
for(int i = 0; i < span.Size; i++) {
|
for(int i = 0; i < span.Size; i++) {
|
||||||
int prgAddress = segment.FileOffset - _headerSize + span.Offset + i;
|
int prgAddress = segment.FileOffset - _headerSize + span.Offset + i;
|
||||||
|
|
||||||
|
@ -580,12 +698,16 @@ namespace Mesen.GUI.Debugger
|
||||||
}
|
}
|
||||||
|
|
||||||
_linesByPrgAddress[prgAddress] = line;
|
_linesByPrgAddress[prgAddress] = line;
|
||||||
if(i == 0) {
|
if(i == 0 && spanID == line.SpanIDs[0]) {
|
||||||
|
//Mark the first byte of the first span representing this line as the PRG address for this line of code
|
||||||
|
FileInfo file = _files[line.FileID];
|
||||||
_prgAddressByLine[file.ID.ToString() + "_" + line.LineNumber.ToString()] = prgAddress;
|
_prgAddressByLine[file.ID.ToString() + "_" + line.LineNumber.ToString()] = prgAddress;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LoadLabels();
|
LoadLabels();
|
||||||
|
|
||||||
|
@ -606,7 +728,7 @@ namespace Mesen.GUI.Debugger
|
||||||
labels.AddRange(_saveRamLabels.Values);
|
labels.AddRange(_saveRamLabels.Values);
|
||||||
labelCount += _ramLabels.Count + _workRamLabels.Count + _saveRamLabels.Count;
|
labelCount += _ramLabels.Count + _workRamLabels.Count + _saveRamLabels.Count;
|
||||||
}
|
}
|
||||||
LabelManager.SetLabels(labels, !silent);
|
LabelManager.SetLabels(labels, true);
|
||||||
|
|
||||||
if(!silent) {
|
if(!silent) {
|
||||||
if(_errorCount > 0) {
|
if(_errorCount > 0) {
|
||||||
|
@ -660,7 +782,7 @@ namespace Mesen.GUI.Debugger
|
||||||
{
|
{
|
||||||
public int ID;
|
public int ID;
|
||||||
public int FileID;
|
public int FileID;
|
||||||
public int? SpanID;
|
public List<int> SpanIDs;
|
||||||
public LineType Type;
|
public LineType Type;
|
||||||
|
|
||||||
public int LineNumber;
|
public int LineNumber;
|
||||||
|
|
|
@ -145,10 +145,10 @@ namespace Mesen.GUI.Debugger
|
||||||
|
|
||||||
public static void CreateAutomaticJumpLabels()
|
public static void CreateAutomaticJumpLabels()
|
||||||
{
|
{
|
||||||
bool[] jumpTargets = InteropEmu.DebugGetJumpTargets();
|
byte[] cdlData = InteropEmu.DebugGetPrgCdlData();
|
||||||
List<CodeLabel> labelsToAdd = new List<CodeLabel>();
|
List<CodeLabel> labelsToAdd = new List<CodeLabel>();
|
||||||
for(int i = 0; i < jumpTargets.Length; i++) {
|
for(int i = 0; i < cdlData.Length; i++) {
|
||||||
if(jumpTargets[i] && LabelManager.GetLabel((uint)i, AddressType.PrgRom) == null) {
|
if((cdlData[i] & (byte)CdlPrgFlags.JumpTarget) != 0 && LabelManager.GetLabel((uint)i, AddressType.PrgRom) == null) {
|
||||||
labelsToAdd.Add(new CodeLabel() { Flags = CodeLabelFlags.AutoJumpLabel, Address = (uint)i, AddressType = AddressType.PrgRom, Label = "L" + i.ToString("X4"), Comment = "" });
|
labelsToAdd.Add(new CodeLabel() { Flags = CodeLabelFlags.AutoJumpLabel, Address = (uint)i, AddressType = AddressType.PrgRom, Label = "L" + i.ToString("X4"), Comment = "" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ namespace Mesen.GUI.Debugger
|
||||||
_resultCount = 0;
|
_resultCount = 0;
|
||||||
|
|
||||||
HashSet<int> entryPoints = new HashSet<int>(InteropEmu.DebugGetFunctionEntryPoints());
|
HashSet<int> entryPoints = new HashSet<int>(InteropEmu.DebugGetFunctionEntryPoints());
|
||||||
bool[] isJumpTargets = InteropEmu.DebugGetJumpTargets();
|
byte[] cdlData = InteropEmu.DebugGetPrgCdlData();
|
||||||
|
|
||||||
List<SearchResultInfo> searchResults = new List<SearchResultInfo>();
|
List<SearchResultInfo> searchResults = new List<SearchResultInfo>();
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ namespace Mesen.GUI.Debugger
|
||||||
SearchResultType resultType = SearchResultType.Data;
|
SearchResultType resultType = SearchResultType.Data;
|
||||||
if(addressInfo?.Type == AddressType.PrgRom && entryPoints.Contains(addressInfo.Address)) {
|
if(addressInfo?.Type == AddressType.PrgRom && entryPoints.Contains(addressInfo.Address)) {
|
||||||
resultType = SearchResultType.Function;
|
resultType = SearchResultType.Function;
|
||||||
} else if(addressInfo?.Type == AddressType.PrgRom && addressInfo.Address < isJumpTargets.Length && isJumpTargets[addressInfo.Address]) {
|
} else if(addressInfo?.Type == AddressType.PrgRom && addressInfo.Address < cdlData.Length && (cdlData[addressInfo.Address] & (byte)CdlPrgFlags.JumpTarget) != 0) {
|
||||||
resultType = SearchResultType.JumpTarget;
|
resultType = SearchResultType.JumpTarget;
|
||||||
} else if(isConstant) {
|
} else if(isConstant) {
|
||||||
resultType = SearchResultType.Constant;
|
resultType = SearchResultType.Constant;
|
||||||
|
@ -226,7 +226,7 @@ namespace Mesen.GUI.Debugger
|
||||||
SearchResultType resultType = SearchResultType.Data;
|
SearchResultType resultType = SearchResultType.Data;
|
||||||
if(label.AddressType == AddressType.PrgRom && entryPoints.Contains((int)label.Address)) {
|
if(label.AddressType == AddressType.PrgRom && entryPoints.Contains((int)label.Address)) {
|
||||||
resultType = SearchResultType.Function;
|
resultType = SearchResultType.Function;
|
||||||
} else if(label.AddressType == AddressType.PrgRom && label.Address < isJumpTargets.Length && isJumpTargets[label.Address]) {
|
} else if(label.AddressType == AddressType.PrgRom && label.Address < cdlData.Length && (cdlData[label.Address] & (byte)CdlPrgFlags.JumpTarget) != 0) {
|
||||||
resultType = SearchResultType.JumpTarget;
|
resultType = SearchResultType.JumpTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -368,21 +368,6 @@ namespace Mesen.GUI
|
||||||
return assembledCode;
|
return assembledCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport(DLLPath, EntryPoint = "DebugGetJumpTargets")] private static extern void DebugGetJumpTargetsWrapper(IntPtr isJumpTargets);
|
|
||||||
public static bool[] DebugGetJumpTargets()
|
|
||||||
{
|
|
||||||
bool[] isJumpTarget = new bool[InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom)];
|
|
||||||
|
|
||||||
GCHandle hJumpTarget = GCHandle.Alloc(isJumpTarget, GCHandleType.Pinned);
|
|
||||||
try {
|
|
||||||
InteropEmu.DebugGetJumpTargetsWrapper(hJumpTarget.AddrOfPinnedObject());
|
|
||||||
} finally {
|
|
||||||
hJumpTarget.Free();
|
|
||||||
}
|
|
||||||
|
|
||||||
return isJumpTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport(DLLPath, EntryPoint = "DebugGetMemoryState")] private static extern UInt32 DebugGetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer);
|
[DllImport(DLLPath, EntryPoint = "DebugGetMemoryState")] private static extern UInt32 DebugGetMemoryStateWrapper(DebugMemoryType type, IntPtr buffer);
|
||||||
public static byte[] DebugGetMemoryState(DebugMemoryType type)
|
public static byte[] DebugGetMemoryState(DebugMemoryType type)
|
||||||
{
|
{
|
||||||
|
@ -608,6 +593,12 @@ namespace Mesen.GUI
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport(DLLPath, EntryPoint = "DebugGetCdlData")] private static extern void DebugGetCdlDataWrapper(UInt32 offset, UInt32 length, DebugMemoryType type, IntPtr counts);
|
[DllImport(DLLPath, EntryPoint = "DebugGetCdlData")] private static extern void DebugGetCdlDataWrapper(UInt32 offset, UInt32 length, DebugMemoryType type, IntPtr counts);
|
||||||
|
|
||||||
|
public static byte[] DebugGetPrgCdlData()
|
||||||
|
{
|
||||||
|
return DebugGetCdlData(0, (uint)InteropEmu.DebugGetMemorySize(DebugMemoryType.PrgRom), DebugMemoryType.PrgRom);
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] DebugGetCdlData(UInt32 offset, UInt32 length, DebugMemoryType type)
|
public static byte[] DebugGetCdlData(UInt32 offset, UInt32 length, DebugMemoryType type)
|
||||||
{
|
{
|
||||||
byte[] cdlData = new byte[length];
|
byte[] cdlData = new byte[length];
|
||||||
|
@ -1180,9 +1171,16 @@ namespace Mesen.GUI
|
||||||
None = 0x00,
|
None = 0x00,
|
||||||
Code = 0x01,
|
Code = 0x01,
|
||||||
Data = 0x02,
|
Data = 0x02,
|
||||||
IndirectCode = 0x10,
|
|
||||||
|
//Bit 0x10 is used for "indirectly accessed as code" in FCEUX
|
||||||
|
//Repurposed to mean the address is the target of a jump instruction
|
||||||
|
JumpTarget = 0x10,
|
||||||
|
|
||||||
IndirectData = 0x20,
|
IndirectData = 0x20,
|
||||||
PcmData = 0x40,
|
PcmData = 0x40,
|
||||||
|
|
||||||
|
//Unused bit in original CDL spec
|
||||||
|
//Used to denote that the byte is the start of function (sub)
|
||||||
SubEntryPoint = 0x80
|
SubEntryPoint = 0x80
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,6 @@ extern "C"
|
||||||
DllExport void __stdcall DebugBreakOnScanline(int32_t scanline) { GetDebugger()->BreakOnScanline(scanline); }
|
DllExport void __stdcall DebugBreakOnScanline(int32_t scanline) { GetDebugger()->BreakOnScanline(scanline); }
|
||||||
DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); }
|
DllExport const char* __stdcall DebugGetCode(uint32_t &length) { return GetDebugger()->GetCode(length); }
|
||||||
|
|
||||||
DllExport void __stdcall DebugGetJumpTargets(bool* isJumpTarget) { GetDebugger()->GetJumpTargets(isJumpTarget); }
|
|
||||||
|
|
||||||
DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(ppuViewerId, scanline, cycle); }
|
DllExport void __stdcall DebugSetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle) { return GetDebugger()->SetPpuViewerScanlineCycle(ppuViewerId, scanline, cycle); }
|
||||||
DllExport void __stdcall DebugClearPpuViewerSettings(int32_t ppuViewerId) { return GetDebugger()->ClearPpuViewerSettings(ppuViewerId); }
|
DllExport void __stdcall DebugClearPpuViewerSettings(int32_t ppuViewerId) { return GetDebugger()->ClearPpuViewerSettings(ppuViewerId); }
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue