Mesen2/Core/PCE/Debugger/PceDisUtils.cpp
Sour 8701d3c15c Debugger: Prevent crash when attempting to load a .dbg file for GB roms
Display error in log window instead and ignore the .dbg file
2023-07-11 16:46:35 +09:00

348 lines
12 KiB
C++

#include "pch.h"
#include "PCE/Debugger/PceDisUtils.h"
#include "PCE/Debugger/DummyPceCpu.h"
#include "PCE/PceConsole.h"
#include "PCE/PceTypes.h"
#include "Shared/EmuSettings.h"
#include "Debugger/DisassemblyInfo.h"
#include "Debugger/LabelManager.h"
#include "Debugger/MemoryDumper.h"
#include "Utilities/HexUtilities.h"
#include "Utilities/FastString.h"
#include "Shared/MemoryType.h"
static constexpr uint8_t _opSize[22] = {
1, 1, 1, 2, 2,
2, 3, 2, 2,
3, 2, 2,
3, 3,
2, 3,
7,
3, 3, 4, 4,
3
};
static constexpr const char* _opName[256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
"BRK", "ORA", "SXY", "ST0", "TSB", "ORA", "ASL", "RMB0", "PHP", "ORA", "ASL", "NOP_0B","TSB", "ORA", "ASL", "BBR0", //0
"BPL", "ORA", "ORA", "ST1", "TRB", "ORA", "ASL", "RMB1", "CLC", "ORA", "INC", "NOP_1B","TRB", "ORA", "ASL", "BBR1", //1
"JSR", "AND", "SAX", "ST2", "BIT", "AND", "ROL", "RMB2", "PLP", "AND", "ROL", "NOP_2B","BIT", "AND", "ROL", "BBR2", //2
"BMI", "AND", "AND", "NOP_33","BIT", "AND", "ROL", "RMB3", "SEC", "AND", "DEC", "NOP_3B","BIT", "AND", "ROL", "BBR3", //3
"RTI", "EOR", "SAY", "TMA", "BSR", "EOR", "LSR", "RMB4", "PHA", "EOR", "LSR", "NOP_4B","JMP", "EOR", "LSR", "BBR4", //4
"BVC", "EOR", "EOR", "TAM", "CSL", "EOR", "LSR", "RMB5", "CLI", "EOR", "PHY", "NOP_5B","NOP_5C","EOR", "LSR", "BBR5", //5
"RTS", "ADC", "CLA", "NOP_63","STZ", "ADC", "ROR", "RMB6", "PLA", "ADC", "ROR", "NOP_6B","JMP", "ADC", "ROR", "BBR6", //6
"BVS", "ADC", "ADC", "TII", "STZ", "ADC", "ROR", "RMB7", "SEI", "ADC", "PLY", "NOP_7B","JMP", "ADC", "ROR", "BBR7", //7
"BRA", "STA", "CLX", "TST", "STY", "STA", "STX", "SMB0", "DEY", "BIT", "TXA", "NOP_8B","STY", "STA", "STX", "BBS0", //8
"BCC", "STA", "STA", "TST", "STY", "STA", "STX", "SMB1", "TYA", "STA", "TXS", "NOP_9B","STZ", "STA", "STZ", "BBS1", //9
"LDY", "LDA", "LDX", "TST", "LDY", "LDA", "LDX", "SMB2", "TAY", "LDA", "TAX", "NOP_AB","LDY", "LDA", "LDX", "BBS2", //A
"BCS", "LDA", "LDA", "TST", "LDY", "LDA", "LDX", "SMB3", "CLV", "LDA", "TSX", "NOP_BB","LDY", "LDA", "LDX", "BBS3", //B
"CPY", "CMP", "CLY", "TDD", "CPY", "CMP", "DEC", "SMB4", "INY", "CMP", "DEX", "NOP_CB","CPY", "CMP", "DEC", "BBS4", //C
"BNE", "CMP", "CMP", "TIN", "CSH", "CMP", "DEC", "SMB5", "CLD", "CMP", "PHX", "NOP_DB","NOP_DC","CMP", "DEC", "BBS5", //D
"CPX", "SBC", "NOP_E2","TIA", "CPX", "SBC", "INC", "SMB6", "INX", "SBC", "NOP", "NOP_EB","CPX", "SBC", "INC", "BBS6", //E
"BEQ", "SBC", "SBC", "TAI", "SET", "SBC", "INC", "SMB7", "SED", "SBC", "PLX", "NOP_FB","NOP_FC","SBC", "INC", "BBS7" //F
};
typedef PceAddrMode M;
static constexpr PceAddrMode _opMode[] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
M::Imm, M::IndX, M::Imp, M::Imm, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imp, M::Abs, M::Abs, M::Abs, M::ZeroRel, //0
M::Rel, M::IndY, M::ZInd, M::Imm, M::Zero, M::ZeroX, M::ZeroX, M::Zero, M::Imp, M::AbsY, M::Imp, M::Imp, M::Abs, M::AbsX, M::AbsX, M::ZeroRel, //1
M::Abs, M::IndX, M::Imp, M::Imm, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imp, M::Abs, M::Abs, M::Abs, M::ZeroRel, //2
M::Rel, M::IndY, M::ZInd, M::Imp, M::ZeroX, M::ZeroX, M::ZeroX, M::Zero, M::Imp, M::AbsY, M::Imp, M::Imp, M::AbsX, M::AbsX, M::AbsX, M::ZeroRel, //3
M::Imp, M::IndX, M::Imp, M::Imm, M::Rel, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imp, M::Abs, M::Abs, M::Abs, M::ZeroRel, //4
M::Rel, M::IndY, M::ZInd, M::Imm, M::Imp, M::ZeroX, M::ZeroX, M::Zero, M::Imp, M::AbsY, M::Imp, M::Imp, M::Imp, M::AbsX, M::AbsX, M::ZeroRel, //5
M::Imp, M::IndX, M::Imp, M::Imp, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imp, M::Ind, M::Abs, M::Abs, M::ZeroRel, //6
M::Rel, M::IndY, M::ZInd, M::Block, M::ZeroX, M::ZeroX, M::ZeroX, M::Zero, M::Imp, M::AbsY, M::Imp, M::Imp, M::AbsXInd, M::AbsX, M::AbsX, M::ZeroRel, //7
M::Rel, M::IndX, M::Imp, M::ImZero, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::ZeroRel, //8
M::Rel, M::IndY, M::ZInd, M::ImAbs, M::ZeroX, M::ZeroX, M::ZeroY, M::Zero, M::Imp, M::AbsY, M::Imp, M::Imp, M::Abs, M::AbsX, M::AbsX, M::ZeroRel, //9
M::Imm, M::IndX, M::Imm, M::ImZeroX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::ZeroRel, //A
M::Rel, M::IndY, M::ZInd, M::ImAbsX, M::ZeroX, M::ZeroX, M::ZeroY, M::Zero, M::Imp, M::AbsY, M::Imp, M::Imp, M::AbsX, M::AbsX, M::AbsY, M::ZeroRel, //B
M::Imm, M::IndX, M::Imp, M::Block, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::ZeroRel, //C
M::Rel, M::IndY, M::ZInd, M::Block, M::Imp, M::ZeroX, M::ZeroX, M::Zero, M::Imp, M::AbsY, M::Imp, M::Imp, M::Imp, M::AbsX, M::AbsX, M::ZeroRel, //D
M::Imm, M::IndX, M::Imp, M::Block, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::ZeroRel, //E
M::Rel, M::IndY, M::ZInd, M::Block, M::Imp, M::ZeroX, M::ZeroX, M::Zero, M::Imp, M::AbsY, M::Imp, M::Imp, M::Imp, M::AbsX, M::AbsX, M::ZeroRel, //F
};
void PceDisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings)
{
FastString str(settings->GetDebugConfig().UseLowerCaseDisassembly);
uint8_t opCode = info.GetOpCode();
uint8_t* byteCode = info.GetByteCode();
PceAddrMode addrMode = _opMode[opCode];
str.Write(_opName[opCode]);
str.Write(' ');
auto writeLabelOrAddr = [&str, &info, labelManager](uint16_t addr) {
AddressInfo address { addr, MemoryType::PceMemory };
string label = labelManager ? labelManager->GetLabel(address, !info.IsJump()) : "";
if(label.empty()) {
str.WriteAll('$', HexUtilities::ToHex(addr));
} else {
str.Write(label, true);
}
};
auto writeZeroAddr = [&str](uint8_t zero) {
str.WriteAll('$', HexUtilities::ToHex(zero));
};
switch(addrMode) {
case PceAddrMode::Acc: str.Write('A'); break;
case PceAddrMode::Imm: str.WriteAll("#$", HexUtilities::ToHex(byteCode[1])); break;
case PceAddrMode::Ind:
str.Write('(');
writeLabelOrAddr(byteCode[1] | (byteCode[2] << 8));
str.Write(')');
break;
case PceAddrMode::ZInd:
str.Write('(');
writeZeroAddr(byteCode[1]);
str.Write(')');
break;
case PceAddrMode::IndY:
str.Write('(');
writeZeroAddr(byteCode[1]);
str.Write("),Y");
break;
case PceAddrMode::IndX:
str.WriteAll('(');
writeZeroAddr(byteCode[1]);
str.Write(",X)");
break;
case PceAddrMode::AbsXInd:
str.WriteAll('(');
writeLabelOrAddr(byteCode[1] | (byteCode[2] << 8));
str.Write(",X)");
break;
case PceAddrMode::Abs: writeLabelOrAddr(byteCode[1] | (byteCode[2] << 8)); break;
case PceAddrMode::Zero: writeZeroAddr(byteCode[1]); break;
case PceAddrMode::Rel: writeLabelOrAddr(((int8_t)byteCode[1] + memoryAddr + 2) & 0xFFFF); break;
case PceAddrMode::AbsX: writeLabelOrAddr(byteCode[1] | (byteCode[2] << 8)); str.Write(",X"); break;
case PceAddrMode::AbsY: writeLabelOrAddr(byteCode[1] | (byteCode[2] << 8)); str.Write(",Y"); break;
case PceAddrMode::ZeroX: writeZeroAddr(byteCode[1]); str.Write(",X"); break;
case PceAddrMode::ZeroY: writeZeroAddr(byteCode[1]); str.Write(",Y"); break;
case PceAddrMode::ZeroRel:
writeZeroAddr(byteCode[1]);
str.Write(',');
writeLabelOrAddr(((int8_t)byteCode[2] + memoryAddr + 3) & 0xFFFF);
break;
case PceAddrMode::Block:
writeLabelOrAddr((uint16_t)(byteCode[1] | (byteCode[2] << 8)));
str.Write(",");
writeLabelOrAddr((uint16_t)(byteCode[3] | (byteCode[4] << 8)));
str.WriteAll(",#$", HexUtilities::ToHex((uint16_t)(byteCode[5] | (byteCode[6] << 8))));
break;
case PceAddrMode::ImZero:
str.WriteAll("#$", HexUtilities::ToHex(byteCode[1]), ",");
writeZeroAddr(byteCode[2]);
break;
case PceAddrMode::ImAbs:
str.WriteAll("#$", HexUtilities::ToHex(byteCode[1]), ",");
writeLabelOrAddr((uint16_t)(byteCode[2] | (byteCode[3] << 8)));
break;
case PceAddrMode::ImZeroX:
str.WriteAll("#$", HexUtilities::ToHex(byteCode[1]), ",");
writeZeroAddr(byteCode[2]);
str.WriteAll(",X");
break;
case PceAddrMode::ImAbsX:
str.WriteAll("#$", HexUtilities::ToHex(byteCode[1]), ",");
writeLabelOrAddr((uint16_t)(byteCode[2] | (byteCode[3] << 8)));
str.WriteAll(",X");
break;
default: break;
}
out += str.ToString();
}
EffectiveAddressInfo PceDisUtils::GetEffectiveAddress(DisassemblyInfo& info, PceConsole* console, PceCpuState& state)
{
bool isJump = PceDisUtils::IsUnconditionalJump(info.GetOpCode()) || PceDisUtils::IsConditionalJump(info.GetOpCode());
if(isJump) {
//For jumps, show no address/value
return { };
}
bool showEffectiveAddress = false;
switch(_opMode[info.GetOpCode()]) {
case PceAddrMode::Imp:
//Show no address/value for stack operations, etc.
return {};
case PceAddrMode::Zero:
case PceAddrMode::ZeroX:
case PceAddrMode::ZeroY:
case PceAddrMode::IndX:
case PceAddrMode::IndY:
case PceAddrMode::Ind:
case PceAddrMode::AbsX:
case PceAddrMode::AbsXInd:
case PceAddrMode::AbsY:
case PceAddrMode::ZInd:
case PceAddrMode::ZeroRel:
case PceAddrMode::ImZero:
case PceAddrMode::ImZeroX:
case PceAddrMode::ImAbs:
case PceAddrMode::ImAbsX:
showEffectiveAddress = true;
break;
default:
showEffectiveAddress = false;
break;
}
DummyPceCpu pceCpu(nullptr, console->GetMemoryManager());
pceCpu.SetDummyState(state);
pceCpu.Exec();
uint32_t count = pceCpu.GetOperationCount();
for(int i = count - 1; i > 0; i--) {
MemoryOperationInfo opInfo = pceCpu.GetOperationInfo(i);
if(opInfo.Type != MemoryOperationType::ExecOperand && opInfo.Type != MemoryOperationType::DummyRead) {
return { (int32_t)opInfo.Address, 1, showEffectiveAddress };
}
}
return {};
}
uint8_t PceDisUtils::GetOpSize(PceAddrMode addrMode)
{
return _opSize[(int)addrMode];
}
uint8_t PceDisUtils::GetOpSize(uint8_t opCode)
{
return GetOpSize(_opMode[opCode]);
}
char const* const PceDisUtils::GetOpName(uint8_t opCode)
{
return _opName[opCode];
}
PceAddrMode PceDisUtils::GetOpMode(uint8_t opCode)
{
return _opMode[opCode];
}
bool PceDisUtils::IsUnconditionalJump(uint8_t opCode)
{
switch(opCode) {
case 0x20: //JSR
case 0x40: //RTI
case 0x44: //BSR
case 0x4C: //JMP (Absolute)
case 0x60: //RTS
case 0x6C: //JMP (Indirect)
case 0x7C: //JMP (Absolute,X)
case 0x80: //BRA
return true;
default:
return false;
}
}
bool PceDisUtils::IsConditionalJump(uint8_t opCode)
{
switch(opCode) {
case 0x10: //BPL
case 0x30: //BMI
case 0x50: //BVC
case 0x70: //BVS
case 0x90: //BCC
case 0xB0: //BCS
case 0xD0: //BNE
case 0xF0: //BEQ
//BBR
case 0x0F: case 0x1F: case 0x2F: case 0x3F:
case 0x4F: case 0x5F: case 0x6F: case 0x7F:
//BBS
case 0x8F: case 0x9F: case 0xAF: case 0xBF:
case 0xCF: case 0xDF: case 0xEF: case 0xFF:
return true;
default:
return false;
}
}
CdlFlags::CdlFlags PceDisUtils::GetOpFlags(uint8_t opCode, uint16_t pc, uint16_t prevPc)
{
switch(opCode) {
case 0x00: //BRK
case 0x20: //JSR
case 0x44: //BSR
return CdlFlags::SubEntryPoint;
case 0x10: //BPL
case 0x30: //BMI
case 0x4C: //JMP (Absolute)
case 0x50: //BVC
case 0x6C: //JMP (Indirect)
case 0x70: //BVS
case 0x7C: //JMP (Absolute,X)
case 0x80: //BRA
case 0x90: //BCC
case 0xB0: //BCS
case 0xD0: //BNE
case 0xF0: //BEQ
case 0x0F: case 0x1F: case 0x2F: case 0x3F: //BBR
case 0x4F: case 0x5F: case 0x6F: case 0x7F: //BBR
case 0x8F: case 0x9F: case 0xAF: case 0xBF: //BBS
case 0xCF: case 0xDF: case 0xEF: case 0xFF: //BBS
return pc != prevPc + PceDisUtils::GetOpSize(opCode) ? CdlFlags::JumpTarget : CdlFlags::None;
default:
return CdlFlags::None;
}
}
bool PceDisUtils::IsJumpToSub(uint8_t opCode)
{
return opCode == 0x20 || opCode == 0x44 || opCode == 0x00; //JSR, BSR, BRK
}
bool PceDisUtils::IsReturnInstruction(uint8_t opCode)
{
return opCode == 0x60 || opCode == 0x40; //RTS & RTI
}
bool PceDisUtils::IsOpUnofficial(uint8_t opCode)
{
switch(opCode) {
case 0x33:
case 0x5C:
case 0x63:
case 0xDC:
case 0xE2:
case 0xFC:
return true;
default:
return (opCode & 0x0F) == 0x0B;
}
}