Lay down basic disassembler
This commit is contained in:
@@ -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)
|
||||||
@@ -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
|
||||||
|
|||||||
57
src/backend/core/Disassembler.cpp
Normal file
57
src/backend/core/Disassembler.cpp
Normal 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;
|
||||||
|
}
|
||||||
47
src/backend/core/Disassembler.hpp
Normal file
47
src/backend/core/Disassembler.hpp
Normal 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;
|
||||||
|
};
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user