200 if (!motherBoard || !
show)
return;
202 ImGui::SetNextWindowSize({340, 540}, ImGuiCond_FirstUseEver);
204 auto& regs = motherBoard->getCPU().getRegisters();
205 auto& cpuInterface = motherBoard->getCPUInterface();
206 auto& debugger = motherBoard->getDebugger();
207 auto time = motherBoard->getCurrentTime();
209 manager.debugger->checkShortcuts(cpuInterface, *motherBoard);
211 std::optional<BreakPoint> addBp;
212 std::optional<unsigned> removeBpId;
214 auto pc = regs.getPC();
215 if (followPC && !MSXCPUInterface::isBreaked()) {
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;
229 ImGui::TableSetupScrollFreeze(0, 1);
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();
236 auto& bps = cpuInterface.getBreakPoints();
237 auto textSize = ImGui::GetTextLineHeight();
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;
244 clipper.Begin(0x10000);
246 clipper.IncludeItemsByIndex(narrow<int>(*gotoTarget), narrow<int>(*gotoTarget + 4));
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) {
258 ImGui::SetScrollHereY(0.25f);
261 if (
bool rowAtPc = !syncDisassemblyWithPC && (addr == pc);
265 bool bpRightClick =
false;
266 if (ImGui::TableNextColumn()) {
267 auto bpLine = examineBpLine(addr16, bps, cpuInterface, debugger);
268 bool hasBp = bpLine.count != 0;
269 bool multi = bpLine.count > 1;
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);
277 auto colIn = calcColor(bpLine.anyEnabled, bpLine.anyInSlot);
278 auto colOut = ImGui::GetColorU32(ImGuiCol_WindowBg);
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;
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);
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;
302 if (ImGui::InvisibleButton(
"##bp-button", {-FLT_MIN, textSize})) {
303 toggleBp(addr16, bpLine, bps, cpuInterface, debugger, addBp, removeBpId);
305 bpRightClick = hasBp && ImGui::IsItemClicked(ImGuiMouseButton_Right);
306 if (bpRightClick) ImGui::OpenPopup(
"bp-context");
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) {
320 if (!mnemonicLabels.empty()) {
321 strAppend(output, mnemonicLabels[cycleLabelsCounter % mnemonicLabels.size()]->name);
323 appendAddrAsHex(output, a);
327 if ((addr < pc) && (pc < (addr + len))) {
330 mnemonicAddr.reset();
333 assert((1 <= len) && (len <= 3));
336 [&](
unsigned i) {
return strCat(
'#', hex_string<2>(opcodes[i])); }),
340 if (ImGui::TableNextColumn()) {
341 bool focusScrollToAddress =
false;
342 bool focusRunToAddress =
false;
345 auto pos = ImGui::GetCursorPos();
346 ImGui::Selectable(
"##row",
false,
347 ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap);
350 if (shortcuts.checkShortcut(DISASM_GOTO_ADDR)) {
351 ImGui::OpenPopup(
"disassembly-context");
352 focusScrollToAddress =
true;
354 if (shortcuts.checkShortcut(DISASM_RUN_TO_ADDR)) {
355 ImGui::OpenPopup(
"disassembly-context");
356 focusRunToAddress =
true;
358 if (!bpRightClick && ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
359 ImGui::OpenPopup(
"disassembly-context");
362 auto addrStr =
tmpStrCat(hex_string<4>(addr));
364 auto addrToolTip = [&](
const std::string& str) {
367 return strCat(
"0x", hex_string<4>(*a));
369 return std::string{};
373 if (ImGui::MenuItem(
"Scroll to PC")) {
378 ImGui::Checkbox(
"Follow PC while running", &followPC);
379 ImGui::Checkbox(
"Scroll to PC on break", &scrollToPcOnBreak);
382 ImGui::AlignTextToFramePadding();
385 if (focusScrollToAddress) ImGui::SetKeyboardFocusHere();
386 if (ImGui::InputText(
"##goto", &gotoAddr, ImGuiInputTextFlags_EnterReturnsTrue)) {
391 addrToolTip(gotoAddr);
395 auto runTo =
strCat(
"Run to here (address 0x", addrStr,
')');
396 if (ImGui::MenuItem(runTo.c_str())) {
400 ImGui::AlignTextToFramePadding();
403 if (focusRunToAddress) ImGui::SetKeyboardFocusHere();
404 if (ImGui::InputText(
"##run", &runToAddr, ImGuiInputTextFlags_EnterReturnsTrue)) {
409 addrToolTip(runToAddr);
413 auto setPc =
strCat(
"Set PC to 0x", addrStr);
414 if (ImGui::MenuItem(setPc.c_str())) {
419 enum class Priority {
425 Priority currentPriority = MISSING_BOTH;
427 auto add = [&](
const Symbol* sym, Priority newPriority) {
428 if (newPriority < currentPriority)
return;
429 if (newPriority > currentPriority) candidates.clear();
430 currentPriority = newPriority;
431 candidates.push_back(sym->
name);
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) {
439 if (symbol->slot && *symbol->slot != psSs)
continue;
440 if (symbol->segment && *symbol->segment != slot.seg)
continue;
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);
447 add(symbol, MISSING_ONE);
450 ImGui::SetCursorPos(pos);
452 ImGui::TextUnformatted(candidates.empty() ? addrStr : candidates[cycleLabelsCounter % candidates.size()]);
454 if (!addrLabels.empty()) {
456 std::string tip(addrStr);
457 if (addrLabels.size() > 1) {
458 strAppend(tip,
"\nmultiple possibilities (click to cycle):\n",
463 ImGui::SetCursorPos(pos);
464 if (ImGui::InvisibleButton(
"##addrButton", {-FLT_MIN, textSize})) {
465 ++cycleLabelsCounter;
470 if (ImGui::TableNextColumn()) {
472 for (
auto i :
xrange(len)) {
473 strAppend(opcodesStr, hex_string<2>(opcodes[i]),
' ');
476 ImGui::TextUnformatted(opcodesStr.data(), opcodesStr.data() + 3 * size_t(len) - 1);
480 if (ImGui::TableNextColumn()) {
481 auto pos = ImGui::GetCursorPos();
483 ImGui::TextUnformatted(mnemonic);
486 ImGui::SetCursorPos(pos);
487 if (ImGui::InvisibleButton(
"##mnemonicButton", {-FLT_MIN, textSize})) {
488 if (!mnemonicLabels.empty()) {
489 ++cycleLabelsCounter;
492 if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
493 nextGotoTarget = *mnemonicAddr;
495 if (!mnemonicLabels.empty()) {
497 auto tip =
strCat(
'#', hex_string<4>(*mnemonicAddr));
498 if (mnemonicLabels.size() > 1) {
499 strAppend(tip,
"\nmultiple possibilities (click to cycle):\n",
511 gotoTarget = nextGotoTarget;
512 if (syncDisassemblyWithPC) {
513 syncDisassemblyWithPC =
false;
515 auto itemHeight = ImGui::GetTextLineHeightWithSpacing();
516 auto winHeight = ImGui::GetWindowHeight();
517 auto lines = std::max(1,
int(winHeight / itemHeight) - 1);
521 ImGui::SetScrollY(
float(topAddr) * itemHeight);
522 }
else if (setDisassemblyScrollY) {
523 ImGui::SetScrollY(*setDisassemblyScrollY);
524 setDisassemblyScrollY.reset();
526 disassemblyScrollY = ImGui::GetScrollY();
530 cpuInterface.insertBreakPoint(std::move(*addBp));
533 cpuInterface.removeBreakPoint(*removeBpId);