openMSX
ImGuiDebugger.cc
Go to the documentation of this file.
1#include "ImGuiDebugger.hh"
2
4#include "ImGuiBreakPoints.hh"
5#include "ImGuiCharacter.hh"
6#include "ImGuiCpp.hh"
7#include "ImGuiManager.hh"
8#include "ImGuiPalette.hh"
10#include "ImGuiSymbols.hh"
11#include "ImGuiUtils.hh"
12#include "ImGuiVdpRegs.hh"
13#include "ImGuiWatchExpr.hh"
14
15#include "CPURegs.hh"
16#include "Dasm.hh"
17#include "Debuggable.hh"
18#include "Debugger.hh"
19#include "MSXCPU.hh"
20#include "MSXCPUInterface.hh"
22#include "MSXMotherBoard.hh"
23#include "RomPlain.hh"
24#include "RomInfo.hh"
25#include "SymbolManager.hh"
26
27#include "narrow.hh"
28#include "join.hh"
29#include "ranges.hh"
30#include "stl.hh"
31#include "strCat.hh"
32#include "StringOp.hh"
33#include "view.hh"
34
35#include <imgui.h>
36#include <imgui_stdlib.h>
37
38#include <cstdint>
39#include <vector>
40
41using namespace std::literals;
42
43namespace openmsx {
44
46 : ImGuiPart(manager_)
47 , symbolManager(manager.getReactor().getSymbolManager())
48{
49}
50
52
53// TODO quick and dirty, doesn't look right yet
54// Created from a .ttf file via:
55// binary_to_compressed_c openmsx-debugger-icons.ttf myIcons > myfont.cpp
56// https://github.com/ocornut/imgui/blob/master/misc/fonts/binary_to_compressed_c.cpp
57static const char icons_compressed_base85[1815+1] =
58 "7])#######;9u/('/###W),##.f1$#Q6>##%[n42)[KU%G5)=-<NE/1aNV=BZrPSb]->>#ICi9.o`(*HGsO2(b6O3L+lQS%,5LsCC,H3AnAMeNA*&#Gb';9Cs3BMNvSN`sr1dD4Eo3R/"
59 "w/)8eXHkG2V3dW.;h^`I@cj`W)a4&>ZW%q/o6Q<Bou@GMQuuoJH`;-*0iR/Go%fr6jMWVRReK?-qmnUCC'Hv'<J]p]`;`Y5@C=GH?o6O+j#)*Mp]G&#70lQ#;0xfLQ7wj#O?DX-@1>F%"
60 "5[)<#8JFgL6SgV-xew@'uB5X7If]8QR3O%b'kS`a)%vW-iXx6*BfW>-'lB#$1fs-$R'Mk+(lB#$ckf&,peu.:J/PQ'-MYY#jFs9)*lXJgaDe8Qw(Y:v47Y&#*r[w'WQ1x'1/V:m^U4+3"
61 "7GJcM604,NJ:-##O]-x-e:k(NX@$##]jP]4ACh^X0(^fLHl/o#g]L7#N'+&#L)Qv$t@0+*I5^+4]77<.aQ9k0$g`,3$+h,3Pqji06rs-$=uG=3tq$>MOAQY>@'.`&K8P>#g>AvL9q*<#"
62 ")EEjLr^PA#m.Us-KRb'4g)Cb4%IuD#GKRL2KkRP/N7R20t3wK#-8/$jJA5U;0viS&[5AC>uIVS%3^oh24GbM(hIL>#xP%U#WE323lpd].B[A30GK_+,sX/D=dn5q&0####GBO&#.1d7$"
63 "3g1$#prgo.-<Tv->;gF4LZ>x#<)]L(/p^I*^nr?#&Y@C#cF&@'wvPJ(?nU^#pRpQ0gF*j0EPtdVN'+9%k*l)*0qi<%>=+dVKrdx0Iu(kLgk4DWX?/YGZv9dMb/q8/+fMO#W+WBOM>#`Q"
64 "xR--3bO1F*.D,G4rFuG-hwhH-AI7q.3B3I):[(cJ[pl6&]3KI%)B1se0fv]M6T3kuGSoBJNCZY#q+er?Y,8v,5cNY5*`$s$#]YV-[@i?#=@[s$G+TC4P#l8./=]s$1Pev6jlje3&-Xf-"
65 "e](jr145d3?;Rv$ZUvC%h5%fqxC$Y@8^CY$O,@H),W'Z-W-Rh27<C[Krsf;$.wg<L9br,4%.6v,>=+dVIIx:/@JJH)Z:Nu.iUkJ2tpPm:t0(Q$@OEp%Upn;%j&5QCa)iS0%5YY#IX$_]"
66 "2^x^]Zrju5Fhf-*i+YV-6'PA#(cK+*mtq;/qwkj1?f6<.0MbI),VW/23>%&4m$ie3%&AA4l'eq7+jkA#dlfj-&$+&^S8VTd.^AN-,CeM0l,'hoodPir`IofLX=$##5C.%T=UYF%^Yk*$"
67 "/)m<-]'LS-&%.20'>uu#OYU7#euQ['GPSw8P`&:)AF14+&YF&#WW`>$Mbk-$I&l-$Bp0.$eo4wLW(I3#Ys%(#U-4&#MXN1#EIc>#ik*$M0xSfLRq)$#@>[A-$Mg;-'Spr-gN<;Ni;-##"
68 "Jer=--`/,Mv_d##tg`=-5X`=-8sG<-*fG<-+C`T.3f1$#mrG<-8X`=-/#XN0Ht*A#e5>##_RFgLX%1kLF=`T.3xL$#)6xU.R,_'#RA`T.T5$C#<rG<-M2(@-xrG<-DKx>-#EfT%8nT`3"
69 "]kU.G-i@+H=DpTCuAo6D<B?hD-rI+HXhdxFL7-AFY'auGt$EJ1m,(@'un@VHdt6L#s[DYGB0-gD.X`'/4+auGpkn+H5-xF-^%/(#B_=?-kQU@-QRt7/uJ6(#Cm[*b[^es/c:@bHdN4/#"
70 "d>?L-NNei.Fn@-#afh]%mxtLF//5UCvs.>B,(rE-i<cM:^0[eF/1u'#.mk.#[5C/#-)IqLR`4rL;wXrLX.lrL1x.qLf.K-#J#`5/%$S-#c7]nLNM-X.Q####F/LMTxS&=u<7L8#";
71
72static constexpr ImWchar DEBUGGER_ICON_MIN = 0xead1;
73static constexpr ImWchar DEBUGGER_ICON_MAX = 0xeb8f;
74static constexpr ImWchar DEBUGGER_ICON_RUN = 0xead3;
75static constexpr ImWchar DEBUGGER_ICON_BREAK = 0xead1;
76static constexpr ImWchar DEBUGGER_ICON_STEP_IN = 0xead4;
77static constexpr ImWchar DEBUGGER_ICON_STEP_OUT = 0xead5;
78static constexpr ImWchar DEBUGGER_ICON_STEP_OVER = 0xead6;
79static constexpr ImWchar DEBUGGER_ICON_STEP_BACK = 0xeb8f;
80
82{
83 auto& io = ImGui::GetIO();
84 ImFontConfig icons_config; icons_config.MergeMode = true; icons_config.PixelSnapH = true;
85 static constexpr std::array<ImWchar, 3> ranges = {DEBUGGER_ICON_MIN, DEBUGGER_ICON_MAX, 0};
86 io.Fonts->AddFontFromMemoryCompressedBase85TTF(icons_compressed_base85, 20.0f, &icons_config, ranges.data());
87}
88
90{
91 syncDisassemblyWithPC = true;
92}
93
94void ImGuiDebugger::save(ImGuiTextBuffer& buf)
95{
96 savePersistent(buf, *this, persistentElements);
97
98 // TODO in the future use c++23 std::chunk_by
99 auto it = hexEditors.begin();
100 auto et = hexEditors.end();
101 while (it != et) {
102 int count = 0;
103 auto name = std::string((*it)->getDebuggableName());
104 do {
105 ++it;
106 ++count;
107 } while (it != et && (*it)->getDebuggableName() == name);
108 buf.appendf("hexEditor.%s=%d\n", name.c_str(), count);
109 }
110
111}
112
114{
115 hexEditors.clear();
116}
117
118void ImGuiDebugger::loadLine(std::string_view name, zstring_view value)
119{
120 static constexpr std::string_view prefix = "hexEditor.";
121
122 if (loadOnePersistent(name, value, *this, persistentElements)) {
123 // already handled
124 } else if (name.starts_with(prefix)) {
125 if (auto r = StringOp::stringTo<unsigned>(value)) {
126 auto debuggableName = std::string(name.substr(prefix.size()));
127 auto [b, e] = ranges::equal_range(hexEditors, debuggableName, {}, &DebuggableEditor::getDebuggableName);
128 auto index = std::distance(b, e); // expected to be 0, but be robust against imgui.ini changes
129 for (auto i : xrange(*r)) {
130 e = hexEditors.insert(e, std::make_unique<DebuggableEditor>(manager, debuggableName, index + i));
131 ++e;
132 }
133 }
134 }
135}
136
138{
139 auto createHexEditor = [&](const std::string& name) {
140 // prefer to reuse a previously closed editor
141 auto [b, e] = ranges::equal_range(hexEditors, name, {}, &DebuggableEditor::getDebuggableName);
142 for (auto it = b; it != e; ++it) {
143 if (!(*it)->open) {
144 (*it)->open = true;
145 return;
146 }
147 }
148 // or create a new one
149 auto index = std::distance(b, e);
150 auto it = hexEditors.insert(e, std::make_unique<DebuggableEditor>(manager, name, index));
151 (*it)->open = true;
152 };
153
154 im::Menu("Debugger", motherBoard != nullptr, [&]{
155 ImGui::MenuItem("Tool bar", nullptr, &showControl);
156 ImGui::MenuItem("Disassembly", nullptr, &showDisassembly);
157 ImGui::MenuItem("CPU registers", nullptr, &showRegisters);
158 ImGui::MenuItem("CPU flags", nullptr, &showFlags);
159 ImGui::MenuItem("Slots", nullptr, &showSlots);
160 ImGui::MenuItem("Stack", nullptr, &showStack);
161 auto it = ranges::lower_bound(hexEditors, "memory", {}, &DebuggableEditor::getDebuggableName);
162 bool memoryOpen = (it != hexEditors.end()) && (*it)->open;
163 if (ImGui::MenuItem("Memory", nullptr, &memoryOpen)) {
164 if (memoryOpen) {
165 createHexEditor("memory");
166 } else {
167 assert(it != hexEditors.end());
168 (*it)->open = false;
169 }
170 }
171 ImGui::Separator();
172 ImGui::MenuItem("Breakpoints", nullptr, &manager.breakPoints->show);
173 ImGui::MenuItem("Symbol manager", nullptr, &manager.symbols->show);
174 ImGui::MenuItem("Watch expression", nullptr, &manager.watchExpr->show);
175 ImGui::Separator();
176 ImGui::MenuItem("VDP bitmap viewer", nullptr, &manager.bitmap->showBitmapViewer);
177 ImGui::MenuItem("VDP tile viewer", nullptr, &manager.character->show);
178 ImGui::MenuItem("VDP sprite viewer", nullptr, &manager.sprite->show);
179 ImGui::MenuItem("VDP register viewer", nullptr, &manager.vdpRegs->show);
180 ImGui::MenuItem("Palette editor", nullptr, &manager.palette->window.open);
181 ImGui::Separator();
182 im::Menu("Add hex editor", [&]{
183 const auto& debugger = motherBoard->getDebugger();
184 auto debuggables = to_vector<std::pair<std::string, Debuggable*>>(debugger.getDebuggables());
185 ranges::sort(debuggables, StringOp::caseless{}, [](const auto& p) { return p.first; }); // sort on name
186 for (const auto& [name, debuggable] : debuggables) {
187 if (ImGui::Selectable(strCat(name, " ...").c_str())) {
188 createHexEditor(name);
189 }
190 }
191 });
192 });
193}
194
196{
197 if (!motherBoard) return;
198
199 auto& regs = motherBoard->getCPU().getRegisters();
200 auto& cpuInterface = motherBoard->getCPUInterface();
201 auto& debugger = motherBoard->getDebugger();
202 auto time = motherBoard->getCurrentTime();
203 drawControl(cpuInterface);
204 drawDisassembly(regs, cpuInterface, debugger, time);
205 drawSlots(cpuInterface, debugger);
206 drawStack(regs, cpuInterface, time);
207 drawRegisters(regs);
208 drawFlags(regs);
209}
210
211void ImGuiDebugger::actionBreakContinue(MSXCPUInterface& cpuInterface)
212{
214 cpuInterface.doContinue();
215 } else {
216 cpuInterface.doBreak();
217 }
218}
219void ImGuiDebugger::actionStepIn(MSXCPUInterface& cpuInterface)
220{
221 cpuInterface.doStep();
222}
223void ImGuiDebugger::actionStepOver()
224{
225 manager.executeDelayed(TclObject("step_over"));
226}
227void ImGuiDebugger::actionStepOut()
228{
229 manager.executeDelayed(TclObject("step_out"));
230}
231void ImGuiDebugger::actionStepBack()
232{
233 manager.executeDelayed(TclObject("step_back"),
234 [&](const TclObject&) { syncDisassemblyWithPC = true; });
235}
236
237void ImGuiDebugger::checkShortcuts(MSXCPUInterface& cpuInterface)
238{
239 using enum Shortcuts::ID;
240 const auto& shortcuts = manager.getShortcuts();
241
242 if (shortcuts.checkShortcut(DEBUGGER_BREAK_CONTINUE)) {
243 actionBreakContinue(cpuInterface);
244 }
245 if (shortcuts.checkShortcut(DEBUGGER_STEP_IN)) {
246 actionStepIn(cpuInterface);
247 }
248 if (shortcuts.checkShortcut(DEBUGGER_STEP_OVER)) {
249 actionStepOver();
250 }
251 if (shortcuts.checkShortcut(DEBUGGER_STEP_OUT)) {
252 actionStepOut();
253 }
254 if (shortcuts.checkShortcut(DEBUGGER_STEP_BACK)) {
255 actionStepBack();
256 }
257}
258
259void ImGuiDebugger::drawControl(MSXCPUInterface& cpuInterface)
260{
261 if (!showControl) return;
262 im::Window("Debugger tool bar", &showControl, [&]{
263 checkShortcuts(cpuInterface);
264
265 auto ButtonGlyph = [&](const char* id, ImWchar c, Shortcuts::ID sid) {
266 const auto* font = ImGui::GetFont();
267 auto texId = font->ContainerAtlas->TexID;
268 const auto* g = font->FindGlyph(c);
269 bool result = ImGui::ImageButton(id, texId, {g->X1 - g->X0, g->Y1 - g->Y0}, {g->U0, g->V0}, {g->U1, g->V1});
270 simpleToolTip([&]() -> std::string {
271 const auto& shortcuts = manager.getShortcuts();
272 const auto& shortcut = shortcuts.getShortcut(sid);
273 if (shortcut.keyChord == ImGuiKey_None) return id;
274 return strCat(id, " (", getKeyChordName(shortcut.keyChord), ')');
275 });
276 return result;
277 };
278
279 bool breaked = MSXCPUInterface::isBreaked();
280 using enum Shortcuts::ID;
281 if (auto breakContinueIcon = breaked ? DEBUGGER_ICON_RUN : DEBUGGER_ICON_BREAK;
282 ButtonGlyph("run", breakContinueIcon, DEBUGGER_BREAK_CONTINUE)) {
283 actionBreakContinue(cpuInterface);
284 }
285 ImGui::SameLine();
286 ImGui::SetCursorPosX(50.0f);
287
288 im::Disabled(!breaked, [&]{
289 if (ButtonGlyph("step-in", DEBUGGER_ICON_STEP_IN, DEBUGGER_STEP_IN)) {
290 actionStepIn(cpuInterface);
291 }
292 ImGui::SameLine();
293
294 if (ButtonGlyph("step-over", DEBUGGER_ICON_STEP_OVER, DEBUGGER_STEP_OVER)) {
295 actionStepOver();
296 }
297 ImGui::SameLine();
298
299 if (ButtonGlyph("step-out", DEBUGGER_ICON_STEP_OUT, DEBUGGER_STEP_OUT)) {
300 actionStepOut();
301 }
302 ImGui::SameLine();
303
304 if (ButtonGlyph("step-back", DEBUGGER_ICON_STEP_BACK, DEBUGGER_STEP_BACK)) {
305 actionStepBack();
306 }
307 });
308 });
309}
310
311[[nodiscard]] static std::pair<const MSXRom*, Debuggable*> getRomBlocks(Debugger& debugger, const MSXDevice* device)
312{
313 Debuggable* debuggable = nullptr;
314 const auto* rom = dynamic_cast<const MSXRom*>(device);
315 if (rom && !dynamic_cast<const RomPlain*>(rom)) {
316 debuggable = debugger.findDebuggable(rom->getName() + " romblocks");
317 }
318 return {rom, debuggable};
319}
320
322 int ps;
323 std::optional<int> ss;
324 std::optional<int> seg;
325};
326[[nodiscard]] static CurrentSlot getCurrentSlot(
327 MSXCPUInterface& cpuInterface, Debugger& debugger,
328 uint16_t addr, bool wantSs = true, bool wantSeg = true)
329{
330 CurrentSlot result;
331 int page = addr / 0x4000;
332 result.ps = cpuInterface.getPrimarySlot(page);
333
334 if (wantSs && cpuInterface.isExpanded(result.ps)) {
335 result.ss = cpuInterface.getSecondarySlot(page);
336 }
337 if (wantSeg) {
338 const auto* device = cpuInterface.getVisibleMSXDevice(page);
339 if (const auto* mapper = dynamic_cast<const MSXMemoryMapperBase*>(device)) {
340 result.seg = mapper->getSelectedSegment(narrow<uint8_t>(page));
341 } else if (auto [_, romBlocks] = getRomBlocks(debugger, device); romBlocks) {
342 result.seg = romBlocks->read(addr);
343 }
344 }
345 return result;
346}
347
348[[nodiscard]] static TclObject toTclExpression(const CurrentSlot& slot)
349{
350 std::string result = strCat("[pc_in_slot ", slot.ps);
351 if (slot.ss) {
352 strAppend(result, ' ', *slot.ss);
353 } else {
354 if (slot.seg) strAppend(result, " X");
355 }
356 if (slot.seg) strAppend(result, ' ', *slot.seg);
357 strAppend(result, ']');
358 return TclObject(result);
359}
360
361[[nodiscard]] static bool addrInSlot(
362 const ParsedSlotCond& slot, MSXCPUInterface& cpuInterface, Debugger& debugger, uint16_t addr)
363{
364 if (!slot.hasPs) return true; // no ps specified -> always ok
365
366 auto current = getCurrentSlot(cpuInterface, debugger, addr, slot.hasSs, slot.hasSeg);
367 if (slot.ps != current.ps) return false;
368 if (slot.hasSs && current.ss && (slot.ss != current.ss)) return false;
369 if (slot.hasSeg && current.seg && (slot.seg != current.seg)) return false;
370 return true;
371}
372
373struct BpLine {
374 int count = 0;
375 int idx = -1; // only valid when count=1
376 bool anyEnabled = false;
377 bool anyDisabled = false;
378 bool anyInSlot = false;
379 bool anyComplex = false;
380};
381static BpLine examineBpLine(uint16_t addr, std::span<const ImGuiBreakPoints::GuiItem> bps, MSXCPUInterface& cpuInterface, Debugger& debugger)
382{
383 BpLine result;
384 for (auto [i, bp] : enumerate(bps)) {
385 if (!bp.addr || *bp.addr != addr) continue;
386 ++result.count;
387 result.idx = int(i);
388
389 bool enabled = (bp.id > 0) && bp.wantEnable;
390 result.anyEnabled |= enabled;
391 result.anyDisabled |= !enabled;
392
393 ParsedSlotCond bpSlot("pc_in_slot", bp.cond.getString());
394 result.anyInSlot |= addrInSlot(bpSlot, cpuInterface, debugger, addr);
395
396 result.anyComplex |= !bpSlot.rest.empty() || (bp.cmd.getString() != "debug break");
397 }
398 return result;
399}
400
401void ImGuiDebugger::drawDisassembly(CPURegs& regs, MSXCPUInterface& cpuInterface, Debugger& debugger, EmuTime::param time)
402{
403 if (!showDisassembly) return;
404 ImGui::SetNextWindowSize({340, 540}, ImGuiCond_FirstUseEver);
405 im::Window("Disassembly", &showDisassembly, [&]{
406 checkShortcuts(cpuInterface);
407
408 std::optional<BreakPoint> addBp;
409 std::optional<unsigned> removeBpId;
410
411 auto pc = regs.getPC();
412 if (followPC && !MSXCPUInterface::isBreaked()) {
413 gotoTarget = pc;
414 }
415
416 auto widthOpcode = ImGui::CalcTextSize("12 34 56 78"sv).x;
417 int flags = ImGuiTableFlags_RowBg |
418 ImGuiTableFlags_BordersV |
419 ImGuiTableFlags_BordersOuterV |
420 ImGuiTableFlags_Resizable |
421 ImGuiTableFlags_Hideable |
422 ImGuiTableFlags_Reorderable |
423 ImGuiTableFlags_ScrollY |
424 ImGuiTableFlags_ScrollX;
425 im::Table("table", 4, flags, [&]{
426 ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
427 ImGui::TableSetupColumn("bp", ImGuiTableColumnFlags_WidthFixed);
428 ImGui::TableSetupColumn("address", ImGuiTableColumnFlags_NoHide);
429 ImGui::TableSetupColumn("opcode", ImGuiTableColumnFlags_WidthFixed, widthOpcode);
430 ImGui::TableSetupColumn("mnemonic", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_NoHide);
431 ImGui::TableHeadersRow();
432
433 auto& guiBps = manager.breakPoints->getBps();
434 auto textSize = ImGui::GetTextLineHeight();
435
436 std::string mnemonic;
437 std::string opcodesStr;
438 std::array<uint8_t, 4> opcodes;
439 ImGuiListClipper clipper; // only draw the actually visible rows
440 clipper.Begin(0x10000);
441 if (gotoTarget) {
442 clipper.IncludeItemsByIndex(*gotoTarget, *gotoTarget + 4);
443 }
444 std::optional<unsigned> nextGotoTarget;
445 while (clipper.Step()) {
446 auto addr16 = instructionBoundary(cpuInterface, narrow<uint16_t>(clipper.DisplayStart), time);
447 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; ++row) {
448 unsigned addr = addr16;
449 ImGui::TableNextRow();
450 if (addr >= 0x10000) continue;
451 im::ID(narrow<int>(addr), [&]{
452 if (gotoTarget && addr >= *gotoTarget) {
453 gotoTarget = {};
454 ImGui::SetScrollHereY(0.25f);
455 }
456
457 if (bool rowAtPc = !syncDisassemblyWithPC && (addr == pc);
458 rowAtPc) {
459 ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, getColor(imColor::YELLOW_BG));
460 }
461 bool bpRightClick = false;
462 if (ImGui::TableNextColumn()) { // bp
463 auto bpLine = examineBpLine(addr16, guiBps, cpuInterface, debugger);
464 bool hasBp = bpLine.count != 0;
465 bool multi = bpLine.count > 1;
466 if (hasBp) {
467 auto calcColor = [](bool enabled, bool inSlot) {
468 auto [r,g,b] = enabled ? std::tuple{0xE0, 0x00, 0x00}
469 : std::tuple{0x70, 0x70, 0x70};
470 auto a = inSlot ? 0xFF : 0x60;
471 return IM_COL32(r, g, b, a);
472 };
473 auto colIn = calcColor(bpLine.anyEnabled, bpLine.anyInSlot);
474 auto colOut = ImGui::GetColorU32(ImGuiCol_WindowBg);
475
476 auto* drawList = ImGui::GetWindowDrawList();
477 gl::vec2 scrn = ImGui::GetCursorScreenPos();
478 auto center = scrn + textSize * gl::vec2(multi ? 0.3f : 0.5f, 0.5f);
479 auto radiusIn = 0.4f * textSize;
480 auto radiusOut = 0.5f * textSize;
481
482 if (multi) {
483 auto center2 = center + textSize * gl::vec2(0.4f, 0.0f);
484 drawList->AddCircleFilled(center2, radiusOut, colOut);
485 auto colIn2 = calcColor(!bpLine.anyDisabled, bpLine.anyInSlot);
486 drawList->AddCircleFilled(center2, radiusIn, colIn2);
487 }
488 drawList->AddCircleFilled(center, radiusOut, colOut);
489 drawList->AddCircleFilled(center, radiusIn, colIn);
490 if (bpLine.anyComplex) {
491 auto d = 0.3f * textSize;
492 auto c = IM_COL32(0, 0, 0, 192);
493 auto t = 0.2f * textSize;
494 drawList->AddLine(center - gl::vec2(d, 0.0f), center + gl::vec2(d, 0.0f), c, t);
495 drawList->AddLine(center - gl::vec2(0.0f, d), center + gl::vec2(0.0f, d), c, t);
496 }
497 }
498 if (ImGui::InvisibleButton("##bp-button", {-FLT_MIN, textSize})) {
499 if (hasBp) {
500 // only allow to remove single breakpoints,
501 // others can be edited via the breakpoint viewer
502 if (!multi) {
503 auto& bp = guiBps[bpLine.idx];
504 if (bp.id > 0) {
505 removeBpId = bp.id; // schedule removal
506 } else {
507 guiBps.erase(guiBps.begin() + bpLine.idx);
508 }
509 }
510 } else {
511 // schedule creation of new bp
512 auto slot = getCurrentSlot(cpuInterface, debugger, addr16);
513 addBp.emplace(addr, TclObject("debug break"), toTclExpression(slot), false);
514 }
515 } else {
516 bpRightClick = hasBp && ImGui::IsItemClicked(ImGuiMouseButton_Right);
517 if (bpRightClick) ImGui::OpenPopup("bp-context");
518 im::Popup("bp-context", [&]{
519 manager.breakPoints->paintBpTab(cpuInterface, debugger, addr16);
520 });
521 }
522 }
523
524 mnemonic.clear();
525 std::optional<uint16_t> mnemonicAddr;
526 std::span<const Symbol* const> mnemonicLabels;
527 auto len = dasm(cpuInterface, addr16, opcodes, mnemonic, time,
528 [&](std::string& output, uint16_t a) {
529 mnemonicAddr = a;
530 mnemonicLabels = symbolManager.lookupValue(a);
531 if (!mnemonicLabels.empty()) {
532 strAppend(output, mnemonicLabels[cycleLabelsCounter % mnemonicLabels.size()]->name);
533 } else {
534 appendAddrAsHex(output, a);
535 }
536 });
537 assert(len >= 1);
538 if ((addr < pc) && (pc < (addr + len))) {
539 // pc is strictly inside current instruction,
540 // replace the just disassembled instruction with "db #..."
541 mnemonicAddr.reset();
542 mnemonicLabels = {};
543 len = pc - addr;
544 assert((1 <= len) && (len <= 3));
545 mnemonic = strCat("db ", join(
547 [&](unsigned i) { return strCat('#', hex_string<2>(opcodes[i])); }),
548 ','));
549 }
550
551 if (ImGui::TableNextColumn()) { // addr
552 bool focusScrollToAddress = false;
553 bool focusRunToAddress = false;
554
555 // do the full-row-selectable stuff in a column that cannot be hidden
556 auto pos = ImGui::GetCursorPos();
557 ImGui::Selectable("##row", false,
558 ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
560 ImGui::OpenPopup("disassembly-context");
561 focusScrollToAddress = true;
562 }
564 ImGui::OpenPopup("disassembly-context");
565 focusRunToAddress = true;
566 }
567 if (!bpRightClick && ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
568 ImGui::OpenPopup("disassembly-context");
569 }
570
571 auto addrStr = tmpStrCat(hex_string<4>(addr));
572 im::Popup("disassembly-context", [&]{
573 auto addrToolTip = [&](const std::string& str) {
574 simpleToolTip([&]{
575 if (auto a = symbolManager.parseSymbolOrValue(str)) {
576 return strCat("0x", hex_string<4>(*a));
577 }
578 return std::string{};
579 });
580 };
581
582 if (ImGui::MenuItem("Scroll to PC")) {
583 nextGotoTarget = pc;
584 }
585
586 im::Indent([&]{
587 ImGui::Checkbox("Follow PC while running", &followPC);
588 });
589
590 ImGui::AlignTextToFramePadding();
591 ImGui::TextUnformatted("Scroll to address:"sv);
592 ImGui::SameLine();
593 if (focusScrollToAddress) ImGui::SetKeyboardFocusHere();
594 if (ImGui::InputText("##goto", &gotoAddr, ImGuiInputTextFlags_EnterReturnsTrue)) {
595 if (auto a = symbolManager.parseSymbolOrValue(gotoAddr)) {
596 nextGotoTarget = *a;
597 }
598 }
599 addrToolTip(gotoAddr);
600
601 ImGui::Separator();
602
603 auto runTo = strCat("Run to here (address 0x", addrStr, ')');
604 if (ImGui::MenuItem(runTo.c_str())) {
605 manager.executeDelayed(makeTclList("run_to", addr));
606 }
607
608 ImGui::AlignTextToFramePadding();
609 ImGui::TextUnformatted("Run to address:"sv);
610 ImGui::SameLine();
611 if (focusRunToAddress) ImGui::SetKeyboardFocusHere();
612 if (ImGui::InputText("##run", &runToAddr, ImGuiInputTextFlags_EnterReturnsTrue)) {
613 if (auto a = symbolManager.parseSymbolOrValue(runToAddr)) {
614 manager.executeDelayed(makeTclList("run_to", *a));
615 }
616 }
617 addrToolTip(runToAddr);
618
619 ImGui::Separator();
620
621 auto setPc = strCat("Set PC to 0x", addrStr);
622 if (ImGui::MenuItem(setPc.c_str())) {
623 regs.setPC(addr16);
624 }
625 });
626
627 auto addrLabels = symbolManager.lookupValue(addr16);
628 std::string_view displayAddr = addrLabels.empty()
629 ? std::string_view(addrStr)
630 : std::string_view(addrLabels[cycleLabelsCounter % addrLabels.size()]->name);
631 ImGui::SetCursorPos(pos);
633 ImGui::TextUnformatted(displayAddr);
634 });
635 if (!addrLabels.empty()) {
636 simpleToolTip([&]{
637 std::string tip(addrStr);
638 if (addrLabels.size() > 1) {
639 strAppend(tip, "\nmultiple possibilities (click to cycle):\n",
640 join(view::transform(addrLabels, &Symbol::name), ' '));
641 }
642 return tip;
643 });
644 ImGui::SetCursorPos(pos);
645 if (ImGui::InvisibleButton("##addrButton", {-FLT_MIN, textSize})) {
646 ++cycleLabelsCounter;
647 }
648 }
649 }
650
651 if (ImGui::TableNextColumn()) { // opcode
652 opcodesStr.clear();
653 for (auto i : xrange(len)) {
654 strAppend(opcodesStr, hex_string<2>(opcodes[i]), ' ');
655 }
657 ImGui::TextUnformatted(opcodesStr.data(), opcodesStr.data() + 3 * len - 1);
658 });
659 }
660
661 if (ImGui::TableNextColumn()) { // mnemonic
662 auto pos = ImGui::GetCursorPos();
664 ImGui::TextUnformatted(mnemonic);
665 });
666 if (mnemonicAddr) {
667 ImGui::SetCursorPos(pos);
668 if (ImGui::InvisibleButton("##mnemonicButton", {-FLT_MIN, textSize})) {
669 if (!mnemonicLabels.empty()) {
670 ++cycleLabelsCounter;
671 }
672 }
673 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
674 nextGotoTarget = *mnemonicAddr;
675 }
676 if (!mnemonicLabels.empty()) {
677 simpleToolTip([&]{
678 auto tip = strCat('#', hex_string<4>(*mnemonicAddr));
679 if (mnemonicLabels.size() > 1) {
680 strAppend(tip, "\nmultiple possibilities (click to cycle):\n",
681 join(view::transform(mnemonicLabels, &Symbol::name), ' '));
682 }
683 return tip;
684 });
685 }
686 }
687 }
688 addr16 += len;
689 });
690 }
691 }
692 gotoTarget = nextGotoTarget;
693 if (syncDisassemblyWithPC) {
694 syncDisassemblyWithPC = false; // only once
695
696 auto itemHeight = ImGui::GetTextLineHeightWithSpacing();
697 auto winHeight = ImGui::GetWindowHeight();
698 auto lines = std::max(1, int(winHeight / itemHeight) - 1); // approx
699
700 auto topAddr = nInstructionsBefore(cpuInterface, pc, time, narrow<int>(lines / 4) + 1);
701
702 ImGui::SetScrollY(topAddr * itemHeight);
703 }
704 });
705 // only add/remove bp's after drawing (can't change list of bp's while iterating over it)
706 if (addBp) {
707 cpuInterface.insertBreakPoint(std::move(*addBp));
708 }
709 if (removeBpId) {
710 cpuInterface.removeBreakPoint(*removeBpId);
711 }
712 });
713}
714
715void ImGuiDebugger::drawSlots(MSXCPUInterface& cpuInterface, Debugger& debugger)
716{
717 if (!showSlots) return;
718 im::Window("Slots", &showSlots, [&]{
719 int flags = ImGuiTableFlags_BordersInnerV |
720 ImGuiTableFlags_Resizable |
721 ImGuiTableFlags_Reorderable |
722 ImGuiTableFlags_Hideable |
723 ImGuiTableFlags_ContextMenuInBody;
724 im::Table("table", 4, flags, [&]{
725 ImGui::TableSetupColumn("Page");
726 ImGui::TableSetupColumn("Address", ImGuiTableColumnFlags_DefaultHide);
727 ImGui::TableSetupColumn("Slot");
728 ImGui::TableSetupColumn("Segment");
729 ImGui::TableHeadersRow();
730
731 for (auto page : xrange(uint8_t(4))) {
732 auto addr = 0x4000 * page;
733 if (ImGui::TableNextColumn()) { // page
734 ImGui::StrCat(page);
735 }
736 if (ImGui::TableNextColumn()) { // address
737 ImGui::StrCat("0x", hex_string<4>(addr));
738 }
739 if (ImGui::TableNextColumn()) { // slot
740 int ps = cpuInterface.getPrimarySlot(page);
741 if (cpuInterface.isExpanded(ps)) {
742 int ss = cpuInterface.getSecondarySlot(page);
743 ImGui::StrCat(ps, '-', ss);
744 } else {
745 ImGui::StrCat(' ', ps);
746 }
747 }
748 if (ImGui::TableNextColumn()) { // segment
749 const auto* device = cpuInterface.getVisibleMSXDevice(page);
750 if (const auto* mapper = dynamic_cast<const MSXMemoryMapperBase*>(device)) {
751 ImGui::StrCat(mapper->getSelectedSegment(page));
752 } else if (auto [rom, romBlocks] = getRomBlocks(debugger, device); romBlocks) {
753 if (unsigned blockSize = RomInfo::getBlockSize(rom->getRomType())) {
754 std::string text;
755 char separator = 'R';
756 for (int offset = 0; offset < 0x4000; offset += blockSize) {
757 strAppend(text, separator, romBlocks->read(addr + offset));
758 separator = '/';
759 }
761 } else {
763 }
764 } else {
766 }
767 }
768 }
769 });
770 });
771}
772
773void ImGuiDebugger::drawStack(const CPURegs& regs, const MSXCPUInterface& cpuInterface, EmuTime::param time)
774{
775 if (!showStack) return;
776
777 auto line = ImGui::GetTextLineHeightWithSpacing();
778 ImGui::SetNextWindowSize(ImVec2(0.0f, 12 * line), ImGuiCond_FirstUseEver);
779 im::Window("stack", &showStack, [&]{
780 auto sp = regs.getSP();
781
782 int flags = ImGuiTableFlags_ScrollY |
783 ImGuiTableFlags_BordersInnerV |
784 ImGuiTableFlags_Resizable |
785 ImGuiTableFlags_Reorderable |
786 ImGuiTableFlags_Hideable |
787 ImGuiTableFlags_ContextMenuInBody;
788 im::Table("table", 3, flags, [&]{
789 ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
790 ImGui::TableSetupColumn("Address");
791 ImGui::TableSetupColumn("Offset", ImGuiTableColumnFlags_DefaultHide);
792 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_NoHide);
793 ImGui::TableHeadersRow();
794
796 im::ListClipper(std::min(128, (0x10000 - sp) / 2), [&](int row) {
797 auto offset = 2 * row;
798 auto addr = sp + offset;
799 if (ImGui::TableNextColumn()) { // address
800 ImGui::StrCat(hex_string<4>(addr));
801 }
802 if (ImGui::TableNextColumn()) { // offset
803 ImGui::Text("SP+%X", offset);
804 }
805 if (ImGui::TableNextColumn()) { // value
806 auto l = cpuInterface.peekMem(narrow_cast<uint16_t>(addr + 0), time);
807 auto h = cpuInterface.peekMem(narrow_cast<uint16_t>(addr + 1), time);
808 auto value = narrow<uint16_t>(256 * h + l);
809 ImGui::StrCat(hex_string<4>(value));
810 }
811 });
812 });
813 });
814}
815
816void ImGuiDebugger::drawRegisters(CPURegs& regs)
817{
818 if (!showRegisters) return;
819 im::Window("CPU registers", &showRegisters, [&]{
821
822 const auto& style = ImGui::GetStyle();
823 auto padding = 2 * style.FramePadding.x;
824 auto width16 = ImGui::CalcTextSize("FFFF"sv).x + padding;
825 auto edit16 = [&](std::string_view label, std::string_view high, std::string_view low, auto getter, auto setter) {
826 uint16_t value = getter();
827 im::Group([&]{
828 ImGui::AlignTextToFramePadding();
830 ImGui::SameLine();
831 ImGui::SetNextItemWidth(width16);
832 if (ImGui::InputScalar(tmpStrCat("##", label).c_str(), ImGuiDataType_U16, &value, nullptr, nullptr, "%04X")) {
833 setter(value);
834 }
835 });
836 simpleToolTip([&]{
837 return strCat(
838 "Bin: ", bin_string<4>(value >> 12), ' ', bin_string<4>(value >> 8), ' ',
839 bin_string<4>(value >> 4), ' ', bin_string<4>(value >> 0), "\n"
840 "Dec: ", dec_string<5>(value), '\n',
841 high, ": ", dec_string<3>(value >> 8), " ", low, ": ", dec_string<3>(value & 0xff));
842 });
843 };
844 auto edit8 = [&](std::string_view label, auto getter, auto setter) {
845 uint8_t value = getter();
846 im::Group([&]{
847 ImGui::AlignTextToFramePadding();
849 ImGui::SameLine();
850 ImGui::SetNextItemWidth(width16);
851 if (ImGui::InputScalar(tmpStrCat("##", label).c_str(), ImGuiDataType_U8, &value, nullptr, nullptr, "%02X")) {
852 setter(value);
853 }
854 });
855 simpleToolTip([&]{
856 return strCat(
857 "Bin: ", bin_string<4>(value >> 4), ' ', bin_string<4>(value >> 0), "\n"
858 "Dec: ", dec_string<3>(value), '\n');
859 });
860 };
861
862 edit16("AF", "A", "F", [&]{ return regs.getAF(); }, [&](uint16_t value) { regs.setAF(value); });
863 ImGui::SameLine(0.0f, 20.0f);
864 edit16("AF'", "A'", "F'", [&]{ return regs.getAF2(); }, [&](uint16_t value) { regs.setAF2(value); });
865
866 edit16("BC", "B", "C", [&]{ return regs.getBC(); }, [&](uint16_t value) { regs.setBC(value); });
867 ImGui::SameLine(0.0f, 20.0f);
868 edit16("BC'", "B'", "C'", [&]{ return regs.getBC2(); }, [&](uint16_t value) { regs.setBC2(value); });
869
870 edit16("DE", "D", "E", [&]{ return regs.getDE(); }, [&](uint16_t value) { regs.setDE(value); });
871 ImGui::SameLine(0.0f, 20.0f);
872 edit16("DE'", "D'", "E'", [&]{ return regs.getDE2(); }, [&](uint16_t value) { regs.setDE2(value); });
873
874 edit16("HL", "H", "L", [&]{ return regs.getHL(); }, [&](uint16_t value) { regs.setHL(value); });
875 ImGui::SameLine(0.0f, 20.0f);
876 edit16("HL'", "H'", "L'", [&]{ return regs.getHL2(); }, [&](uint16_t value) { regs.setHL2(value); });
877
878 edit16("IX", "IXh", "IXl", [&]{ return regs.getIX(); }, [&](uint16_t value) { regs.setIX(value); });
879 ImGui::SameLine(0.0f, 20.0f);
880 edit16("IY ", "IYh", "IYl", [&]{ return regs.getIY(); }, [&](uint16_t value) { regs.setIY(value); });
881
882 edit16("PC", "PCh", "PCl", [&]{ return regs.getPC(); }, [&](uint16_t value) { regs.setPC(value); });
883 ImGui::SameLine(0.0f, 20.0f);
884 edit16("SP ", "SPh", "SPl", [&]{ return regs.getSP(); }, [&](uint16_t value) { regs.setSP(value); });
885
886 edit8("I ", [&]{ return regs.getI(); }, [&](uint8_t value) { regs.setI(value); });
887 ImGui::SameLine(0.0f, 20.0f);
888 edit8("R ", [&]{ return regs.getR(); }, [&](uint8_t value) { regs.setR(value); });
889
890 ImGui::AlignTextToFramePadding();
892 ImGui::SameLine();
893 ImGui::SetNextItemWidth(width16);
894 if (uint8_t im = regs.getIM();
895 ImGui::InputScalar("##IM", ImGuiDataType_U8, &im, nullptr, nullptr, "%d")) {
896 if (im <= 2) regs.setIM(im);
897 }
898
899 ImGui::SameLine(0.0f, 20.0f);
900 ImGui::AlignTextToFramePadding();
901 if (bool ei = regs.getIFF1();
902 ImGui::Selectable(ei ? "EI" : "DI", false, ImGuiSelectableFlags_AllowDoubleClick)) {
903 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
904 regs.setIFF1(!ei);
905 regs.setIFF2(!ei);
906 }
907 }
908 simpleToolTip("double-click to toggle");
909 });
910}
911
912void ImGuiDebugger::drawFlags(CPURegs& regs)
913{
914 if (!showFlags) return;
915 im::Window("CPU flags", &showFlags, [&]{
916 auto [sizeH1_, sizeH2_, sizeV_] = [&]{
918 return std::tuple{
919 ImGui::CalcTextSize("NC"sv),
920 ImGui::CalcTextSize("X:0"sv),
921 ImGui::CalcTextSize("C 0 (NC)"sv)
922 };
923 }();
924 // clang workaround
925 auto sizeH1 = sizeH1_; auto sizeH2 = sizeH2_; auto sizeV = sizeV_;
926
927 auto f = regs.getF();
928
929 auto draw = [&](const char* name, uint8_t bit, const char* val0 = nullptr, const char* val1 = nullptr) {
930 std::string s;
931 ImVec2 sz;
932 if (flagsLayout == 0) {
933 // horizontal
934 if (val0) {
935 s = (f & bit) ? val1 : val0;
936 sz = sizeH1;
937 } else {
938 s = strCat(name, ':', (f & bit) ? '1' : '0');
939 sz = sizeH2;
940 }
941 } else {
942 // vertical
943 s = strCat(name, ' ', (f & bit) ? '1' : '0');
944 if (val0) {
945 strAppend(s, " (", (f & bit) ? val1 : val0, ')');
946 }
947 sz = sizeV;
948 }
950 if (ImGui::Selectable(s.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick, sz)) {
951 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
952 regs.setF(f ^ bit);
953 }
954 }
955 });
956 simpleToolTip("double-click to toggle, right-click to configure");
957 if (flagsLayout == 0) {
958 // horizontal
959 ImGui::SameLine(0.0f, sizeH1.x);
960 }
961 };
962
963 draw("S", 0x80, " P", " M");
964 draw("Z", 0x40, "NZ", " Z");
965 if (showXYFlags) draw("Y", 0x20);
966 draw("H", 0x10);
967 if (showXYFlags) draw("X", 0x08);
968 draw("P", 0x04, "PO", "PE");
969 draw("N", 0x02);
970 draw("C", 0x01, "NC", " C");
971
973 ImGui::TextUnformatted("Layout"sv);
974 ImGui::RadioButton("horizontal", &flagsLayout, 0);
975 ImGui::RadioButton("vertical", &flagsLayout, 1);
976 ImGui::Separator();
977 ImGui::Checkbox("show undocumented", &showXYFlags);
978 });
979 });
980}
981
982} // namespace openmsx
uintptr_t id
int g
TclObject t
std::string_view getDebuggableName() const
void loadStart() override
void showMenu(MSXMotherBoard *motherBoard) override
ImGuiDebugger(ImGuiManager &manager)
void loadLine(std::string_view name, zstring_view value) override
void paint(MSXMotherBoard *motherBoard) override
void save(ImGuiTextBuffer &buf) override
std::unique_ptr< ImGuiBreakPoints > breakPoints
std::unique_ptr< ImGuiVdpRegs > vdpRegs
std::unique_ptr< ImGuiWatchExpr > watchExpr
std::unique_ptr< ImGuiPalette > palette
std::unique_ptr< ImGuiSpriteViewer > sprite
Shortcuts & getShortcuts()
std::unique_ptr< ImGuiBitmapViewer > bitmap
std::unique_ptr< ImGuiCharacter > character
void executeDelayed(std::function< void()> action)
std::unique_ptr< ImGuiSymbols > symbols
ImGuiManager & manager
Definition ImGuiPart.hh:30
auto getPrimarySlot(int page) const
MSXDevice * getVisibleMSXDevice(int page)
bool isExpanded(int ps) const
auto getSecondarySlot(int page) const
CPURegs & getRegisters()
Definition MSXCPU.cc:339
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
MSXCPUInterface & getCPUInterface()
static unsigned getBlockSize(RomType type)
Definition RomInfo.cc:201
bool checkShortcut(const ShortcutWithRepeat &shortcut) const
Definition Shortcuts.cc:134
const Shortcut & getShortcut(ID id) const
Definition Shortcuts.cc:90
std::span< Symbol const *const > lookupValue(uint16_t value)
std::optional< uint16_t > parseSymbolOrValue(std::string_view s) const
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition enumerate.hh:28
detail::Joiner< Collection, Separator > join(Collection &&col, Separator &&sep)
Definition join.hh:60
void StrCat(Ts &&...ts)
Definition ImGuiUtils.hh:43
auto CalcTextSize(std::string_view str)
Definition ImGuiUtils.hh:37
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:24
vecN< 2, float > vec2
Definition gl_vec.hh:178
Definition ImGuiCpp.hh:60
void Table(const char *str_id, int column, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width, std::invocable<> auto next)
Definition ImGuiCpp.hh:459
void Window(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:63
void ID(const char *str_id, std::invocable<> auto next)
Definition ImGuiCpp.hh:244
void PopupContextWindow(const char *str_id, ImGuiPopupFlags popup_flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:442
bool Menu(const char *label, bool enabled, std::invocable<> auto next)
Definition ImGuiCpp.hh:363
void Disabled(bool b, std::invocable<> auto next)
Definition ImGuiCpp.hh:510
void Group(std::invocable<> auto next)
Definition ImGuiCpp.hh:236
void Font(ImFont *font, std::invocable<> auto next)
Definition ImGuiCpp.hh:131
void Indent(float indent_w, std::invocable<> auto next)
Definition ImGuiCpp.hh:224
void ListClipper(size_t count, int forceIndex, float lineHeight, std::invocable< int > auto next)
Definition ImGuiCpp.hh:542
void Popup(const char *str_id, ImGuiWindowFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:395
This file implemented 3 utility functions:
Definition Autofire.cc:11
bool loadOnePersistent(std::string_view name, zstring_view value, C &c, const std::tuple< Elements... > &tup)
void simpleToolTip(std::string_view desc)
Definition ImGuiUtils.hh:77
void savePersistent(ImGuiTextBuffer &buf, C &c, const std::tuple< Elements... > &tup)
unsigned dasm(const MSXCPUInterface &interface, uint16_t pc, std::span< uint8_t, 4 > buf, std::string &dest, EmuTime::param time, function_ref< void(std::string &, uint16_t)> appendAddr)
Disassemble.
Definition Dasm.cc:26
ImU32 getColor(imColor col)
std::string getKeyChordName(ImGuiKeyChord keyChord)
uint16_t instructionBoundary(const MSXCPUInterface &interface, uint16_t addr, EmuTime::param time)
This is only an heuristic to display instructions in a debugger disassembly view.
Definition Dasm.cc:175
uint16_t nInstructionsBefore(const MSXCPUInterface &interface, uint16_t addr, EmuTime::param time, int n)
Get the start address of the 'n'th instruction before the instruction containing the byte at the give...
Definition Dasm.cc:182
TclObject makeTclList(Args &&... args)
Definition TclObject.hh:293
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
Definition ranges.hh:135
constexpr void sort(RandomAccessRange &&range)
Definition ranges.hh:51
auto lower_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
Definition ranges.hh:117
STL namespace.
size_t size(std::string_view utf8)
constexpr auto transform(Range &&range, UnaryOp op)
Definition view.hh:520
std::string strCat()
Definition strCat.hh:703
TemporaryString tmpStrCat(Ts &&... ts)
Definition strCat.hh:742
void strAppend(std::string &result, Ts &&...ts)
Definition strCat.hh:752
std::optional< int > seg
std::optional< int > ss
std::string name
constexpr auto xrange(T e)
Definition xrange.hh:132