mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
308 lines
No EOL
12 KiB
C++
308 lines
No EOL
12 KiB
C++
#include "pch.h"
|
|
#include "NES/Debugger/NesDisUtils.h"
|
|
|
|
#include "NES/NesTypes.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[17] = {
|
|
1, 1, 1, 2, 2,
|
|
2, 3, 2, 2,
|
|
3, 2, 2, 2,
|
|
3, 3, 3, 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", "STP", "SLO", "NOP", "ORA", "ASL", "SLO", "PHP", "ORA", "ASL", "ANC", "NOP", "ORA", "ASL", "SLO", //0
|
|
"BPL", "ORA", "STP", "SLO", "NOP", "ORA", "ASL", "SLO", "CLC", "ORA", "NOP", "SLO", "NOP", "ORA", "ASL", "SLO", //1
|
|
"JSR", "AND", "STP", "RLA", "BIT", "AND", "ROL", "RLA", "PLP", "AND", "ROL", "ANC", "BIT", "AND", "ROL", "RLA", //2
|
|
"BMI", "AND", "STP", "RLA", "NOP", "AND", "ROL", "RLA", "SEC", "AND", "NOP", "RLA", "NOP", "AND", "ROL", "RLA", //3
|
|
"RTI", "EOR", "STP", "SRE", "NOP", "EOR", "LSR", "SRE", "PHA", "EOR", "LSR", "ALR", "JMP", "EOR", "LSR", "SRE", //4
|
|
"BVC", "EOR", "STP", "SRE", "NOP", "EOR", "LSR", "SRE", "CLI", "EOR", "NOP", "SRE", "NOP", "EOR", "LSR", "SRE", //5
|
|
"RTS", "ADC", "STP", "RRA", "NOP", "ADC", "ROR", "RRA", "PLA", "ADC", "ROR", "ARR", "JMP", "ADC", "ROR", "RRA", //6
|
|
"BVS", "ADC", "STP", "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", "STP", "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", "STP", "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", "STP", "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", "STP", "ISC", "NOP", "SBC", "INC", "ISC", "SED", "SBC", "NOP", "ISC", "NOP", "SBC", "INC", "ISC" //F
|
|
};
|
|
|
|
typedef NesAddrMode M;
|
|
static constexpr NesAddrMode _opMode[] = {
|
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
M::Imp, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //0
|
|
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//1
|
|
M::Abs, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //2
|
|
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//3
|
|
M::Imp, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //4
|
|
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//5
|
|
M::Imp, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Ind, M::Abs, M::Abs, M::Abs, //6
|
|
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//7
|
|
M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //8
|
|
M::Rel, M::IndYW, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroY, M::ZeroY, M::Imp, M::AbsYW,M::Imp, M::AbsYW,M::AbsXW,M::AbsXW,M::AbsYW,M::AbsYW,//9
|
|
M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //A
|
|
M::Rel, M::IndY, M::None, M::IndY, M::ZeroX, M::ZeroX, M::ZeroY, M::ZeroY, M::Imp, M::AbsY, M::Imp, M::AbsY, M::AbsX, M::AbsX, M::AbsY, M::AbsY, //B
|
|
M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //C
|
|
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//D
|
|
M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //E
|
|
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//F
|
|
};
|
|
|
|
static constexpr bool _isUnofficial[256] = {
|
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
false,false,true, true, true, false,false,true, false,false,false,true, true, false,false,true, //0
|
|
false,false,true, true, true, false,false,true, false,false,true, true, true, false,false,true, //1
|
|
false,false,true, true, false,false,false,true, false,false,false,true, false,false,false,true, //2
|
|
false,false,true, true, true, false,false,true, false,false,true, true, true, false,false,true, //3
|
|
false,false,true, true, true, false,false,true, false,false,false,true, false,false,false,true, //4
|
|
false,false,true, true, true, false,false,true, false,false,true, true, true, false,false,true, //5
|
|
false,false,true, true, true, false,false,true, false,false,false,true, false,false,false,true, //6
|
|
false,false,true, true, true, false,false,true, false,false,true, true, true, false,false,true, //7
|
|
true, false,true, true, false,false,false,true, false,true, false,true, false,false,false,true, //8
|
|
false,false,true, true, false,false,false,true, false,false,false,true, true, false,true, true, //9
|
|
false,false,false,true, false,false,false,true, false,false,false,true, false,false,false,true, //A
|
|
false,false,true, true, false,false,false,true, false,false,false,true, false,false,false,true, //B
|
|
false,false,true, true, false,false,false,true, false,false,false,true, false,false,false,true, //C
|
|
false,false,true, true, true, false,false,true, false,false,true, true, true, false,false,true, //D
|
|
false,false,true, true, false,false,false,true, false,false,false,true, false,false,false,true, //E
|
|
false,false,true, true, true, false,false,true, false,false,true, true, true, false,false,true //F
|
|
};
|
|
|
|
void NesDisUtils::GetDisassembly(DisassemblyInfo& info, string& out, uint32_t memoryAddr, LabelManager* labelManager, EmuSettings* settings)
|
|
{
|
|
FastString str(settings->GetDebugConfig().UseLowerCaseDisassembly);
|
|
|
|
uint8_t opCode = info.GetOpCode();
|
|
NesAddrMode addrMode = _opMode[opCode];
|
|
str.Write(_opName[opCode]);
|
|
str.Write(' ');
|
|
|
|
uint32_t opAddr = NesDisUtils::GetOperandAddress(info, memoryAddr);
|
|
uint32_t opSize = info.GetOpSize();
|
|
|
|
FastString operand(settings->GetDebugConfig().UseLowerCaseDisassembly);
|
|
if(opSize > 1) {
|
|
if(addrMode != NesAddrMode::Imm) {
|
|
AddressInfo address { (int32_t)opAddr, MemoryType::NesMemory };
|
|
string label = labelManager ? labelManager->GetLabel(address, !info.IsJump()) : "";
|
|
if(label.size()) {
|
|
operand.Write(label, true);
|
|
}
|
|
}
|
|
|
|
if(operand.GetSize() == 0) {
|
|
if(opSize == 3 || addrMode == NesAddrMode::Rel) {
|
|
operand.WriteAll('$', HexUtilities::ToHex((uint16_t)opAddr));
|
|
} else if(opSize == 2) {
|
|
operand.WriteAll('$', HexUtilities::ToHex((uint8_t)opAddr));
|
|
}
|
|
}
|
|
}
|
|
|
|
switch(addrMode) {
|
|
case NesAddrMode::Acc: str.Write('A'); break;
|
|
case NesAddrMode::Imm: str.WriteAll("#", operand); break;
|
|
case NesAddrMode::Ind: str.WriteAll("(", operand, ")"); break;
|
|
case NesAddrMode::IndX: str.WriteAll("(", operand, ",X)"); break;
|
|
|
|
case NesAddrMode::IndY:
|
|
case NesAddrMode::IndYW:
|
|
str.WriteAll("(", operand, "),Y");
|
|
break;
|
|
|
|
case NesAddrMode::Abs:
|
|
case NesAddrMode::Rel:
|
|
case NesAddrMode::Zero:
|
|
str.Write(operand);
|
|
break;
|
|
|
|
case NesAddrMode::AbsX:
|
|
case NesAddrMode::AbsXW:
|
|
case NesAddrMode::ZeroX:
|
|
str.WriteAll(operand, ",X");
|
|
break;
|
|
|
|
case NesAddrMode::AbsY:
|
|
case NesAddrMode::AbsYW:
|
|
case NesAddrMode::ZeroY:
|
|
str.WriteAll(operand, ",Y");
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
out += str.ToString();
|
|
}
|
|
|
|
uint32_t NesDisUtils::GetOperandAddress(DisassemblyInfo& info, uint32_t memoryAddr)
|
|
{
|
|
uint32_t opSize = info.GetOpSize();
|
|
uint32_t opAddr = 0;
|
|
uint8_t* byteCode = info.GetByteCode();
|
|
if(opSize == 2) {
|
|
opAddr = byteCode[1];
|
|
} else if(opSize == 3) {
|
|
opAddr = byteCode[1] | (byteCode[2] << 8);
|
|
}
|
|
|
|
NesAddrMode addrMode = _opMode[byteCode[0]];
|
|
if(addrMode == NesAddrMode::Rel) {
|
|
opAddr = (((int8_t)opAddr + memoryAddr + 2) & 0xFFFF);
|
|
}
|
|
|
|
return opAddr;
|
|
}
|
|
|
|
EffectiveAddressInfo NesDisUtils::GetEffectiveAddress(DisassemblyInfo& info, NesCpuState& state, MemoryDumper* memoryDumper)
|
|
{
|
|
bool isJump = NesDisUtils::IsUnconditionalJump(info.GetOpCode()) || NesDisUtils::IsConditionalJump(info.GetOpCode());
|
|
NesAddrMode addrMode = _opMode[info.GetOpCode()];
|
|
if(isJump && addrMode != NesAddrMode::Ind) {
|
|
//For jumps, show no address/value (except indirect jump)
|
|
return { };
|
|
}
|
|
|
|
uint8_t* byteCode = info.GetByteCode();
|
|
switch(_opMode[info.GetOpCode()]) {
|
|
default: break;
|
|
|
|
case NesAddrMode::Abs: return { byteCode[1] | (byteCode[2] << 8), 1, false };
|
|
case NesAddrMode::Zero: return { byteCode[1], 1, false };
|
|
|
|
case NesAddrMode::ZeroX: return { (uint8_t)(byteCode[1] + state.X), 1, true }; break;
|
|
case NesAddrMode::ZeroY: return { (uint8_t)(byteCode[1] + state.Y), 1, true }; break;
|
|
|
|
case NesAddrMode::IndX: {
|
|
uint8_t zeroAddr = byteCode[1] + state.X;
|
|
return { memoryDumper->GetMemoryValue(MemoryType::NesMemory, zeroAddr) | memoryDumper->GetMemoryValue(MemoryType::NesMemory, (uint8_t)(zeroAddr + 1)) << 8, 1, true };
|
|
}
|
|
|
|
case NesAddrMode::IndY:
|
|
case NesAddrMode::IndYW: {
|
|
uint8_t zeroAddr = byteCode[1];
|
|
uint16_t addr = memoryDumper->GetMemoryValue(MemoryType::NesMemory, zeroAddr) | memoryDumper->GetMemoryValue(MemoryType::NesMemory, (uint8_t)(zeroAddr + 1)) << 8;
|
|
return { (uint16_t)(addr + state.Y), 1, true };
|
|
}
|
|
|
|
case NesAddrMode::Ind: {
|
|
uint16_t addr = byteCode[1] | (byteCode[2] << 8);
|
|
uint8_t lo = memoryDumper->GetMemoryValue(MemoryType::NesMemory, addr);
|
|
//CPU bug when indirect address starts at the end of a page
|
|
uint8_t hi = memoryDumper->GetMemoryValue(MemoryType::NesMemory, (addr & 0xFF00) | ((addr + 1) & 0xFF));
|
|
return { lo | (hi << 8), 1, true };
|
|
}
|
|
|
|
case NesAddrMode::AbsX:
|
|
case NesAddrMode::AbsXW:
|
|
return { (uint16_t)((byteCode[1] | (byteCode[2] << 8)) + state.X) & 0xFFFF, 1, true };
|
|
|
|
case NesAddrMode::AbsY:
|
|
case NesAddrMode::AbsYW:
|
|
return { (uint16_t)((byteCode[1] | (byteCode[2] << 8)) + state.Y) & 0xFFFF, 1, true };
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
uint8_t NesDisUtils::GetOpSize(NesAddrMode addrMode)
|
|
{
|
|
return _opSize[(int)addrMode];
|
|
}
|
|
|
|
uint8_t NesDisUtils::GetOpSize(uint8_t opCode)
|
|
{
|
|
return GetOpSize(_opMode[opCode]);
|
|
}
|
|
|
|
char const* const NesDisUtils::GetOpName(uint8_t opCode)
|
|
{
|
|
return _opName[opCode];
|
|
}
|
|
|
|
NesAddrMode NesDisUtils::GetOpMode(uint8_t opCode)
|
|
{
|
|
return _opMode[opCode];
|
|
}
|
|
|
|
bool NesDisUtils::IsOpUnofficial(uint8_t opCode)
|
|
{
|
|
return _isUnofficial[opCode];
|
|
}
|
|
|
|
bool NesDisUtils::IsUnconditionalJump(uint8_t opCode)
|
|
{
|
|
switch(opCode) {
|
|
case 0x00: //BRK
|
|
case 0x20: //JSR
|
|
case 0x40: //RTI
|
|
case 0x4C: //JMP (Absolute)
|
|
case 0x60: //RTS
|
|
case 0x6C: //JMP (Indirect)
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool NesDisUtils::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
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
CdlFlags::CdlFlags NesDisUtils::GetOpFlags(uint8_t opCode, uint16_t pc, uint16_t prevPc)
|
|
{
|
|
switch(opCode) {
|
|
case 0x00: //BRK
|
|
case 0x20: //JSR
|
|
return CdlFlags::SubEntryPoint;
|
|
|
|
case 0x10: //BPL
|
|
case 0x30: //BMI
|
|
case 0x4C: //JMP (Absolute)
|
|
case 0x50: //BVC
|
|
case 0x6C: //JMP (Indirect)
|
|
case 0x70: //BVS
|
|
case 0x90: //BCC
|
|
case 0xB0: //BCS
|
|
case 0xD0: //BNE
|
|
case 0xF0: //BEQ
|
|
return pc != prevPc + NesDisUtils::GetOpSize(opCode) ? CdlFlags::JumpTarget : CdlFlags::None;
|
|
|
|
default:
|
|
return CdlFlags::None;
|
|
}
|
|
}
|
|
|
|
bool NesDisUtils::IsJumpToSub(uint8_t opCode)
|
|
{
|
|
return opCode == 0x20 || opCode == 0x00; //JSR, BRK
|
|
}
|
|
|
|
bool NesDisUtils::IsReturnInstruction(uint8_t opCode)
|
|
{
|
|
return opCode == 0x60 || opCode == 0x40; //RTS, RTI
|
|
} |