Initial work for comment detail in debugger
This commit is contained in:
@@ -1,99 +1,9 @@
|
||||
#include <Disassembler.hpp>
|
||||
#include <Core.hpp>
|
||||
#include <optional>
|
||||
|
||||
Disassembler::DisassemblyResult Disassembler::DisassembleSimple(const u32 address, const u32 instruction) const {
|
||||
cs_insn *insn;
|
||||
const auto bytes = Util::IntegralToBuffer(std::byteswap(instruction));
|
||||
const auto count = cs_disasm(handle, bytes.data(), bytes.size(), address, 0, &insn);
|
||||
|
||||
if (count <= 0)
|
||||
return {};
|
||||
|
||||
DisassemblyResult result{true, std::format("0x{:016X}:\t{}\t{}", insn[0].address, insn[0].mnemonic, insn[0].op_str)};
|
||||
|
||||
cs_free(insn, count);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] Disassembler::DisassemblyResult Disassembler::Disassemble(const u32 address) const {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
n64::Mem& mem = core.cpu->GetMem();
|
||||
u32 paddr;
|
||||
if(!core.cpu->GetRegs().cop0.MapVAddr(n64::Cop0::TLBAccessType::LOAD, address, paddr))
|
||||
return DisassemblyResult{false, ""};
|
||||
|
||||
u32 instruction = mem.Read<u32>(paddr);
|
||||
|
||||
return details ? DisassembleDetailed(address, instruction) : DisassembleSimple(address, instruction);
|
||||
}
|
||||
|
||||
Disassembler::DisassemblyResult Disassembler::DisassembleDetailed(const u32 address, const u32 instruction) const {
|
||||
cs_insn *insn;
|
||||
const auto bytes = Util::IntegralToBuffer(std::byteswap(instruction));
|
||||
const 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 += std::format("0x{:016X}", result.address) + ":\t";
|
||||
result.full += result.mnemonic + "\t";
|
||||
result.comment = "// ";
|
||||
|
||||
const cs_detail *details = insn[0].detail;
|
||||
auto formatOperand = [&](const cs_mips_op &operand) {
|
||||
switch (operand.type) {
|
||||
case MIPS_OP_IMM:
|
||||
return DisassemblyResult::Operand{
|
||||
0xffcbf1ae,
|
||||
std::format("#{:X}", operand.is_unsigned ? operand.uimm : operand.imm)
|
||||
};
|
||||
case MIPS_OP_MEM:
|
||||
return DisassemblyResult::Operand{
|
||||
0xffaef1c3,
|
||||
std::format("{}(0x{:X})", cs_reg_name(handle, operand.mem.base), operand.mem.disp)
|
||||
};
|
||||
case MIPS_OP_REG:
|
||||
return DisassemblyResult::Operand{
|
||||
0xffaef1eb,
|
||||
std::format("{}", cs_reg_name(handle, operand.reg))
|
||||
};
|
||||
default:
|
||||
return DisassemblyResult::Operand { 0xff808080, "" };
|
||||
}
|
||||
};
|
||||
|
||||
auto formatComment = [&](const cs_mips_op &operand) -> std::string {
|
||||
switch (operand.type) {
|
||||
case MIPS_OP_IMM:
|
||||
return std::format("#{:X}", operand.is_unsigned ? operand.uimm : operand.imm);
|
||||
case MIPS_OP_MEM:
|
||||
return std::format("{}(0x{:X})", CapstoneToRegValue(operand.mem.base), operand.mem.disp);
|
||||
case MIPS_OP_REG:
|
||||
return std::format("{}", CapstoneToRegValue(operand.reg));
|
||||
default:
|
||||
return "! Unknown !";
|
||||
}
|
||||
};
|
||||
|
||||
for (u8 i = 0; i < details->mips.op_count && i < 3; i++) {
|
||||
result.ops[i] = formatOperand(details->mips.operands[i]);
|
||||
result.comment += formatComment(details->mips.operands[i]) + " ";
|
||||
result.full += result.ops[i].str + "\t";
|
||||
}
|
||||
|
||||
result.full += result.comment;
|
||||
|
||||
cs_free(insn, count);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Disassembler::CapstoneToRegValue(mips_reg reg) const {
|
||||
template <>
|
||||
std::optional<u64> Disassembler::CapstoneToRegValue(mips_reg reg) const {
|
||||
n64::Registers& regs = n64::Core::GetInstance().cpu->GetRegs();
|
||||
u64 val = 0;
|
||||
switch(reg) {
|
||||
@@ -191,8 +101,109 @@ std::string Disassembler::CapstoneToRegValue(mips_reg reg) const {
|
||||
val = regs.Read<u64>(31);
|
||||
break;
|
||||
default:
|
||||
return "! Unknown !";
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::format("{} (= 0x{:016X})", cs_reg_name(handle, reg), val);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string Disassembler::CapstoneToRegValue(mips_reg reg) const {
|
||||
n64::Registers& regs = n64::Core::GetInstance().cpu->GetRegs();
|
||||
auto val = CapstoneToRegValue<std::optional<u64>>(reg);
|
||||
|
||||
return std::format("{}", val.has_value() ? std::format("{} (= 0x{:016X})", cs_reg_name(handle, reg), val.value()) : "! Unknown !");
|
||||
}
|
||||
|
||||
Disassembler::DisassemblyResult Disassembler::DisassembleSimple(const u32 address, const u32 instruction) const {
|
||||
cs_insn *insn;
|
||||
const auto bytes = Util::IntegralToBuffer(std::byteswap(instruction));
|
||||
const auto count = cs_disasm(handle, bytes.data(), bytes.size(), address, 0, &insn);
|
||||
|
||||
if (count <= 0)
|
||||
return {};
|
||||
|
||||
DisassemblyResult result{true, std::format("0x{:016X}:\t{}\t{}", insn[0].address, insn[0].mnemonic, insn[0].op_str)};
|
||||
|
||||
cs_free(insn, count);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] Disassembler::DisassemblyResult Disassembler::Disassemble(const u32 address) const {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
n64::Mem& mem = core.cpu->GetMem();
|
||||
u32 paddr;
|
||||
if(!core.cpu->GetRegs().cop0.MapVAddr(n64::Cop0::TLBAccessType::LOAD, address, paddr))
|
||||
return DisassemblyResult{false, ""};
|
||||
|
||||
u32 instruction = mem.Read<u32>(paddr);
|
||||
|
||||
return details ? DisassembleDetailed(address, instruction) : DisassembleSimple(address, instruction);
|
||||
}
|
||||
|
||||
Disassembler::DisassemblyResult Disassembler::DisassembleDetailed(const u32 address, const u32 instruction) const {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
cs_insn *insn;
|
||||
const auto bytes = Util::IntegralToBuffer(std::byteswap(instruction));
|
||||
const 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 += std::format("0x{:016X}", result.address) + ":\t";
|
||||
result.full += result.mnemonic + "\t";
|
||||
|
||||
const cs_detail *details = insn[0].detail;
|
||||
auto formatOperand = [&](const cs_mips_op &operand) {
|
||||
switch (operand.type) {
|
||||
case MIPS_OP_IMM:
|
||||
return DisassemblyResult::Operand{
|
||||
0xffcbf1ae,
|
||||
std::format("#{:X}", operand.is_unsigned ? operand.uimm : operand.imm)
|
||||
};
|
||||
case MIPS_OP_MEM:
|
||||
return DisassemblyResult::Operand{
|
||||
0xffaef1c3,
|
||||
std::format("{}(0x{:X})", cs_reg_name(handle, operand.mem.base), operand.mem.disp)
|
||||
};
|
||||
case MIPS_OP_REG:
|
||||
return DisassemblyResult::Operand{
|
||||
0xffaef1eb,
|
||||
std::format("{}", cs_reg_name(handle, operand.reg))
|
||||
};
|
||||
default:
|
||||
return DisassemblyResult::Operand { 0xff808080, "" };
|
||||
}
|
||||
};
|
||||
|
||||
auto formatComment = [&](const cs_mips_op &operand) -> CommentPart {
|
||||
switch (operand.type) {
|
||||
case MIPS_OP_IMM:
|
||||
return {std::format("#{:X}, ", operand.is_unsigned ? operand.uimm : operand.imm)};
|
||||
case MIPS_OP_MEM:
|
||||
return {
|
||||
std::format("{}(0x{:X}), ", CapstoneToRegValue<std::string>(operand.mem.base), operand.mem.disp),
|
||||
CapstoneToRegValue<std::optional<u64>>(operand.mem.base).value() + (s16)operand.mem.disp,
|
||||
};
|
||||
case MIPS_OP_REG:
|
||||
return {std::format("{}, ", CapstoneToRegValue<std::string>(operand.reg))};
|
||||
default:
|
||||
return {"! Unknown !"};
|
||||
}
|
||||
};
|
||||
|
||||
for (u8 i = 0; i < details->mips.op_count && i < 3; i++) {
|
||||
result.ops[i] = formatOperand(details->mips.operands[i]);
|
||||
result.comment[i] = formatComment(details->mips.operands[i]);
|
||||
result.full += result.ops[i].str + "\t";
|
||||
}
|
||||
|
||||
cs_free(insn, count);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -5,6 +5,11 @@
|
||||
#include <array>
|
||||
|
||||
struct Disassembler {
|
||||
struct CommentPart {
|
||||
std::string str;
|
||||
u64 resolvedAddr = 0xFFFF'FFFF'FFFF'FFFFull;
|
||||
};
|
||||
|
||||
struct DisassemblyResult {
|
||||
bool success = false;
|
||||
std::string full;
|
||||
@@ -15,7 +20,23 @@ struct Disassembler {
|
||||
std::string str;
|
||||
};
|
||||
std::array<Operand, 3> ops{};
|
||||
std::string comment;
|
||||
std::array<CommentPart, 3> comment{};
|
||||
std::string GetFormattedComment() {
|
||||
std::string res{};
|
||||
for(auto& [str, _] : comment) {
|
||||
res += str;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
u64 GetResolvedAddressFromComment() {
|
||||
auto it = std::ranges::find_if(comment, [](auto& part) {
|
||||
return part.resolvedAddr != 0xFFFF'FFFF'FFFF'FFFFull;
|
||||
});
|
||||
|
||||
if(it == comment.end()) return 0xFFFF'FFFF'FFFF'FFFFull;
|
||||
return it->resolvedAddr;
|
||||
}
|
||||
};
|
||||
|
||||
~Disassembler() { cs_close(&handle); }
|
||||
@@ -25,12 +46,12 @@ struct Disassembler {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
[[nodiscard]] DisassemblyResult Disassemble(const u32 address) const;
|
||||
[[nodiscard]] DisassemblyResult DisassembleDetailed(u32 address, u32 instruction) const;
|
||||
[[nodiscard]] DisassemblyResult DisassembleSimple(u32 address, u32 instruction) const;
|
||||
private:
|
||||
std::string CapstoneToRegValue(mips_reg reg) const;
|
||||
template <typename T>
|
||||
T CapstoneToRegValue(mips_reg reg) const;
|
||||
explicit Disassembler(const 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) {
|
||||
|
||||
@@ -2,24 +2,34 @@
|
||||
#include <imgui.h>
|
||||
#include <execution>
|
||||
|
||||
void BreakpointFunc(s64 addr, s64 startAddr, const Disassembler::DisassemblyResult&) {
|
||||
void BreakpointFunc(s64 addr, s64 startAddr, Disassembler::DisassemblyResult&) {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
bool isBroken = core.breakpoints.contains(addr + 4);
|
||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0x800000ff);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.5f);
|
||||
if(ImGui::Checkbox(std::format("##toggleBreakpoint{}", (addr - startAddr) / 4).c_str(), &isBroken)) {
|
||||
core.ToggleBreakpoint(addr + 4);
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void AddressFunc(s64, s64, const Disassembler::DisassemblyResult& disasm) {
|
||||
if(disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
|
||||
void AddressFunc(s64, s64, Disassembler::DisassemblyResult& disasm) {
|
||||
if(!disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
|
||||
}
|
||||
|
||||
void InstructionFunc(s64, s64, const Disassembler::DisassemblyResult& disasm) {
|
||||
void InstructionFunc(s64, s64, Disassembler::DisassemblyResult& disasm) {
|
||||
if(!disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xffcbf1ae), "Disassembly unsuccessful...");
|
||||
return;
|
||||
@@ -46,17 +56,54 @@ void InstructionFunc(s64, s64, const Disassembler::DisassemblyResult& disasm) {
|
||||
}
|
||||
}
|
||||
|
||||
void CommentFunc(s64, s64, const Disassembler::DisassemblyResult& disasm) {
|
||||
if(disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xff71efe5), "%s", std::format("{}", disasm.comment).c_str());
|
||||
void CommentFunc(s64, s64, Disassembler::DisassemblyResult& disasm) {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
n64::Mem& mem = core.cpu->GetMem();
|
||||
n64::Registers& regs = core.cpu->GetRegs();
|
||||
|
||||
if(!disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xff71efe5), "");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextColored(ImColor(0xff71efe5), "");
|
||||
ImGui::TextColored(ImColor(0xff71efe5), "%s", std::format("{}", disasm.GetFormattedComment()).c_str());
|
||||
if(!ImGui::BeginItemTooltip())
|
||||
return;
|
||||
|
||||
ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", disasm.address).c_str());
|
||||
ImGui::BeginTable("##memoryContents", 16);
|
||||
for(int col = 0; col < 16; col++)
|
||||
ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for(int row = 0; row < 16; row++) {
|
||||
ImGui::TableNextRow();
|
||||
for(int col = 0; col < 16; col+=4) {
|
||||
u32 paddr;
|
||||
if(!regs.cop0.MapVAddr(n64::Cop0::LOAD, disasm.GetResolvedAddressFromComment(), paddr))
|
||||
continue;
|
||||
|
||||
u32 val = mem.Read<u32>(paddr);
|
||||
|
||||
ImGui::TableSetColumnIndex(col+0);
|
||||
ImGui::Text("%02X", val >> 24);
|
||||
ImGui::TableSetColumnIndex(col+1);
|
||||
ImGui::Text("%02X", val >> 16);
|
||||
ImGui::TableSetColumnIndex(col+2);
|
||||
ImGui::Text("%02X", val >> 8);
|
||||
ImGui::TableSetColumnIndex(col+3);
|
||||
ImGui::Text("%02X", val >> 0);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
bool Debugger::render() {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
n64::Registers& regs = core.cpu->GetRegs();
|
||||
|
||||
if(!enabled)
|
||||
return false;
|
||||
@@ -81,13 +128,13 @@ bool Debugger::render() {
|
||||
ImGui::SameLine(0,0);
|
||||
|
||||
if(followPC)
|
||||
startAddr = core.cpu->GetRegs().pc - 256; // TODO: arbitrary???
|
||||
startAddr = regs.pc - 256; // TODO: arbitrary???
|
||||
|
||||
if(ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) {
|
||||
core.ToggleBreakpoint(startAddr);
|
||||
}
|
||||
|
||||
ImGui::BeginTable("Disassembly", columns.size(), ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit);
|
||||
ImGui::BeginTable("Disassembly", columns.size(), ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody);
|
||||
for(int i = 0; i < columns.size(); i++)
|
||||
ImGui::TableSetupColumn(columns[i].name);
|
||||
|
||||
@@ -95,26 +142,31 @@ bool Debugger::render() {
|
||||
|
||||
for(u64 addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) {
|
||||
auto disasm = Disassembler::GetInstance().Disassemble(addr);
|
||||
auto shouldColorRed = addr == core.cpu->GetRegs().nextPC || core.breakpoints.contains(addr);
|
||||
if(shouldColorRed) {
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBg, 0x809a9ade);
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, 0x807777bf);
|
||||
auto addrIsCurrent = addr == regs.nextPC;
|
||||
auto addrIsBreakpoint = core.breakpoints.contains(addr);
|
||||
ImColor colorChoice = ImGui::GetStyle().Colors[ImGuiCol_TableRowBg];
|
||||
ImColor colorChoiceAlt = ImGui::GetStyle().Colors[ImGuiCol_TableRowBgAlt];
|
||||
if(addrIsCurrent) {
|
||||
colorChoice = 0x80e27fbc;
|
||||
colorChoiceAlt = 0x80e27fbc;
|
||||
}
|
||||
|
||||
if(addrIsBreakpoint) {
|
||||
colorChoice = 0x800000ff;
|
||||
colorChoiceAlt = 0x800000ff;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBg, colorChoice.Value);
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
for(int i = 0; i < columns.size(); i++) {
|
||||
ImGui::TableSetColumnIndex(i);
|
||||
columns[i].func(addr, startAddr, disasm);
|
||||
}
|
||||
|
||||
if(shouldColorRed) {
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
|
||||
if(ImGui::TableGetHoveredColumn() == 2) {
|
||||
// TODO: do the thing with the little fucking hover popup that shows the memory view
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
#include <backend/Core.hpp>
|
||||
|
||||
void BreakpointFunc(s64, s64, const Disassembler::DisassemblyResult&);
|
||||
void AddressFunc(s64, s64, const Disassembler::DisassemblyResult&);
|
||||
void InstructionFunc(s64, s64, const Disassembler::DisassemblyResult&);
|
||||
void CommentFunc(s64, s64, const Disassembler::DisassemblyResult&);
|
||||
void BreakpointFunc(s64, s64, Disassembler::DisassemblyResult&);
|
||||
void AddressFunc(s64, s64, Disassembler::DisassemblyResult&);
|
||||
void InstructionFunc(s64, s64, Disassembler::DisassemblyResult&);
|
||||
void CommentFunc(s64, s64, Disassembler::DisassemblyResult&);
|
||||
|
||||
class Debugger final {
|
||||
bool enabled = false;
|
||||
@@ -12,11 +12,11 @@ class Debugger final {
|
||||
|
||||
struct Column {
|
||||
const char* name;
|
||||
void (*func)(s64, s64, const Disassembler::DisassemblyResult&) = nullptr;
|
||||
void (*func)(s64, s64, Disassembler::DisassemblyResult&) = nullptr;
|
||||
};
|
||||
|
||||
std::array<Column, 4> columns = {
|
||||
Column{"Breakpoint", &BreakpointFunc},
|
||||
Column{"##BreakpointColumn", &BreakpointFunc},
|
||||
Column{"Address", &AddressFunc},
|
||||
Column{"Instruction", &InstructionFunc},
|
||||
Column{"Comment", &CommentFunc},
|
||||
|
||||
Reference in New Issue
Block a user