Lay down basic disassembler

This commit is contained in:
SimoneN64
2024-09-23 22:56:44 +02:00
parent d3096609fe
commit 60870165d5
10 changed files with 132 additions and 3 deletions

View File

@@ -2,6 +2,9 @@ file(GLOB SOURCES *.cpp)
file(GLOB HEADERS *.hpp) file(GLOB HEADERS *.hpp)
add_subdirectory(core) add_subdirectory(core)
option(CAPSTONE_ARCHITECTURE_DEFAULT OFF)
option(CAPSTONE_MIPS_SUPPORT ON)
add_subdirectory(../../external/capstone capstone)
add_library(backend ${SOURCES} ${HEADERS}) add_library(backend ${SOURCES} ${HEADERS})
target_link_libraries(backend PRIVATE core) target_link_libraries(backend PRIVATE core capstone)

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <Mem.hpp> #include <Mem.hpp>
#include <Registers.hpp> #include <Registers.hpp>
#include <Disassembler.hpp>
namespace n64 { namespace n64 {
struct BaseCPU { struct BaseCPU {
@@ -13,5 +14,6 @@ struct BaseCPU {
virtual void Deserialize(const std::vector<u8> &) = 0; virtual void Deserialize(const std::vector<u8> &) = 0;
virtual Mem &GetMem() = 0; virtual Mem &GetMem() = 0;
virtual Registers &GetRegs() = 0; virtual Registers &GetRegs() = 0;
virtual Disassembler::DisassemblyResult Disassemble(u32, u32) const = 0;
}; };
} // namespace n64 } // namespace n64

View File

@@ -0,0 +1,57 @@
#include <Disassembler.hpp>
Disassembler::DisassemblyResult Disassembler::DisassembleSimple(u32 address, u32 instruction) {
cs_insn *insn;
auto bytes = Util::IntegralToBuffer(instruction);
auto count = cs_disasm(handle, bytes.data(), bytes.size(), address, 0, &insn);
if (count <= 0)
return {};
DisassemblyResult result{true, fmt::format("0x{:016X}:\t{}\t{}", insn[0].address, insn[0].mnemonic, insn[0].op_str)};
cs_free(insn, count);
return result;
}
Disassembler::DisassemblyResult Disassembler::DisassembleDetailed(u32 address, u32 instruction) {
cs_insn *insn;
auto bytes = Util::IntegralToBuffer(instruction);
auto count = cs_disasm(handle, bytes.data(), bytes.size(), address, 0, &insn);
if (count <= 0)
return {};
DisassemblyResult result{true};
result.address = insn[0].address;
result.mnemonic = insn[0].mnemonic;
result.full += result.address + ":\t";
result.full += result.mnemonic + "\t";
cs_detail *details = insn[0].detail;
auto formatOperand = [&](const cs_mips_op &operand) {
switch (operand.type) {
case MIPS_OP_IMM:
return fmt::format("#{:X}", operand.is_unsigned ? operand.uimm : operand.imm);
case MIPS_OP_MEM:
return fmt::format("{}(0x{:X})", cs_reg_name(handle, operand.mem.base), operand.mem.disp);
case MIPS_OP_REG:
return fmt::format("{}", cs_reg_name(handle, operand.reg));
}
};
for (u8 i = 0; i < details->mips.op_count && i < 3; i++) {
result.ops[i] = formatOperand(details->mips.operands[i]);
result.full += result.ops[i] + "\t";
}
result.full += "\t// ";
// TODO: generate a comment
cs_free(insn, count);
return result;
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include <capstone/capstone.h>
#include <utils/log.hpp>
#include <utils/MemoryHelpers.hpp>
#include <portable_endian_bswap.h>
#include <array>
struct Disassembler {
struct DisassemblyResult {
bool success = false;
std::string full;
u64 address;
std::string mnemonic;
std::array<std::string, 3> ops{};
};
static Disassembler &instance(bool rsp = false) {
static Disassembler ret(rsp);
return ret;
}
DisassemblyResult Disassemble(u32 address, u32 instruction) {
return details ? DisassembleDetailed(address, instruction) : DisassembleSimple(address, instruction);
}
~Disassembler() { cs_close(&handle); }
private:
DisassemblyResult DisassembleDetailed(u32 address, u32 instruction);
DisassemblyResult DisassembleSimple(u32 address, u32 instruction);
Disassembler(bool rsp) : rsp(rsp) {
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>((rsp ? CS_MODE_32 : CS_MODE_64) | CS_MODE_BIG_ENDIAN), &handle) !=
CS_ERR_OK) {
Util::panic("Could not initialize {} disassembler!", rsp ? "RSP" : "CPU");
}
if (cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON) != CS_ERR_OK) {
Util::warn("Could not enable disassembler's details!");
details = false;
}
}
bool rsp = false;
bool details = true;
csh handle;
};

View File

@@ -21,6 +21,10 @@ void Interpreter::CheckCompareInterrupt() {
} }
} }
Disassembler::DisassemblyResult Interpreter::Disassemble(u32 address, u32 instruction) const {
return Disassembler::instance().Disassemble(address, instruction);
}
int Interpreter::Step() { int Interpreter::Step() {
CheckCompareInterrupt(); CheckCompareInterrupt();

View File

@@ -20,6 +20,7 @@ struct Interpreter : BaseCPU {
Mem &GetMem() override { return mem; } Mem &GetMem() override { return mem; }
Registers &GetRegs() override { return regs; } Registers &GetRegs() override { return regs; }
Disassembler::DisassemblyResult Disassemble(u32, u32) const override;
private: private:
Registers regs; Registers regs;

View File

@@ -20,6 +20,8 @@ struct JIT : BaseCPU {
Registers &GetRegs() override { return regs; } Registers &GetRegs() override { return regs; }
Disassembler::DisassemblyResult Disassemble(u32, u32) const override { return {}; }
private: private:
Registers regs; Registers regs;
Mem mem; Mem mem;

View File

@@ -36,8 +36,7 @@ include_directories(
../../external/parallel-rdp/parallel-rdp-standalone/util ../../external/parallel-rdp/parallel-rdp-standalone/util
../../external/unarr ../../external/unarr
../../external/SDL/include ../../external/SDL/include
../../external/imgui ../../external/capstone/include
../../external/imgui/backends
) )
option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." OFF) option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." OFF)

View File

@@ -22,4 +22,8 @@ Debugger::Debugger() : QWidget(nullptr) {
verLayout->addLayout(horLayout); verLayout->addLayout(horLayout);
setLayout(verLayout); setLayout(verLayout);
connect(codeView, &QTreeView::activated, this, [&](QModelIndex index) {
});
} }

View File

@@ -6,6 +6,16 @@
#include <portable_endian_bswap.h> #include <portable_endian_bswap.h>
namespace Util { namespace Util {
template <typename T>
static FORCE_INLINE const std::vector<u8> &IntegralToBuffer(const T &val) {
std::vector<u8> ret{};
ret.resize(sizeof(T));
memcpy(ret.data(), &val, sizeof(T));
return ret;
}
template <typename T> template <typename T>
static FORCE_INLINE T ReadAccess(const u8 *data, u32 index) { static FORCE_INLINE T ReadAccess(const u8 *data, u32 index) {
if constexpr (sizeof(T) == 8) { if constexpr (sizeof(T) == 8) {