diff --git a/src/backend/core/Disassembler.cpp b/src/backend/core/Disassembler.cpp index b1fd9b47..90f4b2cf 100644 --- a/src/backend/core/Disassembler.cpp +++ b/src/backend/core/Disassembler.cpp @@ -1,99 +1,9 @@ #include #include +#include -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(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 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(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>(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(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(operand.mem.base), operand.mem.disp), + CapstoneToRegValue>(operand.mem.base).value() + (s16)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[i] = formatComment(details->mips.operands[i]); + result.full += result.ops[i].str + "\t"; + } + + cs_free(insn, count); + + return result; } \ No newline at end of file diff --git a/src/backend/core/Disassembler.hpp b/src/backend/core/Disassembler.hpp index 5a1d9c9a..dfb45733 100644 --- a/src/backend/core/Disassembler.hpp +++ b/src/backend/core/Disassembler.hpp @@ -5,6 +5,11 @@ #include 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 ops{}; - std::string comment; + std::array 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); } @@ -24,13 +45,13 @@ struct Disassembler { static Disassembler ret(rsp); 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 + T CapstoneToRegValue(mips_reg reg) const; explicit Disassembler(const bool rsp) : rsp(rsp) { if (cs_open(CS_ARCH_MIPS, static_cast((rsp ? CS_MODE_32 : CS_MODE_64) | CS_MODE_BIG_ENDIAN), &handle) != CS_ERR_OK) { diff --git a/src/frontend/Debugger.cpp b/src/frontend/Debugger.cpp index d992f98d..5a05f20f 100644 --- a/src/frontend/Debugger.cpp +++ b/src/frontend/Debugger.cpp @@ -2,24 +2,34 @@ #include #include -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(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(); diff --git a/src/frontend/Debugger.hpp b/src/frontend/Debugger.hpp index 74d6ca5a..eb5c6705 100644 --- a/src/frontend/Debugger.hpp +++ b/src/frontend/Debugger.hpp @@ -1,10 +1,10 @@ #pragma once #include -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 columns = { - Column{"Breakpoint", &BreakpointFunc}, + Column{"##BreakpointColumn", &BreakpointFunc}, Column{"Address", &AddressFunc}, Column{"Instruction", &InstructionFunc}, Column{"Comment", &CommentFunc},