openMSX
ImGuiDisassembly.cc
Go to the documentation of this file.
1#include "ImGuiDisassembly.hh"
2
3#include "ImGuiBreakPoints.hh"
4#include "ImGuiCpp.hh"
5#include "ImGuiDebugger.hh"
6#include "ImGuiManager.hh"
7#include "ImGuiUtils.hh"
8
9#include "CPURegs.hh"
10#include "Dasm.hh"
11#include "Debugger.hh"
12#include "MSXCPU.hh"
13#include "MSXCPUInterface.hh"
15#include "MSXMotherBoard.hh"
16#include "RomBlockDebuggable.hh"
17#include "RomPlain.hh"
18#include "SymbolManager.hh"
19
20#include "narrow.hh"
21#include "join.hh"
22#include "strCat.hh"
23#include "view.hh"
24
25#include <imgui.h>
26#include <imgui_stdlib.h>
27
28#include <cstdint>
29#include <vector>
30
31using namespace std::literals;
32
33namespace openmsx {
34
36 : ImGuiPart(manager_)
37 , symbolManager(manager.getReactor().getSymbolManager())
38 , title("Disassembly")
39{
40 if (index) {
41 strAppend(title, " (", index + 1, ')');
42 }
43 scrollToPcOnBreak = index == 0;
44}
45
46void ImGuiDisassembly::save(ImGuiTextBuffer& buf)
47{
48 savePersistent(buf, *this, persistentElements);
49}
50
51void ImGuiDisassembly::loadLine(std::string_view name, zstring_view value)
52{
53 loadOnePersistent(name, value, *this, persistentElements);
54}
55
57{
58 setDisassemblyScrollY = disassemblyScrollY;
59}
60
62{
63 gotoTarget = target;
64 show = true;
65 setDisassemblyScrollY.reset(); // don't restore initial scroll position
66}
67
68std::pair<const MSXRom*, RomBlockDebuggableBase*>
70{
71 RomBlockDebuggableBase* debuggable = nullptr;
72 const auto* rom = dynamic_cast<const MSXRom*>(device);
73 if (rom && !dynamic_cast<const RomPlain*>(rom)) {
74 debuggable = dynamic_cast<RomBlockDebuggableBase*>(
75 debugger.findDebuggable(rom->getName() + " romblocks"));
76 }
77 return {rom, debuggable};
78}
79
81 int ps = 0;
82 std::optional<int> ss;
83 std::optional<int> seg;
84};
85[[nodiscard]] static CurrentSlot getCurrentSlot(
86 MSXCPUInterface& cpuInterface, Debugger& debugger,
87 uint16_t addr, bool wantSs = true, bool wantSeg = true)
88{
89 CurrentSlot result;
90 int page = addr / 0x4000;
91 result.ps = cpuInterface.getPrimarySlot(page);
92
93 if (wantSs && cpuInterface.isExpanded(result.ps)) {
94 result.ss = cpuInterface.getSecondarySlot(page);
95 }
96 if (wantSeg) {
97 const auto* device = cpuInterface.getVisibleMSXDevice(page);
98 if (const auto* mapper = dynamic_cast<const MSXMemoryMapperBase*>(device)) {
99 result.seg = mapper->getSelectedSegment(narrow<uint8_t>(page));
100 } else if (auto [_, romBlocks] = ImGuiDisassembly::getRomBlocks(debugger, device); romBlocks) {
101 result.seg = romBlocks->readExt(addr);
102 }
103 }
104 return result;
105}
106
107[[nodiscard]] static TclObject toTclExpression(const CurrentSlot& slot)
108{
109 std::string result = strCat("[pc_in_slot ", slot.ps);
110 if (slot.ss) {
111 strAppend(result, ' ', *slot.ss);
112 } else {
113 if (slot.seg) strAppend(result, " X");
114 }
115 if (slot.seg) strAppend(result, ' ', *slot.seg);
116 strAppend(result, ']');
117 return TclObject(result);
118}
119
120[[nodiscard]] static bool addrInSlot(
121 const ParsedSlotCond& slot, MSXCPUInterface& cpuInterface, Debugger& debugger, uint16_t addr)
122{
123 if (!slot.hasPs) return true; // no ps specified -> always ok
124
125 auto current = getCurrentSlot(cpuInterface, debugger, addr, slot.hasSs, slot.hasSeg);
126 if (slot.ps != current.ps) return false;
127 if (slot.hasSs && current.ss && (slot.ss != current.ss)) return false;
128 if (slot.hasSeg && current.seg && (slot.seg != current.seg)) return false;
129 return true;
130}
131
132struct BpLine {
133 int count = 0;
134 int idx = -1; // only valid when count=1
135 bool anyEnabled = false;
136 bool anyDisabled = false;
137 bool anyInSlot = false;
138 bool anyComplex = false;
139};
140static BpLine examineBpLine(uint16_t addr, std::span<BreakPoint> bps, MSXCPUInterface& cpuInterface, Debugger& debugger)
141{
142 BpLine result;
143 for (auto [i, bp] : enumerate(bps)) {
144 if (bp.getAddress() != addr) continue;
145 ++result.count;
146 result.idx = int(i);
147
148 bool enabled = bp.isEnabled();
149 result.anyEnabled |= enabled;
150 result.anyDisabled |= !enabled;
151
152 ParsedSlotCond bpSlot("pc_in_slot", bp.getCondition().getString());
153 result.anyInSlot |= addrInSlot(bpSlot, cpuInterface, debugger, addr);
154
155 result.anyComplex |= !bpSlot.rest.empty() || (bp.getCommand().getString() != "debug break");
156 }
157 return result;
158}
159
160static void toggleBp(uint16_t addr, const BpLine& bpLine, std::span<BreakPoint> bps,
161 MSXCPUInterface& cpuInterface, Debugger& debugger,
162 std::optional<BreakPoint>& addBp, std::optional<unsigned>& removeBpId)
163{
164 if (bpLine.count != 0) {
165 // only allow to remove single breakpoints,
166 // others can be edited via the breakpoint viewer
167 if (bpLine.count == 1) {
168 auto& bp = bps[bpLine.idx];
169 removeBpId = bp.getId(); // schedule removal
170 }
171 } else {
172 // schedule creation of new bp
173 addBp.emplace();
174 addBp->setAddress(debugger.getInterpreter(), TclObject(tmpStrCat("0x", hex_string<4>(addr))));
175 addBp->setCondition(toTclExpression(getCurrentSlot(cpuInterface, debugger, addr)));
176 }
177}
179{
180 auto pc = motherBoard.getCPU().getRegisters().getPC();
181 auto& cpuInterface = motherBoard.getCPUInterface();
182 auto& debugger = motherBoard.getDebugger();
183 auto& bps = cpuInterface.getBreakPoints();
184
185 auto bpLine = examineBpLine(pc, bps, cpuInterface, debugger);
186
187 std::optional<BreakPoint> addBp;
188 std::optional<unsigned> removeBpId;
189 toggleBp(pc, bpLine, bps, cpuInterface, debugger, addBp, removeBpId);
190 if (addBp) {
191 cpuInterface.insertBreakPoint(std::move(*addBp));
192 }
193 if (removeBpId) {
194 cpuInterface.removeBreakPoint(*removeBpId);
195 }
196}
197
199{
200 if (!motherBoard || !show) return;
201
202 ImGui::SetNextWindowSize({340, 540}, ImGuiCond_FirstUseEver);
203 im::Window(title.c_str(), &show, [&]{
204 auto& regs = motherBoard->getCPU().getRegisters();
205 auto& cpuInterface = motherBoard->getCPUInterface();
206 auto& debugger = motherBoard->getDebugger();
207 auto time = motherBoard->getCurrentTime();
208
209 manager.debugger->checkShortcuts(cpuInterface, *motherBoard);
210
211 std::optional<BreakPoint> addBp;
212 std::optional<unsigned> removeBpId;
213
214 auto pc = regs.getPC();
215 if (followPC && !MSXCPUInterface::isBreaked()) {
216 gotoTarget = pc;
217 }
218
219 auto widthOpcode = ImGui::CalcTextSize("12 34 56 78"sv).x;
220 int flags = ImGuiTableFlags_RowBg |
221 ImGuiTableFlags_BordersV |
222 ImGuiTableFlags_BordersOuterV |
223 ImGuiTableFlags_Resizable |
224 ImGuiTableFlags_Hideable |
225 ImGuiTableFlags_Reorderable |
226 ImGuiTableFlags_ScrollY |
227 ImGuiTableFlags_ScrollX;
228 im::Table("table", 4, flags, [&]{
229 ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
230 ImGui::TableSetupColumn("bp", ImGuiTableColumnFlags_WidthFixed);
231 ImGui::TableSetupColumn("address", ImGuiTableColumnFlags_NoHide);
232 ImGui::TableSetupColumn("opcode", ImGuiTableColumnFlags_WidthFixed, widthOpcode);
233 ImGui::TableSetupColumn("mnemonic", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_NoHide);
234 ImGui::TableHeadersRow();
235
236 auto& bps = cpuInterface.getBreakPoints();
237 auto textSize = ImGui::GetTextLineHeight();
238
239 std::string mnemonic;
240 std::string opcodesStr;
241 std::vector<std::string_view> candidates;
242 std::array<uint8_t, 4> opcodes;
243 ImGuiListClipper clipper; // only draw the actually visible rows
244 clipper.Begin(0x10000);
245 if (gotoTarget) {
246 clipper.IncludeItemsByIndex(narrow<int>(*gotoTarget), narrow<int>(*gotoTarget + 4));
247 }
248 std::optional<unsigned> nextGotoTarget;
249 while (clipper.Step()) {
250 auto addr16 = instructionBoundary(cpuInterface, narrow<uint16_t>(clipper.DisplayStart), time);
251 for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; ++row) {
252 unsigned addr = addr16;
253 ImGui::TableNextRow();
254 if (addr >= 0x10000) continue;
255 im::ID(narrow<int>(addr), [&]{
256 if (gotoTarget && addr >= *gotoTarget) {
257 gotoTarget = {};
258 ImGui::SetScrollHereY(0.25f);
259 }
260
261 if (bool rowAtPc = !syncDisassemblyWithPC && (addr == pc);
262 rowAtPc) {
263 ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg1, getColor(imColor::YELLOW_BG));
264 }
265 bool bpRightClick = false;
266 if (ImGui::TableNextColumn()) { // bp
267 auto bpLine = examineBpLine(addr16, bps, cpuInterface, debugger);
268 bool hasBp = bpLine.count != 0;
269 bool multi = bpLine.count > 1;
270 if (hasBp) {
271 auto calcColor = [](bool enabled, bool inSlot) {
272 auto [r,g,b] = enabled ? std::tuple{0xE0, 0x00, 0x00}
273 : std::tuple{0x70, 0x70, 0x70};
274 auto a = inSlot ? 0xFF : 0x60;
275 return IM_COL32(r, g, b, a);
276 };
277 auto colIn = calcColor(bpLine.anyEnabled, bpLine.anyInSlot);
278 auto colOut = ImGui::GetColorU32(ImGuiCol_WindowBg);
279
280 auto* drawList = ImGui::GetWindowDrawList();
281 gl::vec2 scrn = ImGui::GetCursorScreenPos();
282 auto center = scrn + textSize * gl::vec2(multi ? 0.3f : 0.5f, 0.5f);
283 auto radiusIn = 0.4f * textSize;
284 auto radiusOut = 0.5f * textSize;
285
286 if (multi) {
287 auto center2 = center + textSize * gl::vec2(0.4f, 0.0f);
288 drawList->AddCircleFilled(center2, radiusOut, colOut);
289 auto colIn2 = calcColor(!bpLine.anyDisabled, bpLine.anyInSlot);
290 drawList->AddCircleFilled(center2, radiusIn, colIn2);
291 }
292 drawList->AddCircleFilled(center, radiusOut, colOut);
293 drawList->AddCircleFilled(center, radiusIn, colIn);
294 if (bpLine.anyComplex) {
295 auto d = 0.3f * textSize;
296 auto c = IM_COL32(0, 0, 0, 192);
297 auto t = 0.2f * textSize;
298 drawList->AddLine(center - gl::vec2(d, 0.0f), center + gl::vec2(d, 0.0f), c, t);
299 drawList->AddLine(center - gl::vec2(0.0f, d), center + gl::vec2(0.0f, d), c, t);
300 }
301 }
302 if (ImGui::InvisibleButton("##bp-button", {-FLT_MIN, textSize})) {
303 toggleBp(addr16, bpLine, bps, cpuInterface, debugger, addBp, removeBpId);
304 } else {
305 bpRightClick = hasBp && ImGui::IsItemClicked(ImGuiMouseButton_Right);
306 if (bpRightClick) ImGui::OpenPopup("bp-context");
307 im::Popup("bp-context", [&]{
308 manager.breakPoints->paintBpTab(cpuInterface, addr16);
309 });
310 }
311 }
312
313 mnemonic.clear();
314 std::optional<uint16_t> mnemonicAddr;
315 std::span<const Symbol* const> mnemonicLabels;
316 auto len = dasm(cpuInterface, addr16, opcodes, mnemonic, time,
317 [&](std::string& output, uint16_t a) {
318 mnemonicAddr = a;
319 mnemonicLabels = symbolManager.lookupValue(a);
320 if (!mnemonicLabels.empty()) {
321 strAppend(output, mnemonicLabels[cycleLabelsCounter % mnemonicLabels.size()]->name);
322 } else {
323 appendAddrAsHex(output, a);
324 }
325 });
326 assert(len >= 1);
327 if ((addr < pc) && (pc < (addr + len))) {
328 // pc is strictly inside current instruction,
329 // replace the just disassembled instruction with "db #..."
330 mnemonicAddr.reset();
331 mnemonicLabels = {};
332 len = pc - addr;
333 assert((1 <= len) && (len <= 3));
334 mnemonic = strCat("db ", join(
336 [&](unsigned i) { return strCat('#', hex_string<2>(opcodes[i])); }),
337 ','));
338 }
339
340 if (ImGui::TableNextColumn()) { // addr
341 bool focusScrollToAddress = false;
342 bool focusRunToAddress = false;
343
344 // do the full-row-selectable stuff in a column that cannot be hidden
345 auto pos = ImGui::GetCursorPos();
346 ImGui::Selectable("##row", false,
347 ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
348 using enum Shortcuts::ID;
349 auto& shortcuts = manager.getShortcuts();
350 if (shortcuts.checkShortcut(DISASM_GOTO_ADDR)) {
351 ImGui::OpenPopup("disassembly-context");
352 focusScrollToAddress = true;
353 }
354 if (shortcuts.checkShortcut(DISASM_RUN_TO_ADDR)) {
355 ImGui::OpenPopup("disassembly-context");
356 focusRunToAddress = true;
357 }
358 if (!bpRightClick && ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
359 ImGui::OpenPopup("disassembly-context");
360 }
361
362 auto addrStr = tmpStrCat(hex_string<4>(addr));
363 im::Popup("disassembly-context", [&]{
364 auto addrToolTip = [&](const std::string& str) {
365 simpleToolTip([&]{
366 if (auto a = symbolManager.parseSymbolOrValue(str)) {
367 return strCat("0x", hex_string<4>(*a));
368 }
369 return std::string{};
370 });
371 };
372
373 if (ImGui::MenuItem("Scroll to PC")) {
374 nextGotoTarget = pc;
375 }
376
377 im::Indent([&]{
378 ImGui::Checkbox("Follow PC while running", &followPC);
379 ImGui::Checkbox("Scroll to PC on break", &scrollToPcOnBreak);
380 });
381
382 ImGui::AlignTextToFramePadding();
383 ImGui::TextUnformatted("Scroll to address:"sv);
384 ImGui::SameLine();
385 if (focusScrollToAddress) ImGui::SetKeyboardFocusHere();
386 if (ImGui::InputText("##goto", &gotoAddr, ImGuiInputTextFlags_EnterReturnsTrue)) {
387 if (auto a = symbolManager.parseSymbolOrValue(gotoAddr)) {
388 nextGotoTarget = *a;
389 }
390 }
391 addrToolTip(gotoAddr);
392
393 ImGui::Separator();
394
395 auto runTo = strCat("Run to here (address 0x", addrStr, ')');
396 if (ImGui::MenuItem(runTo.c_str())) {
397 manager.executeDelayed(makeTclList("run_to", addr));
398 }
399
400 ImGui::AlignTextToFramePadding();
401 ImGui::TextUnformatted("Run to address:"sv);
402 ImGui::SameLine();
403 if (focusRunToAddress) ImGui::SetKeyboardFocusHere();
404 if (ImGui::InputText("##run", &runToAddr, ImGuiInputTextFlags_EnterReturnsTrue)) {
405 if (auto a = symbolManager.parseSymbolOrValue(runToAddr)) {
406 manager.executeDelayed(makeTclList("run_to", *a));
407 }
408 }
409 addrToolTip(runToAddr);
410
411 ImGui::Separator();
412
413 auto setPc = strCat("Set PC to 0x", addrStr);
414 if (ImGui::MenuItem(setPc.c_str())) {
415 regs.setPC(addr16);
416 }
417 });
418
419 enum class Priority {
420 MISSING_BOTH = 0, // from lowest to highest
421 MISSING_ONE,
422 SLOT_AND_SEGMENT
423 };
424 using enum Priority;
425 Priority currentPriority = MISSING_BOTH;
426 candidates.clear();
427 auto add = [&](const Symbol* sym, Priority newPriority) {
428 if (newPriority < currentPriority) return; // we already have a better candidate
429 if (newPriority > currentPriority) candidates.clear(); // drop previous candidates, we found a better one
430 currentPriority = newPriority;
431 candidates.push_back(sym->name); // cycle symbols in the same priority level
432 };
433
434 auto slot = getCurrentSlot(cpuInterface, debugger, addr16);
435 auto psSs = (slot.ss.value_or(0) << 2) + slot.ps;
436 auto addrLabels = symbolManager.lookupValue(addr16);
437 for (const Symbol* symbol: addrLabels) {
438 // skip symbols with any mismatch
439 if (symbol->slot && *symbol->slot != psSs) continue;
440 if (symbol->segment && *symbol->segment != slot.seg) continue;
441 // the info that's present does match
442 if (symbol->slot && symbol->segment == slot.seg) {
443 add(symbol, SLOT_AND_SEGMENT);
444 } else if (!symbol->slot && !symbol->segment) {
445 add(symbol, MISSING_BOTH);
446 } else {
447 add(symbol, MISSING_ONE);
448 }
449 }
450 ImGui::SetCursorPos(pos);
452 ImGui::TextUnformatted(candidates.empty() ? addrStr : candidates[cycleLabelsCounter % candidates.size()]);
453 });
454 if (!addrLabels.empty()) {
455 simpleToolTip([&]{
456 std::string tip(addrStr);
457 if (addrLabels.size() > 1) {
458 strAppend(tip, "\nmultiple possibilities (click to cycle):\n",
459 join(view::transform(addrLabels, &Symbol::name), ' '));
460 }
461 return tip;
462 });
463 ImGui::SetCursorPos(pos);
464 if (ImGui::InvisibleButton("##addrButton", {-FLT_MIN, textSize})) {
465 ++cycleLabelsCounter;
466 }
467 }
468 }
469
470 if (ImGui::TableNextColumn()) { // opcode
471 opcodesStr.clear();
472 for (auto i : xrange(len)) {
473 strAppend(opcodesStr, hex_string<2>(opcodes[i]), ' ');
474 }
476 ImGui::TextUnformatted(opcodesStr.data(), opcodesStr.data() + 3 * size_t(len) - 1);
477 });
478 }
479
480 if (ImGui::TableNextColumn()) { // mnemonic
481 auto pos = ImGui::GetCursorPos();
483 ImGui::TextUnformatted(mnemonic);
484 });
485 if (mnemonicAddr) {
486 ImGui::SetCursorPos(pos);
487 if (ImGui::InvisibleButton("##mnemonicButton", {-FLT_MIN, textSize})) {
488 if (!mnemonicLabels.empty()) {
489 ++cycleLabelsCounter;
490 }
491 }
492 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
493 nextGotoTarget = *mnemonicAddr;
494 }
495 if (!mnemonicLabels.empty()) {
496 simpleToolTip([&]{
497 auto tip = strCat('#', hex_string<4>(*mnemonicAddr));
498 if (mnemonicLabels.size() > 1) {
499 strAppend(tip, "\nmultiple possibilities (click to cycle):\n",
500 join(view::transform(mnemonicLabels, &Symbol::name), ' '));
501 }
502 return tip;
503 });
504 }
505 }
506 }
507 addr16 += len;
508 });
509 }
510 }
511 gotoTarget = nextGotoTarget;
512 if (syncDisassemblyWithPC) {
513 syncDisassemblyWithPC = false; // only once
514
515 auto itemHeight = ImGui::GetTextLineHeightWithSpacing();
516 auto winHeight = ImGui::GetWindowHeight();
517 auto lines = std::max(1, int(winHeight / itemHeight) - 1); // approx
518
519 auto topAddr = nInstructionsBefore(cpuInterface, pc, time, narrow<int>(lines / 4) + 1);
520
521 ImGui::SetScrollY(float(topAddr) * itemHeight);
522 } else if (setDisassemblyScrollY) {
523 ImGui::SetScrollY(*setDisassemblyScrollY);
524 setDisassemblyScrollY.reset();
525 }
526 disassemblyScrollY = ImGui::GetScrollY();
527 });
528 // only add/remove bp's after drawing (can't change list of bp's while iterating over it)
529 if (addBp) {
530 cpuInterface.insertBreakPoint(std::move(*addBp));
531 }
532 if (removeBpId) {
533 cpuInterface.removeBreakPoint(*removeBpId);
534 }
535 });
536}
537
538} // namespace openmsx
int g
TclObject t
word getPC() const
Definition CPURegs.hh:58
Debuggable * findDebuggable(std::string_view name)
Definition Debugger.cc:66
void loadLine(std::string_view name, zstring_view value) override
void save(ImGuiTextBuffer &buf) override
static std::pair< const MSXRom *, RomBlockDebuggableBase * > getRomBlocks(Debugger &debugger, const MSXDevice *device)
static void actionToggleBp(MSXMotherBoard &motherBoard)
ImGuiDisassembly(ImGuiManager &manager_, size_t index)
void setGotoTarget(uint16_t target)
void paint(MSXMotherBoard *motherBoard) override
std::unique_ptr< ImGuiBreakPoints > breakPoints
Shortcuts & getShortcuts()
void executeDelayed(std::function< void()> action)
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:343
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
Definition MSXDevice.hh:36
MSXCPUInterface & getCPUInterface()
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
auto CalcTextSize(std::string_view str)
Definition ImGuiUtils.hh:39
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:26
vecN< 2, float > vec2
Definition gl_vec.hh:382
void Table(const char *str_id, int column, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width, std::invocable<> auto next)
Definition ImGuiCpp.hh:455
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 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 Popup(const char *str_id, ImGuiWindowFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:391
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:79
void savePersistent(ImGuiTextBuffer &buf, C &c, const std::tuple< Elements... > &tup)
ImU32 getColor(imColor col)
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:196
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:203
TclObject makeTclList(Args &&... args)
Definition TclObject.hh:293
unsigned dasm(std::span< const uint8_t > opcode, uint16_t pc, std::string &dest, function_ref< void(std::string &, uint16_t)> appendAddr)
Disassemble.
Definition Dasm.cc:38
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