#include #include 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(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(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(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; }