mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
348 lines
12 KiB
C++
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;
|
|
}
|
|
}
|