mirror of
https://github.com/zoriya/ComSquare.git
synced 2026-06-03 02:23:49 +00:00
Implementing a dissasembly context to keep track of flags durring the disassembly
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
#include "CPUDebug.hpp"
|
#include "CPUDebug.hpp"
|
||||||
#include "../Utility/Utility.hpp"
|
#include "../Utility/Utility.hpp"
|
||||||
#include "../Exceptions/InvalidOpcode.hpp"
|
#include "../Exceptions/InvalidOpcode.hpp"
|
||||||
|
#include "../CPU/CPU.hpp"
|
||||||
#include <QtEvents>
|
#include <QtEvents>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -75,7 +76,9 @@ namespace ComSquare::Debugger
|
|||||||
this->_isPaused = true;
|
this->_isPaused = true;
|
||||||
}
|
}
|
||||||
uint24_t pc = (this->_registers.pbr << 16u) | (this->_registers.pc - 1u);
|
uint24_t pc = (this->_registers.pbr << 16u) | (this->_registers.pc - 1u);
|
||||||
this->_ui.logger->append((this->_parseInstruction(pc).toString() + " - " + Utility::to_hex(opcode)).c_str());
|
DisassemblyContext ctx = {this->_registers.p.m, this->_registers.p.x_b};
|
||||||
|
DisassembledInstruction instruction = this->_parseInstruction(pc, ctx);
|
||||||
|
this->_ui.logger->append((instruction.toString() + " - " + Utility::to_hex(opcode)).c_str());
|
||||||
unsigned ret = CPU::_executeInstruction(opcode);
|
unsigned ret = CPU::_executeInstruction(opcode);
|
||||||
this->_updateRegistersPanel();
|
this->_updateRegistersPanel();
|
||||||
return ret;
|
return ret;
|
||||||
@@ -135,53 +138,104 @@ namespace ComSquare::Debugger
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<DisassembledInstruction> CPUDebug::_disassemble(uint24_t pc, uint24_t length)
|
|
||||||
{
|
|
||||||
std::vector<DisassembledInstruction> map;
|
|
||||||
uint24_t endAddr = pc + length;
|
|
||||||
|
|
||||||
while (pc < endAddr) {
|
|
||||||
DisassembledInstruction instruction = this->_parseInstruction(pc);
|
|
||||||
map.push_back(instruction);
|
|
||||||
pc += instruction.size;
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPUDebug::clearHistory()
|
void CPUDebug::clearHistory()
|
||||||
{
|
{
|
||||||
this->_ui.logger->clear();
|
this->_ui.logger->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CPUDebug::_getImmediateValueForA(uint24_t pc)
|
std::vector<DisassembledInstruction> CPUDebug::_disassemble(uint24_t pc, uint24_t length)
|
||||||
|
{
|
||||||
|
std::vector<DisassembledInstruction> map;
|
||||||
|
uint24_t endAddr = pc + length;
|
||||||
|
DisassemblyContext ctx;
|
||||||
|
|
||||||
|
while (pc < endAddr) {
|
||||||
|
DisassembledInstruction instruction = this->_parseInstruction(pc, ctx);
|
||||||
|
map.push_back(instruction);
|
||||||
|
pc += instruction.size;
|
||||||
|
if (instruction.addressingMode == ImmediateForA && !ctx.mFlag)
|
||||||
|
pc++;
|
||||||
|
if (instruction.addressingMode == ImmediateForX && !ctx.xFlag)
|
||||||
|
pc++;
|
||||||
|
|
||||||
|
if (instruction.opcode == 0x40 && ctx.isEmulationMode) { // RTI
|
||||||
|
ctx.mFlag = true;
|
||||||
|
ctx.xFlag = true;
|
||||||
|
}
|
||||||
|
if (instruction.opcode == 0xC2) { // REP
|
||||||
|
if (ctx.isEmulationMode) {
|
||||||
|
ctx.mFlag = true;
|
||||||
|
ctx.xFlag = true;
|
||||||
|
} else {
|
||||||
|
uint8_t m = this->_bus->read(pc - 1);
|
||||||
|
ctx.mFlag &= ~m & 0b00100000u;
|
||||||
|
ctx.xFlag &= ~m & 0b00010000u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (instruction.opcode == 0xE2) { // SEP
|
||||||
|
uint8_t m = this->_bus->read(pc - 1);
|
||||||
|
ctx.mFlag |= m & 0b00100000u;
|
||||||
|
ctx.xFlag |= m & 0b00010000u;
|
||||||
|
}
|
||||||
|
if (instruction.opcode == 0x28) { // PLP
|
||||||
|
if (ctx.isEmulationMode) {
|
||||||
|
ctx.mFlag = true;
|
||||||
|
ctx.xFlag = true;
|
||||||
|
} else
|
||||||
|
ctx.compromised = true;
|
||||||
|
}
|
||||||
|
if (instruction.opcode == 0xFB) {// XCE
|
||||||
|
ctx.compromised = true;
|
||||||
|
ctx.isEmulationMode = false; // The most common use of the XCE is to enable native mode at the start of the ROM so we guess that it has done that.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisassembledInstruction CPUDebug::_parseInstruction(uint24_t pc, DisassemblyContext &ctx)
|
||||||
|
{
|
||||||
|
uint24_t opcode = this->_bus->read(pc, true);
|
||||||
|
Instruction instruction = this->_instructions[opcode];
|
||||||
|
std::string argument = this->_getInstructionParameter(instruction, pc + 1, ctx);
|
||||||
|
return DisassembledInstruction(instruction, pc, argument, opcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CPUDebug::_getInstructionParameter(Instruction &instruction, uint24_t pc, DisassemblyContext &ctx)
|
||||||
|
{
|
||||||
|
switch (instruction.addressingMode) {
|
||||||
|
case Implied:
|
||||||
|
return "";
|
||||||
|
case ImmediateForA:
|
||||||
|
return this->_getImmediateValue(pc, !ctx.mFlag);
|
||||||
|
case ImmediateForX:
|
||||||
|
return this->_getImmediateValue(pc, !ctx.xFlag);
|
||||||
|
case Immediate8bits:
|
||||||
|
return this->_getImmediateValue(pc, false);
|
||||||
|
case Absolute:
|
||||||
|
return this->_getAbsoluteValue(pc);
|
||||||
|
case AbsoluteLong:
|
||||||
|
return this->_getAbsoluteLongValue(pc);
|
||||||
|
case DirectPage:
|
||||||
|
return this->_getDirectValue(pc);
|
||||||
|
case DirectPageIndexedByX:
|
||||||
|
return this->_getDirectIndexedByXValue(pc);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CPUDebug::_getImmediateValue(uint24_t pc, bool dual)
|
||||||
{
|
{
|
||||||
unsigned value = this->_bus->read(pc, true);
|
unsigned value = this->_bus->read(pc, true);
|
||||||
|
|
||||||
if (!this->_registers.p.m)
|
if (dual)
|
||||||
value += this->_bus->read(pc + 1, true) << 8u;
|
value += this->_bus->read(pc + 1, true) << 8u;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "#$" << std::hex << value;
|
ss << "#$" << std::hex << value;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CPUDebug::_getImmediateValueForX(uint24_t pc)
|
|
||||||
{
|
|
||||||
unsigned value = this->_bus->read(pc, true);
|
|
||||||
|
|
||||||
if (!this->_registers.p.x_b)
|
|
||||||
value += this->_bus->read(pc + 1, true) << 8u;
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "#$" << std::hex << value;
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CPUDebug::_getImmediateValue8Bits(uint24_t pc)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "#$" << std::hex << static_cast<unsigned>(this->_bus->read(pc, true));
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CPUDebug::_getImmediateValue16Bits(uint24_t pc)
|
std::string CPUDebug::_getImmediateValue16Bits(uint24_t pc)
|
||||||
{
|
{
|
||||||
unsigned value = this->_bus->read(pc, true);
|
unsigned value = this->_bus->read(pc, true);
|
||||||
@@ -226,39 +280,6 @@ namespace ComSquare::Debugger
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
DisassembledInstruction CPUDebug::_parseInstruction(uint24_t pc)
|
|
||||||
{
|
|
||||||
uint24_t opcode = this->_bus->read(pc, true);
|
|
||||||
Instruction instruction = this->_instructions[opcode];
|
|
||||||
std::string argument = this->_getInstructionParameter(instruction, pc + 1);
|
|
||||||
return DisassembledInstruction(instruction, pc, argument, opcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CPUDebug::_getInstructionParameter(Instruction &instruction, uint24_t pc)
|
|
||||||
{
|
|
||||||
switch (instruction.addressingMode) {
|
|
||||||
case Implied:
|
|
||||||
return "";
|
|
||||||
case ImmediateForA:
|
|
||||||
return this->_getImmediateValueForA(pc);
|
|
||||||
case ImmediateForX:
|
|
||||||
return this->_getImmediateValueForX(pc);
|
|
||||||
case Immediate8bits:
|
|
||||||
return this->_getImmediateValue8Bits(pc);
|
|
||||||
case Absolute:
|
|
||||||
return this->_getAbsoluteValue(pc);
|
|
||||||
case AbsoluteLong:
|
|
||||||
return this->_getAbsoluteLongValue(pc);
|
|
||||||
case DirectPage:
|
|
||||||
return this->_getDirectValue(pc);
|
|
||||||
case DirectPageIndexedByX:
|
|
||||||
return this->_getDirectIndexedByXValue(pc);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "???";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int CPUDebug::RESB(uint24_t)
|
int CPUDebug::RESB(uint24_t)
|
||||||
{
|
{
|
||||||
CPU::RESB();
|
CPU::RESB();
|
||||||
|
|||||||
@@ -38,6 +38,21 @@ public:
|
|||||||
|
|
||||||
namespace ComSquare::Debugger
|
namespace ComSquare::Debugger
|
||||||
{
|
{
|
||||||
|
//! @brief Struct used to emulate the state of the processor during the disassembly since instructions take a different amount of space depending on some flags.
|
||||||
|
struct DisassemblyContext {
|
||||||
|
//! @brief The accumulator and Memory width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode.
|
||||||
|
//! @info If this flag is set to false, instructions that have the ImmediateByA addressing mode take 1 more bit of space.
|
||||||
|
bool mFlag = true;
|
||||||
|
//! @brief The indeX register width flag (in native mode only) - 0 = 16 bits mode, 1 = 8 bits mode OR the Break flag (in emulation mode only)
|
||||||
|
//! @info If this flag is set to false, instructions that have the ImmediateByX addressing mode take 1 more bit of space.
|
||||||
|
bool xFlag = true;
|
||||||
|
//! @brief Is the CPU emulating a 6502? If yes, some instructions don't change flags the same way.
|
||||||
|
bool isEmulationMode = true;
|
||||||
|
//! @brief Sometimes, the flags can't be tracked correctly after an instruction so the next instructions may not be correctly disassembled.
|
||||||
|
bool compromised = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! @brief Struct representing an instruction in an human readable way (created by disassembling the rom).
|
||||||
struct DisassembledInstruction : public CPU::Instruction {
|
struct DisassembledInstruction : public CPU::Instruction {
|
||||||
uint24_t address;
|
uint24_t address;
|
||||||
std::string argument;
|
std::string argument;
|
||||||
@@ -68,23 +83,20 @@ namespace ComSquare::Debugger
|
|||||||
SNES &_snes;
|
SNES &_snes;
|
||||||
//! @brief Reimplement the basic instruction execution method to log instructions inside the logger view.
|
//! @brief Reimplement the basic instruction execution method to log instructions inside the logger view.
|
||||||
unsigned _executeInstruction(uint8_t opcode) override;
|
unsigned _executeInstruction(uint8_t opcode) override;
|
||||||
//! @brief Parse the instruction at the program counter given to have human readable information.
|
|
||||||
DisassembledInstruction _parseInstruction(uint24_t pc);
|
|
||||||
//! @brief Get the parameter of the instruction as an hexadecimal string.
|
|
||||||
std::string _getInstructionParameter(ComSquare::CPU::Instruction &instruction, uint24_t pc);
|
|
||||||
//! @brief Get a printable string representing the flags.
|
|
||||||
std::string _getFlagsString();
|
|
||||||
//! @brief Disassemble part of the memory (using the bus) and parse it to a map of address and disassembled instruction.
|
//! @brief Disassemble part of the memory (using the bus) and parse it to a map of address and disassembled instruction.
|
||||||
std::vector<DisassembledInstruction> _disassemble(uint24_t startAddr, uint24_t size);
|
std::vector<DisassembledInstruction> _disassemble(uint24_t startAddr, uint24_t size);
|
||||||
|
//! @brief Parse the instruction at the program counter given to have human readable information.
|
||||||
|
DisassembledInstruction _parseInstruction(uint24_t pc, DisassemblyContext &ctx);
|
||||||
|
//! @brief Get the parameter of the instruction as an hexadecimal string.
|
||||||
|
std::string _getInstructionParameter(ComSquare::CPU::Instruction &instruction, uint24_t pc, DisassemblyContext &ctx);
|
||||||
|
//! @brief Get a printable string representing the flags.
|
||||||
|
std::string _getFlagsString();
|
||||||
//! @brief Update the register's panel (accumulator, stack pointer...)
|
//! @brief Update the register's panel (accumulator, stack pointer...)
|
||||||
void _updateRegistersPanel();
|
void _updateRegistersPanel();
|
||||||
|
|
||||||
//! @brief Return a printable string corresponding to the value of an immediate addressing mode for a.
|
//! @brief Return a printable string corresponding to the value of a 8 or 16 bits immediate addressing mode.
|
||||||
std::string _getImmediateValueForA(uint24_t pc);
|
//! @param dual Set this to true if the instruction take 16bits and not 8. (used for the immediate by a when the flag m is not set or the immediate by x if the flag x is not set).
|
||||||
//! @brief Return a printable string corresponding to the value of an immediate addressing mode for x.
|
std::string _getImmediateValue(uint24_t pc, bool dual);
|
||||||
std::string _getImmediateValueForX(uint24_t pc);
|
|
||||||
//! @brief Return a printable string corresponding to the value of a 8bits immediate addressing mode (used only with SEP and REP).
|
|
||||||
std::string _getImmediateValue8Bits(uint24_t pc);
|
|
||||||
//! @brief Return a printable string corresponding to the value of a 16bits immediate addressing mode.
|
//! @brief Return a printable string corresponding to the value of a 16bits immediate addressing mode.
|
||||||
std::string _getImmediateValue16Bits(uint24_t pc);
|
std::string _getImmediateValue16Bits(uint24_t pc);
|
||||||
//! @brief Return a printable string corresponding to the value of a direct addressing mode.
|
//! @brief Return a printable string corresponding to the value of a direct addressing mode.
|
||||||
|
|||||||
Reference in New Issue
Block a user