250 lines
6.9 KiB
C++
250 lines
6.9 KiB
C++
#include <Debugger.hpp>
|
|
#include <imgui.h>
|
|
|
|
char const* regNames[] = {
|
|
"zero", "at", "v0", "v1",
|
|
"a0", "a1", "a2", "a3",
|
|
"t0", "t1", "t2", "t3",
|
|
"t4", "t5", "t6", "t7",
|
|
"s0", "s1", "s2", "s3",
|
|
"s4", "s5", "s6", "s7",
|
|
"t8", "t9", "k0", "k1",
|
|
"gp", "sp", "s8", "ra",
|
|
};
|
|
|
|
void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult&) {
|
|
n64::Core& core = n64::Core::GetInstance();
|
|
bool isBroken = core.breakpoints.contains(addr);
|
|
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).c_str(), &isBroken)) {
|
|
core.ToggleBreakpoint(addr);
|
|
}
|
|
ImGui::PopStyleVar();
|
|
ImGui::PopStyleColor();
|
|
ImGui::PopStyleColor();
|
|
ImGui::PopStyleColor();
|
|
ImGui::PopStyleColor();
|
|
}
|
|
|
|
void AddressFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
|
if(!disasm.success) {
|
|
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
|
|
return;
|
|
}
|
|
|
|
ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
|
|
}
|
|
|
|
void InstructionFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
|
if(!disasm.success) {
|
|
ImGui::TextColored(ImColor(0xffcbf1ae), "Disassembly unsuccessful...");
|
|
return;
|
|
}
|
|
|
|
ImGui::TextColored(ImColor(0xffcbf1ae), "%s", std::format("{} ", disasm.mnemonic).c_str());
|
|
ImGui::SameLine(0, 0);
|
|
for(int i = 0; i < 3; i++) {
|
|
if(disasm.ops[i].str.empty())
|
|
continue;
|
|
|
|
if(i >= 2) {
|
|
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", disasm.ops[i].str.c_str());
|
|
ImGui::SameLine(0, 0);
|
|
continue;
|
|
}
|
|
|
|
std::string op_str = disasm.ops[i].str;
|
|
if(!disasm.ops[i+1].str.empty())
|
|
op_str += ", ";
|
|
|
|
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str());
|
|
ImGui::SameLine(0, 0);
|
|
}
|
|
}
|
|
|
|
void Debugger::RegisterView() {
|
|
if(!ImGui::BeginTabItem("Registers"))
|
|
return;
|
|
|
|
if(!ImGui::BeginTable("##regs", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody))
|
|
return;
|
|
|
|
ImGui::TableSetupColumn("Name");
|
|
ImGui::TableSetupColumn("Value");
|
|
ImGui::TableSetupColumn("Name");
|
|
ImGui::TableSetupColumn("Value");
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
auto renderMemoryTable = [&](u64 vaddr) {
|
|
if(!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip))
|
|
return;
|
|
|
|
if(!ImGui::BeginTooltip())
|
|
return;
|
|
|
|
ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str());
|
|
if(!ImGui::BeginTable("##memoryContents", 16))
|
|
return;
|
|
|
|
for(u32 col = 0; col < 16; col++)
|
|
ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str());
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
for(u32 row = 0; row < 16; row++) {
|
|
ImGui::TableNextRow();
|
|
for(u32 col = 0; col < 16; col+=4) {
|
|
u32 paddr;
|
|
if (!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr + row * 0x10 + col, paddr))
|
|
continue;
|
|
|
|
const u32 val = n64::Core::GetMem().Read<u32>(paddr);
|
|
|
|
ImGui::TableSetColumnIndex(col+0);
|
|
ImGui::Text("%02X", (val >> 24) & 0xff);
|
|
ImGui::TableSetColumnIndex(col+1);
|
|
ImGui::Text("%02X", (val >> 16) & 0xff);
|
|
ImGui::TableSetColumnIndex(col+2);
|
|
ImGui::Text("%02X", (val >> 8) & 0xff);
|
|
ImGui::TableSetColumnIndex(col+3);
|
|
ImGui::Text("%02X", (val >> 0) & 0xff);
|
|
}
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
ImGui::EndTooltip();
|
|
};
|
|
|
|
n64::Registers& regs = n64::Core::GetRegs();
|
|
|
|
for(int i = 0; i < 32; i+=2) {
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::Text("%s", regNames[i]);
|
|
|
|
ImGui::TableSetColumnIndex(1);
|
|
auto value = regs.Read<u64>(i);
|
|
ImGui::Text("%s", std::format("{:016X}", value).c_str());
|
|
renderMemoryTable(value);
|
|
|
|
ImGui::TableSetColumnIndex(2);
|
|
ImGui::Text("%s", regNames[i+1]);
|
|
|
|
ImGui::TableSetColumnIndex(3);
|
|
value = regs.Read<u64>(i+1);
|
|
ImGui::Text("%s", std::format("{:016X}", value).c_str());
|
|
renderMemoryTable(value);
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
bool Debugger::render() {
|
|
n64::Core &core = n64::Core::GetInstance();
|
|
const n64::Registers& regs = n64::Core::GetRegs();
|
|
|
|
if(!enabled)
|
|
return false;
|
|
|
|
static s64 startAddr = 0xFFFF'FFFF'8000'0000;
|
|
constexpr int step = 4;
|
|
constexpr int stepFast = 256;
|
|
|
|
if(!ImGui::Begin("Debugger", &enabled)) {
|
|
ImGui::End();
|
|
return false;
|
|
}
|
|
|
|
ImGui::BeginDisabled(followPC);
|
|
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX", ImGuiInputTextFlags_CharsHexadecimal);
|
|
ImGui::EndDisabled();
|
|
|
|
ImGui::Text("Follow program counter:");
|
|
ImGui::SameLine(0,0);
|
|
|
|
ImGui::Checkbox("##followPC", &followPC);
|
|
ImGui::SameLine(0,0);
|
|
|
|
ImGui::Text("Add a breakpoint");
|
|
ImGui::SameLine(0,0);
|
|
|
|
if(followPC)
|
|
startAddr = regs.pc - 256; // TODO: arbitrary???
|
|
|
|
if (ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) {
|
|
core.ToggleBreakpoint(startAddr);
|
|
}
|
|
|
|
if(!ImGui::BeginTabBar("##debuggerTabs")) {
|
|
ImGui::EndTabBar();
|
|
ImGui::End();
|
|
return false;
|
|
}
|
|
|
|
RegisterView();
|
|
|
|
if(!ImGui::BeginTabItem("MIPS R4300i code view")) {
|
|
ImGui::EndTabBar();
|
|
ImGui::End();
|
|
return false;
|
|
}
|
|
|
|
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter |
|
|
ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
|
|
|
|
if(!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
|
|
ImGui::EndTabBar();
|
|
ImGui::End();
|
|
return false;
|
|
}
|
|
|
|
for(auto &[name, _] : columns)
|
|
ImGui::TableSetupColumn(name);
|
|
|
|
ImGui::TableHeadersRow();
|
|
|
|
for(auto addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) {
|
|
auto disasm = Disassembler::GetInstance().Disassemble(addr);
|
|
const auto addrIsCurrent = addr == regs.nextPC;
|
|
const 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; auto &[_, func] : columns) {
|
|
ImGui::TableSetColumnIndex(i++);
|
|
func(addr, disasm);
|
|
}
|
|
|
|
ImGui::PopStyleColor();
|
|
ImGui::PopStyleColor();
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
|
|
ImGui::EndTabItem();
|
|
ImGui::EndTabBar();
|
|
|
|
ImGui::End();
|
|
return true;
|
|
}
|