openMSX
ImGuiBreakPoints.cc
Go to the documentation of this file.
1#include "ImGuiBreakPoints.hh"
2
3#include "ImGuiCpp.hh"
4#include "ImGuiDebugger.hh"
5#include "ImGuiManager.hh"
6#include "ImGuiUtils.hh"
7
8#include "BreakPoint.hh"
9#include "DebugCondition.hh"
10#include "Debugger.hh"
11#include "Interpreter.hh"
12#include "MSXCPUInterface.hh"
13#include "MSXMotherBoard.hh"
14#include "Reactor.hh"
15#include "SymbolManager.hh"
16#include "WatchPoint.hh"
17
18#include "narrow.hh"
19#include "one_of.hh"
20#include "strCat.hh"
21#include "unreachable.hh"
22
23#include <imgui.h>
24#include <imgui_stdlib.h>
25
26#include <cstdint>
27#include <tuple>
28#include <vector>
29
30using namespace std::literals;
31
32namespace openmsx {
33
35 : ImGuiPart(manager_)
36 , symbolManager(manager.getReactor().getSymbolManager())
37{
38}
39
40void ImGuiBreakPoints::save(ImGuiTextBuffer& buf)
41{
42 savePersistent(buf, *this, persistentElements);
43}
44
45void ImGuiBreakPoints::loadLine(std::string_view name, zstring_view value)
46{
47 loadOnePersistent(name, value, *this, persistentElements);
48}
49
51{
52 if (!motherBoard || !show) return;
53
54 ImGui::SetNextWindowSize(gl::vec2{25, 14} * ImGui::GetFontSize(), ImGuiCond_FirstUseEver);
55 im::Window("Breakpoints", &show, [&]{
56 im::TabBar("tabs", [&]{
57 auto& cpuInterface = motherBoard->getCPUInterface();
58 im::TabItem("Breakpoints", [&]{
59 paintTab<BreakPoint>(cpuInterface);
60 });
61 im::TabItem("Watchpoints", [&]{
62 paintTab<WatchPoint>(cpuInterface);
63 });
64 im::TabItem("Conditions", [&]{
65 paintTab<DebugCondition>(cpuInterface);
66 });
67 });
68 });
69}
70
71template<typename T> static std::string_view getCheckCmd();
72template<> std::string_view getCheckCmd<BreakPoint>() { return "pc_in_slot"; }
73template<> std::string_view getCheckCmd<WatchPoint>() { return "watch_in_slot"; }
74template<> std::string_view getCheckCmd<DebugCondition>() { return "pc_in_slot"; }
75
76ParsedSlotCond::ParsedSlotCond(std::string_view checkCmd, std::string_view cond)
77 : rest(cond) // for when we fail to match a slot-expression
78{
79 size_t pos = 0;
80 auto end = cond.size();
81 std::optional<int> o_ps;
82 std::optional<int> o_ss;
83 std::optional<int> o_seg;
84
85 auto next = [&](std::string_view s) {
86 if (cond.substr(pos).starts_with(s)) {
87 pos += s.size();
88 return true;
89 }
90 return false;
91 };
92 auto done = [&]{
93 bool stop = cond.substr(pos) == "]";
94 if (stop || (next("] && (") && cond.ends_with(')'))) {
95 if (stop) {
96 rest.clear();
97 } else {
98 rest = cond.substr(pos, cond.size() - pos - 1);
99 }
100 if (o_ps) { hasPs = true; ps = *o_ps; }
101 if (o_ss) { hasSs = true; ss = *o_ss; }
102 if (o_seg) { hasSeg = true; seg = narrow<uint8_t>(*o_seg); }
103 return true;
104 }
105 return false;
106 };
107 auto isDigit = [](char c) { return ('0' <= c) && (c <= '9'); };
108 auto getInt = [&](unsigned max) -> std::optional<int> {
109 unsigned i = 0;
110 if ((pos == end) || !isDigit(cond[pos])) return {};
111 while ((pos != end) && isDigit(cond[pos])) {
112 i = 10 * i + (cond[pos] - '0');
113 ++pos;
114 }
115 if (i >= max) return {};
116 return i;
117 };
118
119 if (!next(tmpStrCat('[', checkCmd, ' '))) return; // no slot
120 o_ps = getInt(4);
121 if (!o_ps) return; // invalid ps
122 if (done()) return; // ok, only ps
123 if (!next(" ")) return; // invalid separator
124 if (!next("X")) {
125 o_ss = getInt(4);
126 if (!o_ss) return; // invalid ss
127 }
128 if (done()) return; // ok, ps + ss
129 if (!next(" ")) return; // invalid separator
130 o_seg = getInt(256);
131 if (!o_seg) return; // invalid seg
132 if (done()) return; // ok, ps + ss + seg
133 // invalid terminator
134}
135
136std::string ParsedSlotCond::toTclExpression(std::string_view checkCmd) const
137{
138 if (!hasPs) return rest;
139 std::string result = strCat('[', checkCmd, ' ', ps);
140 if (hasSs) {
141 strAppend(result, ' ', ss);
142 } else {
143 if (hasSeg) strAppend(result, " X");
144 }
145 if (hasSeg) {
146 strAppend(result, ' ', seg);
147 }
148 strAppend(result, ']');
149 if (!rest.empty()) {
150 strAppend(result, " && (", rest, ')');
151 }
152 return result;
153}
154
156{
157 if (!hasPs) return rest;
158 std::string result = strCat("Slot:", ps, '-');
159 if (hasSs) {
160 strAppend(result, ss);
161 } else {
162 strAppend(result, 'X');
163 }
164 if (hasSeg) {
165 strAppend(result, ',', seg);
166 }
167 if (!rest.empty()) {
168 strAppend(result, " && ", rest);
169 }
170 return result;
171}
172
173template<typename> struct BaseBpType;
174template<> struct BaseBpType<BreakPoint> { using type = BreakPoint; };
175template<> struct BaseBpType<std::shared_ptr<WatchPoint>> { using type = WatchPoint; };
176template<> struct BaseBpType<DebugCondition> { using type = DebugCondition; };
177template<typename T> using BaseBpType_t = typename BaseBpType<T>::type;
178
179template<typename Item> struct HasAddress : std::true_type {};
180template<> struct HasAddress<DebugCondition> : std::false_type {};
181
182template<typename Item> struct AllowEmptyCond : std::true_type {};
183template<> struct AllowEmptyCond<DebugCondition> : std::false_type {};
184
185static std::vector<BreakPoint>& getItems(BreakPoint*, MSXCPUInterface&)
186{
188}
189static std::vector<std::shared_ptr<WatchPoint>>& getItems(WatchPoint*, MSXCPUInterface& cpuInterface)
190{
191 return cpuInterface.getWatchPoints();
192}
193static std::vector<DebugCondition>& getItems(DebugCondition*, MSXCPUInterface&)
194{
196}
197
198template<typename T> static void createNew(MSXCPUInterface& cpuInterface, Interpreter& interp, std::optional<uint16_t> addr);
199template<> void createNew<BreakPoint>(MSXCPUInterface& cpuInterface, Interpreter& interp, std::optional<uint16_t> addr)
200{
201 BreakPoint bp;
202 if (addr) {
203 bp.setAddress(interp, TclObject(tmpStrCat("0x", hex_string<4>(*addr))));
204 }
205 cpuInterface.insertBreakPoint(std::move(bp));
206}
207template<> void createNew<WatchPoint>(MSXCPUInterface& cpuInterface, Interpreter&, std::optional<uint16_t>)
208{
209 cpuInterface.setWatchPoint(std::make_shared<WatchPoint>());
210}
211template<> void createNew<DebugCondition>(MSXCPUInterface& cpuInterface, Interpreter&, std::optional<uint16_t>)
212{
213 cpuInterface.setCondition(DebugCondition());
214}
215
216template<typename T> static void remove(MSXCPUInterface& cpuInterface, unsigned id);
217template<> void remove<BreakPoint>(MSXCPUInterface& cpuInterface, unsigned id) {
218 cpuInterface.removeBreakPoint(id);
219}
220template<> void remove<WatchPoint>(MSXCPUInterface& cpuInterface, unsigned id) {
221 cpuInterface.removeWatchPoint(id);
222}
223template<> void remove<DebugCondition>(MSXCPUInterface& cpuInterface, unsigned id) {
224 cpuInterface.removeCondition(id);
225}
226
227[[nodiscard]] static unsigned getId(const BreakPoint& bp) { return bp.getId(); }
228[[nodiscard]] static unsigned getId(const std::shared_ptr<WatchPoint>& wp) { return wp->getId(); }
229[[nodiscard]] static unsigned getId(const DebugCondition& cond) { return cond.getId(); }
230
231[[nodiscard]] static bool getEnabled(const BreakPoint& bp) { return bp.isEnabled(); }
232[[nodiscard]] static bool getEnabled(const std::shared_ptr<WatchPoint>& wp) { return wp->isEnabled(); }
233[[nodiscard]] static bool getEnabled(const DebugCondition& cond) { return cond.isEnabled(); }
234static void setEnabled(BreakPoint& bp, bool e) { bp.setEnabled(e); }
235static void setEnabled(std::shared_ptr<WatchPoint>& wp, bool e) { wp->setEnabled(e); }
236static void setEnabled(DebugCondition& cond, bool e) { cond.setEnabled(e); }
237
238[[nodiscard]] static std::optional<uint16_t> getAddress(const BreakPoint& bp) { return bp.getAddress(); }
239[[nodiscard]] static std::optional<uint16_t> getAddress(const std::shared_ptr<WatchPoint>& wp) { return wp->getBeginAddress(); }
240[[nodiscard]] static std::optional<uint16_t> getAddress(const DebugCondition&) { return {}; }
241
242[[nodiscard]] static TclObject getAddressString(const BreakPoint& bp) { return bp.getAddressString(); }
243[[nodiscard]] static TclObject getAddressString(const std::shared_ptr<WatchPoint>& wp) { return wp->getBeginAddressString(); }
244[[nodiscard]] static TclObject getAddressString(const DebugCondition&) { return {}; }
245
246[[nodiscard]] static TclObject getEndAddressString(const BreakPoint&) { return {}; }
247[[nodiscard]] static TclObject getEndAddressString(const std::shared_ptr<WatchPoint>& wp) { return wp->getEndAddressString(); }
248
249[[nodiscard]] static TclObject getCondition(const BreakPoint& bp) { return bp.getCondition(); }
250[[nodiscard]] static TclObject getCondition(const std::shared_ptr<WatchPoint>& wp) { return wp->getCondition(); }
251[[nodiscard]] static TclObject getCondition(const DebugCondition& cond) { return cond.getCondition(); }
252static void setCondition(BreakPoint& bp, const TclObject& c) { bp.setCondition(c); }
253static void setCondition(std::shared_ptr<WatchPoint>& wp, const TclObject& c) { wp->setCondition(c); }
254static void setCondition(DebugCondition& cond, const TclObject& c) { cond.setCondition(c); }
255
256[[nodiscard]] static TclObject getCommand(const BreakPoint& bp) { return bp.getCommand(); }
257[[nodiscard]] static TclObject getCommand(const std::shared_ptr<WatchPoint>& wp) { return wp->getCommand(); }
258[[nodiscard]] static TclObject getCommand(const DebugCondition& cond) { return cond.getCommand(); }
259static void setCommand(BreakPoint& bp, const TclObject& c) { bp.setCommand(c); }
260static void setCommand(std::shared_ptr<WatchPoint>& wp, const TclObject& c) { wp->setCommand(c); }
261static void setCommand(DebugCondition& cond, const TclObject& c) { cond.setCommand(c); }
262
263[[nodiscard]] static bool getOnce(const BreakPoint& bp) { return bp.onlyOnce(); }
264[[nodiscard]] static bool getOnce(const std::shared_ptr<WatchPoint>& wp) { return wp->onlyOnce(); }
265[[nodiscard]] static bool getOnce(const DebugCondition& cond) { return cond.onlyOnce(); }
266static void setOnce(BreakPoint& bp, bool o) { bp.setOnce(o); }
267static void setOnce(std::shared_ptr<WatchPoint>& wp, bool o) { wp->setOnce(o); }
268static void setOnce(DebugCondition& cond, bool o) { cond.setOnce(o); }
269
271[[nodiscard]] static DummyScopedChange getScopedChange(BreakPoint&, MSXCPUInterface&) { return {}; }
272[[nodiscard]] static auto getScopedChange(std::shared_ptr<WatchPoint>& wp, MSXCPUInterface& cpuInterface) {
273 return cpuInterface.getScopedChangeWatchpoint(wp);
274}
275[[nodiscard]] static DummyScopedChange getScopedChange(DebugCondition&, MSXCPUInterface&) { return {}; }
276
277template<typename Item>
278static void checkSort(std::vector<Item>& items)
279{
280 using Type = BaseBpType_t<Item>;
281 constexpr bool isWatchPoint = std::is_same_v<Type, WatchPoint>;
282 constexpr bool hasAddress = HasAddress<Type>{};
283
284 auto* sortSpecs = ImGui::TableGetSortSpecs();
285 if (!sortSpecs->SpecsDirty) return;
286
287 sortSpecs->SpecsDirty = false;
288 assert(sortSpecs->SpecsCount == 1);
289 assert(sortSpecs->Specs);
290 assert(sortSpecs->Specs->SortOrder == 0);
291
292 switch (sortSpecs->Specs->ColumnIndex) {
293 case 0: // enable
294 sortUpDown_T(items, sortSpecs, [](const auto& item) { return getEnabled(item); });
295 break;
296 case 1: // type
297 if constexpr (isWatchPoint) {
298 sortUpDown_T(items, sortSpecs, &WatchPoint::getType);
299 }
300 break;
301 case 2: // addr
302 if constexpr (hasAddress) {
303 sortUpDown_T(items, sortSpecs, [](const auto& item) {
304 return std::tuple(getAddressString (item).getString(),
305 getEndAddressString(item).getString());
306 });
307 }
308 break;
309 case 3: // cond
310 sortUpDown_String(items, sortSpecs, [](const auto& item) { return getCondition(item).getString(); });
311 break;
312 case 4: // action
313 sortUpDown_String(items, sortSpecs, [](const auto& item) { return getCommand(item).getString(); });
314 break;
315 default:
317 }
318}
319
320template<typename Type>
321[[nodiscard]] static std::string isValidCond(std::string_view cond, Interpreter& interp)
322{
323 if (cond.empty()) {
324 return AllowEmptyCond<Type>{} ? std::string{}
325 : std::string("cannot be empty");
326 }
327 return interp.parseExpressionError(cond);
328}
329
330[[nodiscard]] static std::string isValidCmd(std::string_view cmd, Interpreter& interp)
331{
332 if (cmd.empty()) return {}; // ok
333 return interp.parseCommandError(cmd);
334}
335
336std::string ImGuiBreakPoints::displayAddr(const TclObject& addr) const
337{
338 auto str = addr.getString();
339 if (auto symbol = [&]() -> std::optional<std::string_view> {
340 if (str.ends_with(')')) {
341 if (str.starts_with("$sym(")) return str.substr(5, str.size() - 6);
342 if (str.starts_with("$::sym(")) return str.substr(7, str.size() - 8);
343 }
344 return {};
345 }()) {
346 if (symbolManager.lookupSymbol(*symbol)) {
347 return std::string(*symbol);
348 }
349 }
350 return std::string(str);
351}
352
353std::string ImGuiBreakPoints::parseDisplayAddress(std::string_view str) const
354{
355 if (symbolManager.lookupSymbol(str)) {
356 return strCat("$sym(", str, ')');
357 }
358 return std::string(str);
359}
360
361template<typename Item>
362void ImGuiBreakPoints::drawRow(MSXCPUInterface& cpuInterface, int row, Item& item)
363{
364 using Type = BaseBpType_t<Item>;
365 constexpr bool isWatchPoint = std::is_same_v<Type, WatchPoint>;
366 constexpr bool isBreakPoint = std::is_same_v<Type, BreakPoint>;
367
368 auto& interp = manager.getInterpreter();
369 const auto& style = ImGui::GetStyle();
370 float rowHeight = 2.0f * style.FramePadding.y + ImGui::GetTextLineHeight();
371
372 auto setRedBg = [](bool valid) {
373 if (valid) return;
374 ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, getColor(imColor::RED_BG));
375 };
376
377 if (ImGui::TableNextColumn()) { // enable
378 auto pos = ImGui::GetCursorPos();
379 if (ImGui::Selectable("##selection", selectedRow == row,
380 ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap,
381 {0.0f, rowHeight})) {
382 selectedRow = row;
383 }
384 ImGui::SetCursorPos(pos);
385
386 bool enabled = getEnabled(item);
387 if (ImGui::Checkbox("##enabled", &enabled)) {
388 auto sc = getScopedChange(item, cpuInterface); (void)sc;
389 setEnabled(item, enabled);
390 }
391 if (ImGui::IsItemActive()) selectedRow = row;
392 }
393 if (ImGui::TableNextColumn()) { // type
394 if constexpr (isWatchPoint) {
395 ImGui::SetNextItemWidth(-FLT_MIN);
396 int wpType = static_cast<int>(item->getType());
397 if (ImGui::Combo("##type", &wpType, "read IO\000write IO\000read memory\000write memory\000")) {
398 auto sc = getScopedChange(item, cpuInterface); (void)sc;
399 item->setType(interp, static_cast<WatchPoint::Type>(wpType));
400 }
401 if (ImGui::IsItemActive()) selectedRow = row;
402 }
403 }
404 if (ImGui::TableNextColumn()) { // address
405 std::string addrStr = displayAddr(getAddressString(item));
406 ImGui::SetNextItemWidth(-FLT_MIN);
407 if constexpr (isWatchPoint) {
408 std::string parseError = item->parseAddressError(interp);
409 setRedBg(parseError.empty());
410
411 auto pos = ImGui::GetCursorPos();
412 std::string endAddrStr = displayAddr(item->getEndAddressString());
413 std::string displayAddr = addrStr;
414 if (!endAddrStr.empty()) {
415 strAppend(displayAddr, "...", endAddrStr);
416 }
418 ImGui::TextUnformatted(displayAddr);
419 });
420 ImGui::SetCursorPos(pos);
421 if (ImGui::InvisibleButton("##range-button", {-FLT_MIN, rowHeight})) {
422 ImGui::OpenPopup("range-popup");
423 }
424
425 auto addr = item->getBeginAddress();
426 if (parseError.empty() && addr) {
427 simpleToolTip([&]{
428 auto tip = strCat("0x", hex_string<4>(*addr));
429 if (auto endAddr = item->getEndAddress(); endAddr && *endAddr != *addr) {
430 strAppend(tip, "...0x", hex_string<4>(*endAddr));
431 }
432 return tip;
433 });
434 } else {
435 simpleToolTip(parseError);
436 }
437
438 if (ImGui::IsItemActive()) selectedRow = row;
439 im::Popup("range-popup", [&]{
440 editRange(cpuInterface, item, addrStr, endAddrStr);
441 });
442 } else if constexpr (isBreakPoint) {
443 std::string parseError = item.parseAddressError(interp);
444 setRedBg(parseError.empty());
445
447 if (ImGui::InputText("##addr", &addrStr)) {
448 auto sc = getScopedChange(item, cpuInterface); (void)sc;
449 item.setAddress(interp, TclObject(parseDisplayAddress(addrStr)));
450 }
451 });
452
453 auto addr = item.getAddress();
454 if (parseError.empty() && addr) {
455 simpleToolTip([&]{ return strCat("0x", hex_string<4>(*addr)); });
456 } else {
457 simpleToolTip(parseError);
458 }
459
460 im::PopupContextItem("context menu", [&]{
461 if (ImGui::MenuItem("Show in Disassembly", nullptr, nullptr, addr.has_value())) {
462 manager.debugger->setGotoTarget(*addr);
463 }
464 });
465
466 if (ImGui::IsItemActive()) selectedRow = row;
467 }
468 }
469 if (ImGui::TableNextColumn()) { // condition
470 std::string cond{getCondition(item).getString()};
471 std::string parseError = isValidCond<Type>(cond, interp);
472 setRedBg(parseError.empty());
473 auto checkCmd = getCheckCmd<Type>();
474 ParsedSlotCond slot(checkCmd, cond);
475 auto pos = ImGui::GetCursorPos();
476 im::Font(manager.fontMono, [&]{
477 ImGui::TextUnformatted(slot.toDisplayString());
478 });
479 ImGui::SetCursorPos(pos);
480 if (ImGui::InvisibleButton("##cond-button", {-FLT_MIN, rowHeight})) {
481 ImGui::OpenPopup("cond-popup");
482 }
483 if (ImGui::IsItemActive()) selectedRow = row;
484 simpleToolTip(parseError);
485 im::Popup("cond-popup", [&]{
486 if (editCondition(slot)) {
487 cond = slot.toTclExpression(checkCmd);
488 setCondition(item, TclObject(cond));
489 }
490 });
491 }
492 if (ImGui::TableNextColumn()) { // action
493 std::string cmd{getCommand(item).getString()};
494 std::string parseError = isValidCmd(cmd, interp);
495 setRedBg(parseError.empty());
496 im::Font(manager.fontMono, [&]{
497 ImGui::SetNextItemWidth(-FLT_MIN);
498 if (ImGui::InputText("##cmd", &cmd)) {
499 setCommand(item, TclObject(cmd));
500 }
501 if (ImGui::IsItemActive()) selectedRow = row;
502 });
503 simpleToolTip(parseError);
504 }
505 if (ImGui::TableNextColumn()) { // once
506 bool once = getOnce(item);
507 if (ImGui::Checkbox("##once", &once)) {
508 setOnce(item, once);
509 }
510 if (ImGui::IsItemActive()) selectedRow = row;
511 }
512}
513
514void ImGuiBreakPoints::paintBpTab(MSXCPUInterface& cpuInterface, uint16_t addr)
515{
516 paintTab<BreakPoint>(cpuInterface, addr);
517}
518
519template<typename Type>
520void ImGuiBreakPoints::paintTab(MSXCPUInterface& cpuInterface, std::optional<uint16_t> addr)
521{
522 constexpr bool isWatchPoint = std::is_same_v<Type, WatchPoint>;
523 constexpr bool isCondition = std::is_same_v<Type, DebugCondition>;
524 bool hasAddress = HasAddress<Type>{} && !addr; // don't draw address-column if filtering a specific address
525 Type* tag = nullptr;
526 auto& items = getItems(tag, cpuInterface);
527
528 int flags = ImGuiTableFlags_RowBg |
529 ImGuiTableFlags_BordersV |
530 ImGuiTableFlags_BordersOuter |
531 ImGuiTableFlags_Resizable |
532 ImGuiTableFlags_Sortable |
533 ImGuiTableFlags_Hideable |
534 ImGuiTableFlags_Reorderable |
535 ImGuiTableFlags_ContextMenuInBody |
536 ImGuiTableFlags_SizingStretchProp;
537 if (!addr) {
538 flags |= ImGuiTableFlags_ScrollY
539 | ImGuiTableFlags_ScrollX;
540 }
541 const auto& style = ImGui::GetStyle();
542 auto width = style.ItemSpacing.x + 2.0f * style.FramePadding.x + ImGui::CalcTextSize("Remove").x;
543 bool disableRemove = true;
544 int count = 0;
545 int lastDrawnRow = -1; // should only be used when count=1
546 im::Table("items", 6, flags, {-width, 0}, [&]{
547 ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible
548 ImGui::TableSetupColumn("Enable", ImGuiTableColumnFlags_WidthFixed);
549 int typeFlags = isWatchPoint ? ImGuiTableColumnFlags_NoHide : ImGuiTableColumnFlags_Disabled;
550 ImGui::TableSetupColumn("Type", typeFlags);
551 int addressFlags = hasAddress ? ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_DefaultSort
552 : ImGuiTableColumnFlags_Disabled;
553 ImGui::TableSetupColumn("Address", addressFlags);
554 ImGui::TableSetupColumn("Condition", isCondition ? ImGuiTableColumnFlags_NoHide : 0);
555 ImGui::TableSetupColumn("Action", addr ? 0 : ImGuiTableColumnFlags_DefaultHide);
556 ImGui::TableSetupColumn("Once", ImGuiTableColumnFlags_DefaultHide);
557 ImGui::TableHeadersRow();
558
559 checkSort(items);
560
561 im::ID_for_range(items.size(), [&](int row) {
562 if (!addr || (getAddress(items[row]) == addr)) {
563 ++count; lastDrawnRow = row;
564 if (row == selectedRow) disableRemove = false;
565 drawRow(cpuInterface, row, items[row]);
566 }
567 });
568 });
569 if (count == 1) disableRemove = false;
570 ImGui::SameLine();
571 im::Group([&] {
572 if (ImGui::Button("Add")) {
573 createNew<Type>(cpuInterface, manager.getInterpreter(), addr);
574 selectedRow = -1;
575 }
576 im::Disabled(disableRemove, [&]{
577 if (ImGui::Button("Remove")) {
578 int removeRow = (count == 1) ? lastDrawnRow : selectedRow;
579 auto it = items.begin() + removeRow;
580 remove<Type>(cpuInterface, getId(*it));
581 selectedRow = -1;
582 }
583 });
584 if (!addr) {
585 ImGui::Spacing();
586 im::Disabled(items.empty() ,[&]{
587 if (ImGui::Button("Clear")) {
588 while (!items.empty()) {
589 remove<Type>(cpuInterface, getId(items.back()));
590 }
591 }
592 });
593 }
594 });
595}
596
597void ImGuiBreakPoints::editRange(MSXCPUInterface& cpuInterface, std::shared_ptr<WatchPoint>& wp, std::string& begin, std::string& end)
598{
599 ImGui::TextUnformatted("address range"sv);
600 im::Indent([&]{
601 const auto& style = ImGui::GetStyle();
602 auto pos = ImGui::GetCursorPos().x + ImGui::CalcTextSize("end: (?)").x + 2.0f * style.ItemSpacing.x;
603
604 ImGui::AlignTextToFramePadding();
605 ImGui::TextUnformatted("begin: "sv);
606 ImGui::SameLine(pos);
607 im::Font(manager.fontMono, [&]{
608 if (ImGui::InputText("##begin", &begin)) {
609 auto& interp = manager.getInterpreter();
610 auto sc = getScopedChange(wp, cpuInterface); (void)sc;
611 wp->setBeginAddressString(interp, TclObject(parseDisplayAddress(begin)));
612 }
613 });
614
615 ImGui::AlignTextToFramePadding();
616 ImGui::TextUnformatted("end:"sv);
617 HelpMarker("End address is included in the range.\n"
618 "Leave empty for a single address.");
619 ImGui::SameLine(pos);
620 im::Font(manager.fontMono, [&]{
621 if (ImGui::InputText("##end", &end)) {
622 auto& interp = manager.getInterpreter();
623 auto sc = getScopedChange(wp, cpuInterface); (void)sc;
624 wp->setEndAddressString(interp, TclObject(parseDisplayAddress(end)));
625 }
626 });
627 });
628}
629
630bool ImGuiBreakPoints::editCondition(ParsedSlotCond& slot)
631{
632 bool changed = false;
633 ImGui::TextUnformatted("slot"sv);
634 im::Indent([&]{
635 const auto& style = ImGui::GetStyle();
636 auto pos = ImGui::GetCursorPos().x + ImGui::GetFrameHeight() +
637 ImGui::CalcTextSize("secondary").x + 2.0f * style.ItemSpacing.x;
638
639 changed |= ImGui::Checkbox("primary", &slot.hasPs);
640 ImGui::SameLine(pos);
641 im::Disabled(!slot.hasPs, [&]{
642 changed |= ImGui::Combo("##ps", &slot.ps, "0\0001\0002\0003\000");
643
644 changed |= ImGui::Checkbox("secondary", &slot.hasSs);
645 ImGui::SameLine(pos);
646 im::Disabled(!slot.hasSs, [&]{
647 changed |= ImGui::Combo("##ss", &slot.ss, "0\0001\0002\0003\000");
648 });
649
650 changed |= ImGui::Checkbox("segment", &slot.hasSeg);
651 ImGui::SameLine(pos);
652 im::Disabled(!slot.hasSeg, [&]{
653 uint8_t one = 1;
654 changed |= ImGui::InputScalar("##seg", ImGuiDataType_U8, &slot.seg, &one);
655 });
656 });
657 });
658 ImGui::TextUnformatted("Tcl expression"sv);
659 im::Indent([&]{
660 im::Font(manager.fontMono, [&]{
661 ImGui::SetNextItemWidth(-FLT_MIN);
662 changed |= ImGui::InputText("##cond", &slot.rest);
663 });
664 });
665 return changed;
666}
667
668void ImGuiBreakPoints::refreshSymbols()
669{
670 auto& interp = manager.getInterpreter();
671 for (auto& bp : MSXCPUInterface::getBreakPoints()) {
672 bp.evaluateAddress(interp);
673 }
674 if (auto* motherBoard = manager.getReactor().getMotherBoard()) {
675 auto& cpuInterface = motherBoard->getCPUInterface();
676 for (auto& wp : cpuInterface.getWatchPoints()) {
677 auto sc = getScopedChange(wp, cpuInterface);
678 wp->evaluateAddress(interp);
679 }
680 }
681 // nothing for DebugCondition
682}
683
684} // namespace openmsx
void setAddress(Interpreter &interp, const TclObject &addr)
Definition BreakPoint.hh:22
General debugger condition Like breakpoints, but not tied to a specific address.
ImGuiBreakPoints(ImGuiManager &manager)
void save(ImGuiTextBuffer &buf) override
void loadLine(std::string_view name, zstring_view value) override
void paint(MSXMotherBoard *motherBoard) override
Interpreter & getInterpreter()
ImGuiManager & manager
Definition ImGuiPart.hh:30
void setWatchPoint(const std::shared_ptr< WatchPoint > &watchPoint)
void insertBreakPoint(BreakPoint bp)
void removeCondition(const DebugCondition &cond)
static BreakPoints & getBreakPoints()
void removeWatchPoint(std::shared_ptr< WatchPoint > watchPoint)
void setCondition(DebugCondition cond)
void removeBreakPoint(const BreakPoint &bp)
static Conditions & getConditions()
MSXCPUInterface & getCPUInterface()
std::optional< uint16_t > lookupSymbol(std::string_view s) const
zstring_view getString() const
Definition TclObject.cc:141
Type getType() const
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
auto CalcTextSize(std::string_view str)
Definition ImGuiUtils.hh:39
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:26
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 PopupContextItem(const char *str_id, ImGuiPopupFlags popup_flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:421
void TabBar(const char *str_id, ImGuiTabBarFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:476
void Disabled(bool b, std::invocable<> auto next)
Definition ImGuiCpp.hh:506
void Group(std::invocable<> auto next)
Definition ImGuiCpp.hh:236
void Font(ImFont *font, std::invocable<> auto next)
Definition ImGuiCpp.hh:131
void TabItem(const char *label, bool *p_open, ImGuiTabItemFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:489
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
void ID_for_range(std::integral auto count, std::invocable< int > auto next)
Definition ImGuiCpp.hh:281
This file implemented 3 utility functions:
Definition Autofire.cc:11
void remove< BreakPoint >(MSXCPUInterface &cpuInterface, unsigned id)
void createNew< BreakPoint >(MSXCPUInterface &cpuInterface, Interpreter &interp, std::optional< uint16_t > addr)
bool loadOnePersistent(std::string_view name, zstring_view value, C &c, const std::tuple< Elements... > &tup)
std::string_view getCheckCmd< BreakPoint >()
void sortUpDown_String(Range &range, const ImGuiTableSortSpecs *sortSpecs, Projection proj)
void simpleToolTip(std::string_view desc)
Definition ImGuiUtils.hh:79
void createNew< DebugCondition >(MSXCPUInterface &cpuInterface, Interpreter &, std::optional< uint16_t >)
void createNew< WatchPoint >(MSXCPUInterface &cpuInterface, Interpreter &, std::optional< uint16_t >)
void savePersistent(ImGuiTextBuffer &buf, C &c, const std::tuple< Elements... > &tup)
std::string_view getCheckCmd< WatchPoint >()
void remove< WatchPoint >(MSXCPUInterface &cpuInterface, unsigned id)
void sortUpDown_T(Range &range, const ImGuiTableSortSpecs *sortSpecs, Projection proj)
void HelpMarker(std::string_view desc)
Definition ImGuiUtils.cc:23
ImU32 getColor(imColor col)
std::string_view getCheckCmd< DebugCondition >()
typename BaseBpType< T >::type BaseBpType_t
void remove< DebugCondition >(MSXCPUInterface &cpuInterface, unsigned id)
auto count(InputRange &&range, const T &value)
Definition ranges.hh:349
STL namespace.
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::string toDisplayString() const
std::string toTclExpression(std::string_view checkCmd) const
ParsedSlotCond(std::string_view checkCmd, std::string_view cond)
#define UNREACHABLE
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)