| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
#include "imgui.h" |
| 9 |
#include "debug_ui_panels.h" |
| 10 |
#include "debug_ui_bridge.h" |
| 11 |
#include "debug_ui_widgets.h" |
| 12 |
|
| 13 |
#include <stdint.h> |
| 14 |
#include <string.h> |
| 15 |
#include <cctype> |
| 16 |
#include <vector> |
| 17 |
#include <algorithm> |
| 18 |
|
| 19 |
namespace { |
| 20 |
|
| 21 |
char s_filter[64] = ""; |
| 22 |
|
| 23 |
|
| 24 |
bool |
| 25 |
contains_ci(const char *hay, const char *needle) |
| 26 |
{ |
| 27 |
if (!needle[0]) |
| 28 |
return true; |
| 29 |
size_t nl = strlen(needle); |
| 30 |
for (const char *p = hay; *p; ++p) { |
| 31 |
size_t i = 0; |
| 32 |
while (i < nl && p[i] && |
| 33 |
tolower((unsigned char)p[i]) == tolower((unsigned char)needle[i])) |
| 34 |
i++; |
| 35 |
if (i == nl) |
| 36 |
return true; |
| 37 |
} |
| 38 |
return false; |
| 39 |
} |
| 40 |
|
| 41 |
|
| 42 |
int |
| 43 |
sym_stricmp(const char *a, const char *b) |
| 44 |
{ |
| 45 |
for (;; a++, b++) { |
| 46 |
int ca = tolower((unsigned char)*a); |
| 47 |
int cb = tolower((unsigned char)*b); |
| 48 |
if (ca != cb) |
| 49 |
return ca - cb; |
| 50 |
if (!ca) |
| 51 |
return 0; |
| 52 |
} |
| 53 |
} |
| 54 |
|
| 55 |
|
| 56 |
void |
| 57 |
sym_toggle_bp(uint16_t addr) |
| 58 |
{ |
| 59 |
|
| 60 |
int x16 = (addr >= 0xA000) |
| 61 |
? (addr < 0xC000 ? (int)memory_get_ram_bank() : (int)memory_get_rom_bank()) |
| 62 |
: -1; |
| 63 |
if (!DEBUGRemoveBreakPoint((int)addr, 0, x16)) { |
| 64 |
struct breakpoint bp = {}; |
| 65 |
bp.pc = (int)addr; |
| 66 |
bp.bank = 0; |
| 67 |
bp.x16Bank = x16; |
| 68 |
DEBUGAddBreakPoint(bp); |
| 69 |
} |
| 70 |
} |
| 71 |
|
| 72 |
struct SymRow { |
| 73 |
const char *name; |
| 74 |
uint16_t addr; |
| 75 |
}; |
| 76 |
|
| 77 |
void |
| 78 |
symbols_panel_render(bool *p_open) |
| 79 |
{ |
| 80 |
if (!ImGui::Begin("Symbols", p_open)) { |
| 81 |
dbgui_window_end(); |
| 82 |
return; |
| 83 |
} |
| 84 |
dbgui_window_zoom("symbols"); |
| 85 |
|
| 86 |
const ImVec4 orange(1.0f, 0.78f, 0.35f, 1.0f); |
| 87 |
|
| 88 |
if (!dbg_info_is_loaded() || dbg_info_symbol_count() == 0) { |
| 89 |
ImGui::TextColored(orange, "No symbols loaded."); |
| 90 |
ImGui::Spacing(); |
| 91 |
ImGui::TextWrapped("Symbols come from cc65 .dbg address labels. Start with " |
| 92 |
"-dbgfile <prog.dbg> (and -srcpath if needed), or LOAD a " |
| 93 |
"program whose matching .dbg auto-loads."); |
| 94 |
dbgui_window_end(); |
| 95 |
return; |
| 96 |
} |
| 97 |
|
| 98 |
|
| 99 |
ImGui::SetNextItemWidth(220); |
| 100 |
ImGui::InputTextWithHint("##symfilter", "filter by name...", s_filter, sizeof s_filter); |
| 101 |
ImGui::SameLine(); |
| 102 |
ImGui::TextDisabled("%d symbols", dbg_info_symbol_count()); |
| 103 |
ImGui::SameLine(); |
| 104 |
ImGui::TextDisabled("(double-click = go to; right-click for more)"); |
| 105 |
|
| 106 |
|
| 107 |
static std::vector<SymRow> rows; |
| 108 |
rows.clear(); |
| 109 |
int n = dbg_info_symbol_count(); |
| 110 |
for (int i = 0; i < n; i++) { |
| 111 |
const char *nm = nullptr; |
| 112 |
uint16_t a; |
| 113 |
if (debug_ui_symbol_at(i, &nm, &a) && nm && contains_ci(nm, s_filter)) |
| 114 |
rows.push_back({nm, a}); |
| 115 |
} |
| 116 |
|
| 117 |
ImGuiTableFlags tflags = ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerV | |
| 118 |
ImGuiTableFlags_ScrollY | ImGuiTableFlags_Sortable | |
| 119 |
ImGuiTableFlags_Resizable; |
| 120 |
if (ImGui::BeginTable("symbols", 4, tflags, ImVec2(0, 0))) { |
| 121 |
ImGui::TableSetupScrollFreeze(0, 1); |
| 122 |
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_DefaultSort); |
| 123 |
ImGui::TableSetupColumn("Addr", ImGuiTableColumnFlags_WidthFixed); |
| 124 |
ImGui::TableSetupColumn("Byte", ImGuiTableColumnFlags_WidthFixed); |
| 125 |
ImGui::TableSetupColumn("Word", ImGuiTableColumnFlags_WidthFixed); |
| 126 |
ImGui::TableHeadersRow(); |
| 127 |
|
| 128 |
|
| 129 |
if (ImGuiTableSortSpecs *ss = ImGui::TableGetSortSpecs()) { |
| 130 |
if (ss->SpecsCount > 0) { |
| 131 |
const ImGuiTableColumnSortSpecs &s = ss->Specs[0]; |
| 132 |
bool asc = s.SortDirection != ImGuiSortDirection_Descending; |
| 133 |
if (s.ColumnIndex == 0) { |
| 134 |
std::sort(rows.begin(), rows.end(), [asc](const SymRow &a, const SymRow &b) { |
| 135 |
int c = sym_stricmp(a.name, b.name); |
| 136 |
return asc ? c < 0 : c > 0; |
| 137 |
}); |
| 138 |
} else { |
| 139 |
std::sort(rows.begin(), rows.end(), [asc](const SymRow &a, const SymRow &b) { |
| 140 |
return asc ? a.addr < b.addr : a.addr > b.addr; |
| 141 |
}); |
| 142 |
} |
| 143 |
} |
| 144 |
} |
| 145 |
|
| 146 |
ImGuiListClipper clip; |
| 147 |
clip.Begin((int)rows.size()); |
| 148 |
while (clip.Step()) { |
| 149 |
for (int i = clip.DisplayStart; i < clip.DisplayEnd; i++) { |
| 150 |
const SymRow &r = rows[i]; |
| 151 |
uint8_t b = debug_ui_read6502(r.addr, 0, DEBUG_UI_CURRENT_BANK); |
| 152 |
uint8_t b2 = debug_ui_read6502((uint16_t)(r.addr + 1), 0, DEBUG_UI_CURRENT_BANK); |
| 153 |
uint16_t w = (uint16_t)(b | (b2 << 8)); |
| 154 |
|
| 155 |
ImGui::TableNextRow(); |
| 156 |
ImGui::PushID(i); |
| 157 |
|
| 158 |
ImGui::TableSetColumnIndex(0); |
| 159 |
if (ImGui::Selectable(r.name, false, |
| 160 |
ImGuiSelectableFlags_SpanAllColumns | |
| 161 |
ImGuiSelectableFlags_AllowDoubleClick) && |
| 162 |
ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { |
| 163 |
debug_ui_request_goto(r.addr, 0); |
| 164 |
} |
| 165 |
if (ImGui::BeginPopupContextItem("symctx")) { |
| 166 |
if (ImGui::MenuItem("Go to (disasm + memory)")) |
| 167 |
debug_ui_request_goto(r.addr, 0); |
| 168 |
if (ImGui::MenuItem("Toggle breakpoint")) |
| 169 |
sym_toggle_bp(r.addr); |
| 170 |
ImGui::BeginDisabled(!DEBUGIsPaused()); |
| 171 |
if (ImGui::MenuItem("Run to here")) |
| 172 |
DEBUGRunTo(r.addr, 0, DEBUG_OWNER_UI); |
| 173 |
ImGui::EndDisabled(); |
| 174 |
ImGui::EndPopup(); |
| 175 |
} |
| 176 |
|
| 177 |
ImGui::TableSetColumnIndex(1); |
| 178 |
ImGui::Text("$%04X", r.addr); |
| 179 |
ImGui::TableSetColumnIndex(2); |
| 180 |
ImGui::Text("$%02X", b); |
| 181 |
ImGui::TableSetColumnIndex(3); |
| 182 |
ImGui::Text("$%04X", w); |
| 183 |
|
| 184 |
ImGui::PopID(); |
| 185 |
} |
| 186 |
} |
| 187 |
ImGui::EndTable(); |
| 188 |
} |
| 189 |
|
| 190 |
dbgui_window_end(); |
| 191 |
} |
| 192 |
|
| 193 |
DebugPanelRegistration s_reg("Symbols", symbols_panel_render, true); |
| 194 |
|
| 195 |
} |