| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
#ifndef DEBUG_UI_INSN_TOOLTIP_H |
| 14 |
#define DEBUG_UI_INSN_TOOLTIP_H |
| 15 |
|
| 16 |
#include "imgui.h" |
| 17 |
#include "debug_ui_bridge.h" |
| 18 |
|
| 19 |
#include <stdint.h> |
| 20 |
#include <ctype.h> |
| 21 |
|
| 22 |
extern "C" { |
| 23 |
#include "insn_info.h" |
| 24 |
#include "disasm.h" |
| 25 |
#include "dbg_info.h" |
| 26 |
} |
| 27 |
|
| 28 |
|
| 29 |
|
| 30 |
|
| 31 |
|
| 32 |
|
| 33 |
|
| 34 |
static inline void |
| 35 |
dbgui_instruction_tooltip_body(const char *mnemonic, uint16_t addr, uint8_t bank, bool at_pc) |
| 36 |
{ |
| 37 |
const insn_info_t *insn = insn_info_lookup(mnemonic); |
| 38 |
if (!insn) { |
| 39 |
return; |
| 40 |
} |
| 41 |
|
| 42 |
ImGui::Text("%s - %s", insn->mnemonic, insn->title); |
| 43 |
ImGui::TextDisabled("%s", insn->summary); |
| 44 |
|
| 45 |
char flags[32]; |
| 46 |
insn_info_flag_text(insn->flags_set, flags, sizeof flags); |
| 47 |
if (flags[0]) { |
| 48 |
ImGui::Separator(); |
| 49 |
ImGui::TextDisabled("affects: %s", flags); |
| 50 |
} |
| 51 |
|
| 52 |
if (!at_pc) { |
| 53 |
return; |
| 54 |
} |
| 55 |
|
| 56 |
char dis[128]; |
| 57 |
int32_t eff = -1; |
| 58 |
const int16_t x16bank = (int16_t)DEBUG_UI_CURRENT_BANK; |
| 59 |
const int nbytes = disasm(addr, bank, nullptr, dis, sizeof dis, x16bank, |
| 60 |
regs.status, &eff); |
| 61 |
|
| 62 |
ImGui::Separator(); |
| 63 |
ImGui::TextColored(ImVec4(0.55f, 0.85f, 1.0f, 1.0f), "At the PC: %s", dis); |
| 64 |
|
| 65 |
const bool acc16 = regs.is65c816 && !regs.e && !(regs.status & FLAG_MEMORY_WIDTH); |
| 66 |
const bool idx16 = regs.is65c816 && !regs.e && !(regs.status & FLAG_INDEX_WIDTH); |
| 67 |
const int w = acc16 ? 4 : 2; |
| 68 |
|
| 69 |
uint16_t memval = 0; |
| 70 |
bool have_mem = false; |
| 71 |
if (eff >= 0) { |
| 72 |
uint8_t lo = debug_ui_read6502((uint16_t)eff, 0, x16bank); |
| 73 |
uint8_t hi = debug_ui_read6502((uint16_t)(eff + 1), 0, x16bank); |
| 74 |
memval = acc16 ? (uint16_t)(lo | (hi << 8)) : lo; |
| 75 |
have_mem = true; |
| 76 |
ImGui::TextDisabled("operand $%04X = $%0*X", (unsigned)eff, w, memval); |
| 77 |
} |
| 78 |
|
| 79 |
const ImVec4 will(0.45f, 0.95f, 0.45f, 1.0f); |
| 80 |
const ImVec4 wont(0.95f, 0.70f, 0.30f, 1.0f); |
| 81 |
const char m0 = (char)tolower((unsigned char)mnemonic[0]); |
| 82 |
const char m1 = mnemonic[1] ? (char)tolower((unsigned char)mnemonic[1]) : '\0'; |
| 83 |
const char m2 = mnemonic[1] && mnemonic[2] ? (char)tolower((unsigned char)mnemonic[2]) : '\0'; |
| 84 |
|
| 85 |
switch (insn->kind) { |
| 86 |
case INSN_KIND_BRANCH: |
| 87 |
if (insn->branch_flag) { |
| 88 |
const uint8_t f = insn->branch_flag; |
| 89 |
const bool set = (regs.status & f) != 0; |
| 90 |
const bool taken = (set == insn->branch_when_set); |
| 91 |
const char *fname = f == INSN_FLAG_Z ? "Z" |
| 92 |
: f == INSN_FLAG_C ? "C" |
| 93 |
: f == INSN_FLAG_N ? "N" |
| 94 |
: "V"; |
| 95 |
|
| 96 |
|
| 97 |
uint16_t target = (uint16_t)(eff >= 0 ? eff : 0); |
| 98 |
if (eff < 0 && nbytes >= 2) { |
| 99 |
const int8_t d = (int8_t)debug_ui_read6502((uint16_t)(addr + 1), bank, x16bank); |
| 100 |
target = (uint16_t)(addr + nbytes + d); |
| 101 |
} |
| 102 |
ImGui::Text("tests %s (currently %d), branches when %s", fname, set ? 1 : 0, |
| 103 |
insn->branch_when_set ? "set" : "clear"); |
| 104 |
if (taken) |
| 105 |
ImGui::TextColored(will, "WILL branch to $%04X", (unsigned)target); |
| 106 |
else |
| 107 |
ImGui::TextColored(wont, "will NOT branch - continues at $%04X", |
| 108 |
(unsigned)((addr + nbytes) & 0xFFFF)); |
| 109 |
const char *tn = nullptr; |
| 110 |
if (taken && dbg_info_addr_to_label(target, &tn) && tn) |
| 111 |
ImGui::TextDisabled("target: %s", tn); |
| 112 |
ImGui::TextDisabled("A=$%02X X=$%02X Y=$%02X P=$%02X", regs.a, regs.xl, regs.yl, |
| 113 |
regs.status); |
| 114 |
} |
| 115 |
break; |
| 116 |
|
| 117 |
case INSN_KIND_LOAD: |
| 118 |
if (have_mem) { |
| 119 |
const char *reg = m2 == 'a' ? "A" : (m2 == 'x' ? "X" : "Y"); |
| 120 |
ImGui::TextColored(will, "%s will become $%0*X", reg, w, memval); |
| 121 |
ImGui::TextDisabled("then Z=%d N=%d", memval == 0 ? 1 : 0, |
| 122 |
(memval & (acc16 ? 0x8000 : 0x80)) ? 1 : 0); |
| 123 |
} |
| 124 |
break; |
| 125 |
|
| 126 |
case INSN_KIND_STORE: |
| 127 |
if (eff >= 0) { |
| 128 |
const uint16_t v = m2 == 'a' ? (acc16 ? regs.c : regs.a) |
| 129 |
: m2 == 'x' ? (idx16 ? regs.x : regs.xl) |
| 130 |
: m2 == 'y' ? (idx16 ? regs.y : regs.yl) |
| 131 |
: 0; |
| 132 |
ImGui::TextColored(will, "$%04X will become $%0*X", (unsigned)eff, w, v); |
| 133 |
if (have_mem && v == memval) |
| 134 |
ImGui::TextDisabled("(no change - already that value)"); |
| 135 |
} |
| 136 |
break; |
| 137 |
|
| 138 |
case INSN_KIND_COMPARE: |
| 139 |
if (have_mem) { |
| 140 |
const uint16_t r = m1 == 'm' ? (acc16 ? regs.c : regs.a) |
| 141 |
: m2 == 'x' ? (idx16 ? regs.x : regs.xl) |
| 142 |
: (idx16 ? regs.y : regs.yl); |
| 143 |
ImGui::Text("compares $%0*X with $%0*X", w, r, w, memval); |
| 144 |
ImGui::TextColored(will, "result: %s (Z=%d C=%d)", |
| 145 |
r == memval ? "equal" : (r > memval ? "greater" : "less"), |
| 146 |
r == memval ? 1 : 0, r >= memval ? 1 : 0); |
| 147 |
} |
| 148 |
break; |
| 149 |
|
| 150 |
case INSN_KIND_JUMP: |
| 151 |
case INSN_KIND_RETURN: |
| 152 |
if (eff >= 0) { |
| 153 |
ImGui::TextColored(will, "transfers control to $%04X", (unsigned)eff); |
| 154 |
const char *nm = nullptr; |
| 155 |
if (dbg_info_addr_to_label((uint16_t)eff, &nm) && nm) |
| 156 |
ImGui::TextDisabled("target: %s", nm); |
| 157 |
} |
| 158 |
break; |
| 159 |
|
| 160 |
case INSN_KIND_INCDEC: |
| 161 |
if (have_mem) { |
| 162 |
const bool up = (m0 == 'i'); |
| 163 |
const unsigned nv = (unsigned)((up ? memval + 1 : memval - 1) & |
| 164 |
(acc16 ? 0xFFFF : 0xFF)); |
| 165 |
ImGui::TextColored(will, "$%04X: $%0*X -> $%0*X", (unsigned)eff, w, memval, w, nv); |
| 166 |
} |
| 167 |
break; |
| 168 |
|
| 169 |
default: |
| 170 |
break; |
| 171 |
} |
| 172 |
|
| 173 |
ImGui::TextDisabled("%d byte%s", nbytes, nbytes == 1 ? "" : "s"); |
| 174 |
} |
| 175 |
|
| 176 |
|
| 177 |
|
| 178 |
static inline bool |
| 179 |
dbgui_mnemonic_of(const char *disasm_line, char *out, size_t outsz) |
| 180 |
{ |
| 181 |
if (!disasm_line || !out || outsz == 0) { |
| 182 |
return false; |
| 183 |
} |
| 184 |
while (*disasm_line == ' ' || *disasm_line == '\t') { |
| 185 |
disasm_line++; |
| 186 |
} |
| 187 |
size_t n = 0; |
| 188 |
while (disasm_line[n] && !isspace((unsigned char)disasm_line[n]) && n + 1 < outsz) { |
| 189 |
out[n] = disasm_line[n]; |
| 190 |
n++; |
| 191 |
} |
| 192 |
out[n] = '\0'; |
| 193 |
return n > 0; |
| 194 |
} |
| 195 |
|
| 196 |
#endif |