36#include <imgui_stdlib.h>
41using namespace std::literals;
47 , symbolManager(manager.getReactor().getSymbolManager())
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]GFlQ#;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#";
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;
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());
91 syncDisassemblyWithPC =
true;
99 auto it = hexEditors.begin();
100 auto et = hexEditors.end();
103 auto name = std::string((*it)->getDebuggableName());
107 }
while (it != et && (*it)->getDebuggableName() == name);
108 buf.appendf(
"hexEditor.%s=%d\n", name.c_str(), count);
120 static constexpr std::string_view prefix =
"hexEditor.";
124 }
else if (name.starts_with(prefix)) {
125 if (
auto r = StringOp::stringTo<unsigned>(value)) {
126 auto debuggableName = std::string(name.substr(prefix.size()));
128 auto index = std::distance(b, e);
129 for (
auto i :
xrange(*r)) {
130 e = hexEditors.insert(e, std::make_unique<DebuggableEditor>(
manager, debuggableName, index + i));
139 auto createHexEditor = [&](
const std::string& name) {
142 for (
auto it = b; it != e; ++it) {
149 auto index = std::distance(b, e);
150 auto it = hexEditors.insert(e, std::make_unique<DebuggableEditor>(
manager, name, index));
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);
162 bool memoryOpen = (it != hexEditors.end()) && (*it)->open;
163 if (ImGui::MenuItem(
"Memory",
nullptr, &memoryOpen)) {
165 createHexEditor(
"memory");
167 assert(it != hexEditors.end());
173 ImGui::MenuItem(
"Symbol manager",
nullptr, &
manager.
symbols->show);
176 ImGui::MenuItem(
"VDP bitmap viewer",
nullptr, &
manager.
bitmap->showBitmapViewer);
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);
184 auto debuggables = to_vector<std::pair<std::string, Debuggable*>>(debugger.getDebuggables());
186 for (
const auto& [name, debuggable] : debuggables) {
187 if (ImGui::Selectable(
strCat(name,
" ...").c_str())) {
188 createHexEditor(name);
197 if (!motherBoard)
return;
203 drawControl(cpuInterface);
204 drawDisassembly(regs, cpuInterface, debugger, time);
205 drawSlots(cpuInterface, debugger);
206 drawStack(regs, cpuInterface, time);
219void ImGuiDebugger::actionStepIn(MSXCPUInterface& cpuInterface)
221 cpuInterface.doStep();
223void ImGuiDebugger::actionStepOver()
227void ImGuiDebugger::actionStepOut()
231void ImGuiDebugger::actionStepBack()
234 [&](
const TclObject&) { syncDisassemblyWithPC =
true; });
237void ImGuiDebugger::checkShortcuts(MSXCPUInterface& cpuInterface)
242 if (shortcuts.checkShortcut(DEBUGGER_BREAK_CONTINUE)) {
243 actionBreakContinue(cpuInterface);
245 if (shortcuts.checkShortcut(DEBUGGER_STEP_IN)) {
246 actionStepIn(cpuInterface);
248 if (shortcuts.checkShortcut(DEBUGGER_STEP_OVER)) {
251 if (shortcuts.checkShortcut(DEBUGGER_STEP_OUT)) {
254 if (shortcuts.checkShortcut(DEBUGGER_STEP_BACK)) {
259void ImGuiDebugger::drawControl(MSXCPUInterface& cpuInterface)
261 if (!showControl)
return;
262 im::Window(
"Debugger tool bar", &showControl, [&]{
263 checkShortcuts(cpuInterface);
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});
273 if (shortcut.keyChord == ImGuiKey_None)
return id;
281 if (
auto breakContinueIcon = breaked ? DEBUGGER_ICON_RUN : DEBUGGER_ICON_BREAK;
282 ButtonGlyph(
"run", breakContinueIcon, DEBUGGER_BREAK_CONTINUE)) {
283 actionBreakContinue(cpuInterface);
286 ImGui::SetCursorPosX(50.0f);
289 if (ButtonGlyph(
"step-in", DEBUGGER_ICON_STEP_IN, DEBUGGER_STEP_IN)) {
290 actionStepIn(cpuInterface);
294 if (ButtonGlyph(
"step-over", DEBUGGER_ICON_STEP_OVER, DEBUGGER_STEP_OVER)) {
299 if (ButtonGlyph(
"step-out", DEBUGGER_ICON_STEP_OUT, DEBUGGER_STEP_OUT)) {
304 if (ButtonGlyph(
"step-back", DEBUGGER_ICON_STEP_BACK, DEBUGGER_STEP_BACK)) {
311[[nodiscard]]
static std::pair<const MSXRom*, Debuggable*> getRomBlocks(Debugger& debugger,
const MSXDevice* device)
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");
318 return {rom, debuggable};
323 std::optional<int>
ss;
328 uint16_t addr,
bool wantSs =
true,
bool wantSeg =
true)
331 int page = addr / 0x4000;
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);
348[[nodiscard]]
static TclObject toTclExpression(
const CurrentSlot& slot)
350 std::string result =
strCat(
"[pc_in_slot ", slot.ps);
356 if (slot.seg)
strAppend(result,
' ', *slot.seg);
358 return TclObject(result);
361[[nodiscard]]
static bool addrInSlot(
362 const ParsedSlotCond& slot, MSXCPUInterface& cpuInterface, Debugger& debugger, uint16_t addr)
364 if (!slot.hasPs)
return true;
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;
385 if (!bp.addr || *bp.addr != addr)
continue;
389 bool enabled = (bp.id > 0) && bp.wantEnable;
394 result.
anyInSlot |= addrInSlot(bpSlot, cpuInterface, debugger, addr);
396 result.
anyComplex |= !bpSlot.
rest.empty() || (bp.cmd.getString() !=
"debug break");
401void ImGuiDebugger::drawDisassembly(CPURegs& regs, MSXCPUInterface& cpuInterface, Debugger& debugger, EmuTime::param time)
403 if (!showDisassembly)
return;
404 ImGui::SetNextWindowSize({340, 540}, ImGuiCond_FirstUseEver);
405 im::Window(
"Disassembly", &showDisassembly, [&]{
406 checkShortcuts(cpuInterface);
408 std::optional<BreakPoint> addBp;
409 std::optional<unsigned> removeBpId;
411 auto pc = regs.getPC();
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;
426 ImGui::TableSetupScrollFreeze(0, 1);
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();
434 auto textSize = ImGui::GetTextLineHeight();
436 std::string mnemonic;
437 std::string opcodesStr;
438 std::array<uint8_t, 4> opcodes;
439 ImGuiListClipper clipper;
440 clipper.Begin(0x10000);
442 clipper.IncludeItemsByIndex(*gotoTarget, *gotoTarget + 4);
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) {
454 ImGui::SetScrollHereY(0.25f);
457 if (
bool rowAtPc = !syncDisassemblyWithPC && (addr == pc);
461 bool bpRightClick =
false;
462 if (ImGui::TableNextColumn()) {
463 auto bpLine = examineBpLine(addr16, guiBps, cpuInterface, debugger);
464 bool hasBp = bpLine.count != 0;
465 bool multi = bpLine.count > 1;
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);
473 auto colIn = calcColor(bpLine.anyEnabled, bpLine.anyInSlot);
474 auto colOut = ImGui::GetColorU32(ImGuiCol_WindowBg);
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;
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);
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;
498 if (ImGui::InvisibleButton(
"##bp-button", {-FLT_MIN, textSize})) {
503 auto& bp = guiBps[bpLine.idx];
507 guiBps.erase(guiBps.begin() + bpLine.idx);
512 auto slot = getCurrentSlot(cpuInterface, debugger, addr16);
513 addBp.emplace(addr, TclObject(
"debug break"), toTclExpression(slot),
false);
516 bpRightClick = hasBp && ImGui::IsItemClicked(ImGuiMouseButton_Right);
517 if (bpRightClick) ImGui::OpenPopup(
"bp-context");
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) {
531 if (!mnemonicLabels.empty()) {
532 strAppend(output, mnemonicLabels[cycleLabelsCounter % mnemonicLabels.size()]->name);
534 appendAddrAsHex(output, a);
538 if ((addr < pc) && (pc < (addr + len))) {
541 mnemonicAddr.reset();
544 assert((1 <= len) && (len <= 3));
547 [&](
unsigned i) {
return strCat(
'#', hex_string<2>(opcodes[i])); }),
551 if (ImGui::TableNextColumn()) {
552 bool focusScrollToAddress =
false;
555 auto pos = ImGui::GetCursorPos();
556 ImGui::Selectable(
"##row",
false,
557 ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
559 ImGui::OpenPopup(
"disassembly-context");
560 focusScrollToAddress =
true;
562 if (!bpRightClick && ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
563 ImGui::OpenPopup(
"disassembly-context");
566 auto addrStr =
tmpStrCat(hex_string<4>(addr));
568 auto addrToolTip = [&](
const std::string& str) {
571 return strCat(
"0x", hex_string<4>(*a));
573 return std::string{};
577 if (ImGui::MenuItem(
"Scroll to PC")) {
582 ImGui::Checkbox(
"Follow PC while running", &followPC);
585 ImGui::AlignTextToFramePadding();
588 if (focusScrollToAddress) ImGui::SetKeyboardFocusHere();
589 if (ImGui::InputText(
"##goto", &gotoAddr, ImGuiInputTextFlags_EnterReturnsTrue)) {
594 addrToolTip(gotoAddr);
598 auto runTo =
strCat(
"Run to here (address 0x", addrStr,
')');
599 if (ImGui::MenuItem(runTo.c_str())) {
603 ImGui::AlignTextToFramePadding();
606 if (ImGui::InputText(
"##run", &runToAddr, ImGuiInputTextFlags_EnterReturnsTrue)) {
611 addrToolTip(runToAddr);
615 auto setPc =
strCat(
"Set PC to 0x", addrStr);
616 if (ImGui::MenuItem(setPc.c_str())) {
621 auto addrLabels = symbolManager.
lookupValue(addr16);
622 std::string_view displayAddr = addrLabels.empty()
623 ? std::string_view(addrStr)
624 :
std::string_view(addrLabels[cycleLabelsCounter % addrLabels.
size()]->name);
625 ImGui::SetCursorPos(pos);
627 ImGui::TextUnformatted(displayAddr);
629 if (!addrLabels.empty()) {
631 std::string tip(addrStr);
632 if (addrLabels.size() > 1) {
633 strAppend(tip,
"\nmultiple possibilities (click to cycle):\n",
638 ImGui::SetCursorPos(pos);
639 if (ImGui::InvisibleButton(
"##addrButton", {-FLT_MIN, textSize})) {
640 ++cycleLabelsCounter;
645 if (ImGui::TableNextColumn()) {
647 for (
auto i :
xrange(len)) {
648 strAppend(opcodesStr, hex_string<2>(opcodes[i]),
' ');
651 ImGui::TextUnformatted(opcodesStr.data(), opcodesStr.data() + 3 * len - 1);
655 if (ImGui::TableNextColumn()) {
656 auto pos = ImGui::GetCursorPos();
658 ImGui::TextUnformatted(mnemonic);
661 ImGui::SetCursorPos(pos);
662 if (ImGui::InvisibleButton(
"##mnemonicButton", {-FLT_MIN, textSize})) {
663 if (!mnemonicLabels.empty()) {
664 ++cycleLabelsCounter;
667 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
668 nextGotoTarget = *mnemonicAddr;
670 if (!mnemonicLabels.empty()) {
672 auto tip =
strCat(
'#', hex_string<4>(*mnemonicAddr));
673 if (mnemonicLabels.size() > 1) {
674 strAppend(tip,
"\nmultiple possibilities (click to cycle):\n",
686 gotoTarget = nextGotoTarget;
687 if (syncDisassemblyWithPC) {
688 syncDisassemblyWithPC =
false;
690 auto itemHeight = ImGui::GetTextLineHeightWithSpacing();
691 auto winHeight = ImGui::GetWindowHeight();
692 auto lines = std::max(1,
int(winHeight / itemHeight) - 1);
696 ImGui::SetScrollY(topAddr * itemHeight);
701 cpuInterface.insertBreakPoint(std::move(*addBp));
704 cpuInterface.removeBreakPoint(*removeBpId);
709void ImGuiDebugger::drawSlots(MSXCPUInterface& cpuInterface, Debugger& debugger)
711 if (!showSlots)
return;
713 int flags = ImGuiTableFlags_BordersInnerV |
714 ImGuiTableFlags_Resizable |
715 ImGuiTableFlags_Reorderable |
716 ImGuiTableFlags_Hideable |
717 ImGuiTableFlags_ContextMenuInBody;
719 ImGui::TableSetupColumn(
"Page");
720 ImGui::TableSetupColumn(
"Address", ImGuiTableColumnFlags_DefaultHide);
721 ImGui::TableSetupColumn(
"Slot");
722 ImGui::TableSetupColumn(
"Segment");
723 ImGui::TableHeadersRow();
725 for (
auto page :
xrange(uint8_t(4))) {
726 auto addr = 0x4000 * page;
727 if (ImGui::TableNextColumn()) {
730 if (ImGui::TableNextColumn()) {
733 if (ImGui::TableNextColumn()) {
734 int ps = cpuInterface.getPrimarySlot(page);
735 if (cpuInterface.isExpanded(ps)) {
736 int ss = cpuInterface.getSecondarySlot(page);
742 if (ImGui::TableNextColumn()) {
743 const auto* device = cpuInterface.getVisibleMSXDevice(page);
746 }
else if (
auto [rom, romBlocks] = getRomBlocks(debugger, device); romBlocks) {
749 char separator =
'R';
750 for (
int offset = 0; offset < 0x4000; offset += blockSize) {
751 strAppend(text, separator, romBlocks->read(addr + offset));
767void ImGuiDebugger::drawStack(
const CPURegs& regs,
const MSXCPUInterface& cpuInterface, EmuTime::param time)
769 if (!showStack)
return;
771 auto line = ImGui::GetTextLineHeightWithSpacing();
772 ImGui::SetNextWindowSize(ImVec2(0.0f, 12 * line), ImGuiCond_FirstUseEver);
774 auto sp = regs.getSP();
776 int flags = ImGuiTableFlags_ScrollY |
777 ImGuiTableFlags_BordersInnerV |
778 ImGuiTableFlags_Resizable |
779 ImGuiTableFlags_Reorderable |
780 ImGuiTableFlags_Hideable |
781 ImGuiTableFlags_ContextMenuInBody;
783 ImGui::TableSetupScrollFreeze(0, 1);
784 ImGui::TableSetupColumn(
"Address");
785 ImGui::TableSetupColumn(
"Offset", ImGuiTableColumnFlags_DefaultHide);
786 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_NoHide);
787 ImGui::TableHeadersRow();
791 auto offset = 2 * row;
792 auto addr = sp + offset;
793 if (ImGui::TableNextColumn()) {
796 if (ImGui::TableNextColumn()) {
797 ImGui::Text(
"SP+%X", offset);
799 if (ImGui::TableNextColumn()) {
800 auto l = cpuInterface.peekMem(narrow_cast<uint16_t>(addr + 0), time);
801 auto h = cpuInterface.peekMem(narrow_cast<uint16_t>(addr + 1), time);
802 auto value = narrow<uint16_t>(256 * h + l);
810void ImGuiDebugger::drawRegisters(CPURegs& regs)
812 if (!showRegisters)
return;
813 im::Window(
"CPU registers", &showRegisters, [&]{
816 const auto& style = ImGui::GetStyle();
817 auto padding = 2 * style.FramePadding.x;
819 auto edit16 = [&](std::string_view label, std::string_view high, std::string_view low,
auto getter,
auto setter) {
820 uint16_t value = getter();
822 ImGui::AlignTextToFramePadding();
825 ImGui::SetNextItemWidth(width16);
826 if (ImGui::InputScalar(
tmpStrCat(
"##", label).c_str(), ImGuiDataType_U16, &value,
nullptr,
nullptr,
"%04X")) {
832 "Bin: ", bin_string<4>(value >> 12),
' ', bin_string<4>(value >> 8),
' ',
833 bin_string<4>(value >> 4),
' ', bin_string<4>(value >> 0),
"\n"
834 "Dec: ", dec_string<5>(value),
'\n',
835 high,
": ", dec_string<3>(value >> 8),
" ", low,
": ", dec_string<3>(value & 0xff));
838 auto edit8 = [&](std::string_view label,
auto getter,
auto setter) {
839 uint8_t value = getter();
841 ImGui::AlignTextToFramePadding();
844 ImGui::SetNextItemWidth(width16);
845 if (ImGui::InputScalar(
tmpStrCat(
"##", label).c_str(), ImGuiDataType_U8, &value,
nullptr,
nullptr,
"%02X")) {
851 "Bin: ", bin_string<4>(value >> 4),
' ', bin_string<4>(value >> 0),
"\n"
852 "Dec: ", dec_string<3>(value),
'\n');
856 edit16(
"AF",
"A",
"F", [&]{
return regs.getAF(); }, [&](uint16_t value) { regs.setAF(value); });
857 ImGui::SameLine(0.0f, 20.0f);
858 edit16(
"AF'",
"A'",
"F'", [&]{
return regs.getAF2(); }, [&](uint16_t value) { regs.setAF2(value); });
860 edit16(
"BC",
"B",
"C", [&]{
return regs.getBC(); }, [&](uint16_t value) { regs.setBC(value); });
861 ImGui::SameLine(0.0f, 20.0f);
862 edit16(
"BC'",
"B'",
"C'", [&]{
return regs.getBC2(); }, [&](uint16_t value) { regs.setBC2(value); });
864 edit16(
"DE",
"D",
"E", [&]{
return regs.getDE(); }, [&](uint16_t value) { regs.setDE(value); });
865 ImGui::SameLine(0.0f, 20.0f);
866 edit16(
"DE'",
"D'",
"E'", [&]{
return regs.getDE2(); }, [&](uint16_t value) { regs.setDE2(value); });
868 edit16(
"HL",
"H",
"L", [&]{
return regs.getHL(); }, [&](uint16_t value) { regs.setHL(value); });
869 ImGui::SameLine(0.0f, 20.0f);
870 edit16(
"HL'",
"H'",
"L'", [&]{
return regs.getHL2(); }, [&](uint16_t value) { regs.setHL2(value); });
872 edit16(
"IX",
"IXh",
"IXl", [&]{
return regs.getIX(); }, [&](uint16_t value) { regs.setIX(value); });
873 ImGui::SameLine(0.0f, 20.0f);
874 edit16(
"IY ",
"IYh",
"IYl", [&]{
return regs.getIY(); }, [&](uint16_t value) { regs.setIY(value); });
876 edit16(
"PC",
"PCh",
"PCl", [&]{
return regs.getPC(); }, [&](uint16_t value) { regs.setPC(value); });
877 ImGui::SameLine(0.0f, 20.0f);
878 edit16(
"SP ",
"SPh",
"SPl", [&]{
return regs.getSP(); }, [&](uint16_t value) { regs.setSP(value); });
880 edit8(
"I ", [&]{
return regs.getI(); }, [&](uint8_t value) { regs.setI(value); });
881 ImGui::SameLine(0.0f, 20.0f);
882 edit8(
"R ", [&]{
return regs.getR(); }, [&](uint8_t value) { regs.setR(value); });
884 ImGui::AlignTextToFramePadding();
887 ImGui::SetNextItemWidth(width16);
888 if (uint8_t
im = regs.getIM();
889 ImGui::InputScalar(
"##IM", ImGuiDataType_U8, &
im,
nullptr,
nullptr,
"%d")) {
890 if (
im <= 2) regs.setIM(
im);
893 ImGui::SameLine(0.0f, 20.0f);
894 ImGui::AlignTextToFramePadding();
895 if (
bool ei = regs.getIFF1();
896 ImGui::Selectable(ei ?
"EI" :
"DI", false, ImGuiSelectableFlags_AllowDoubleClick)) {
897 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
906void ImGuiDebugger::drawFlags(CPURegs& regs)
908 if (!showFlags)
return;
910 auto [sizeH1_, sizeH2_, sizeV_] = [&]{
919 auto sizeH1 = sizeH1_;
auto sizeH2 = sizeH2_;
auto sizeV = sizeV_;
921 auto f = regs.getF();
923 auto draw = [&](
const char* name, uint8_t bit,
const char* val0 =
nullptr,
const char* val1 =
nullptr) {
926 if (flagsLayout == 0) {
929 s = (f & bit) ? val1 : val0;
932 s =
strCat(name,
':', (f & bit) ?
'1' :
'0');
937 s =
strCat(name,
' ', (f & bit) ?
'1' :
'0');
939 strAppend(s,
" (", (f & bit) ? val1 : val0,
')');
944 if (ImGui::Selectable(s.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick, sz)) {
945 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
950 simpleToolTip(
"double-click to toggle, right-click to configure");
951 if (flagsLayout == 0) {
953 ImGui::SameLine(0.0f, sizeH1.x);
957 draw(
"S", 0x80,
" P",
" M");
958 draw(
"Z", 0x40,
"NZ",
" Z");
959 if (showXYFlags) draw(
"Y", 0x20);
961 if (showXYFlags) draw(
"X", 0x08);
962 draw(
"P", 0x04,
"PO",
"PE");
964 draw(
"C", 0x01,
"NC",
" C");
968 ImGui::RadioButton(
"horizontal", &flagsLayout, 0);
969 ImGui::RadioButton(
"vertical", &flagsLayout, 1);
971 ImGui::Checkbox(
"show undocumented", &showXYFlags);
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
auto getPrimarySlot(int page) const
MSXDevice * getVisibleMSXDevice(int page)
bool isExpanded(int ps) const
auto getSecondarySlot(int page) const
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
MSXCPUInterface & getCPUInterface()
static unsigned getBlockSize(RomType type)
bool checkShortcut(const ShortcutWithRepeat &shortcut) const
const Shortcut & getShortcut(ID id) const
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....
detail::Joiner< Collection, Separator > join(Collection &&col, Separator &&sep)
auto CalcTextSize(std::string_view str)
void TextUnformatted(const std::string &str)
void Table(const char *str_id, int column, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width, std::invocable<> auto next)
void Window(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
void ID(const char *str_id, std::invocable<> auto next)
void PopupContextWindow(const char *str_id, ImGuiPopupFlags popup_flags, std::invocable<> auto next)
bool Menu(const char *label, bool enabled, std::invocable<> auto next)
void Disabled(bool b, std::invocable<> auto next)
void Group(std::invocable<> auto next)
void Font(ImFont *font, std::invocable<> auto next)
void Indent(float indent_w, std::invocable<> auto next)
void ListClipper(size_t count, int forceIndex, float lineHeight, std::invocable< int > auto next)
void Popup(const char *str_id, ImGuiWindowFlags flags, std::invocable<> auto next)
This file implemented 3 utility functions:
bool loadOnePersistent(std::string_view name, zstring_view value, C &c, const std::tuple< Elements... > &tup)
void simpleToolTip(std::string_view desc)
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.
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.
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...
TclObject makeTclList(Args &&... args)
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
constexpr void sort(RandomAccessRange &&range)
auto lower_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
size_t size(std::string_view utf8)
constexpr auto transform(Range &&range, UnaryOp op)
TemporaryString tmpStrCat(Ts &&... ts)
void strAppend(std::string &result, Ts &&...ts)
constexpr auto xrange(T e)