186 if (!motherBoard)
return;
188 ImGui::SetNextWindowSize({528, 620}, ImGuiCond_FirstUseEver);
190 auto* vdp = dynamic_cast<VDP*>(motherBoard->findDevice(
"VDP"));
191 if (!vdp || vdp->isMSX1VDP()) return;
193 auto parseMode = [](DisplayMode mode) {
194 auto base = mode.getBase();
195 if (base == DisplayMode::GRAPHIC4) return SCR5;
196 if (base == DisplayMode::GRAPHIC5) return SCR6;
197 if (base == DisplayMode::GRAPHIC6) return SCR7;
198 if (base != DisplayMode::GRAPHIC7) return OTHER;
199 if (mode.getByte() & DisplayMode::YJK) {
200 if (mode.getByte() & DisplayMode::YAE) {
209 int vdpMode = parseMode(vdp->getDisplayMode());
211 int vdpPages = vdpMode <=
SCR6 ? 4 : 2;
212 int vdpPage = vdp->getDisplayPage();
213 if (vdpPage >= vdpPages) vdpPage &= 1;
215 int vdpLines = (vdp->getNumberOfLines() == 192) ? 0 : 1;
221 return vdp->getBackgroundColor() & 15;
224 auto modeToStr = [](
int mode) {
225 if (mode ==
SCR5 )
return "screen 5";
226 if (mode ==
SCR6 )
return "screen 6";
227 if (mode ==
SCR7 )
return "screen 7";
228 if (mode ==
SCR8 )
return "screen 8";
229 if (mode ==
SCR11)
return "screen 11";
230 if (mode ==
SCR12)
return "screen 12";
231 if (mode ==
OTHER)
return "non-bitmap";
232 assert(
false);
return "ERROR";
235 static const char*
const color0Str =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000none\000";
236 bool manualMode = overrideAll || overrideMode;
237 bool manualPage = overrideAll || overridePage;
238 bool manualLines = overrideAll || overrideLines;
239 bool manualColor0 = overrideAll || overrideColor0;
243 ImGui::AlignTextToFramePadding();
247 ImGui::AlignTextToFramePadding();
251 ImGui::AlignTextToFramePadding();
255 ImGui::AlignTextToFramePadding();
262 ImGui::Checkbox(
"Manual override", &overrideAll);
265 ImGui::Checkbox(
"##mode", overrideAll ? &overrideAll : &overrideMode);
266 ImGui::Checkbox(
"##page", overrideAll ? &overrideAll : &overridePage);
267 ImGui::Checkbox(
"##lines", overrideAll ? &overrideAll : &overrideLines);
268 ImGui::Checkbox(
"##color0", overrideAll ? &overrideAll : &overrideColor0);
275 ImGui::Combo(
"##Screen mode", &bitmapScrnMode,
"screen 5\000screen 6\000screen 7\000screen 8\000screen 11\000screen 12\000");
278 int numPages = bitmapScrnMode <=
SCR6 ? 4 : 2;
279 if (bitmapPage >= numPages) bitmapPage = numPages - 1;
280 if (bitmapPage < 0) bitmapPage = numPages;
281 ImGui::Combo(
"##Display page", &bitmapPage, numPages == 2 ?
"0\0001\000All\000" :
"0\0001\0002\0003\000All\000");
282 if (bitmapPage == numPages) bitmapPage = -1;
285 ImGui::Combo(
"##Visible lines", &bitmapLines,
"192\000212\000256\000");
288 ImGui::Combo(
"##Color 0 replacement", &bitmapColor0, color0Str);
295 ImGui::Dummy(ImVec2(15, 1));
298 const auto& vram = vdp->getVRAM();
299 int mode = manualMode ? bitmapScrnMode : vdpMode;
300 int page = manualPage ? bitmapPage : vdpPage;
301 int lines = manualLines ? bitmapLines : vdpLines;
302 int color0 = manualColor0 ? bitmapColor0 : vdpColor0;
304 int width = 512 / divX;
305 int height = (lines == 0) ? 192
309 int numPages = mode <=
SCR6 ? 4 : 2;
310 height = 256 * numPages;
313 auto rasterBeamPos = vdp->getMSXPos(vdp->getCurrentTime());
314 rasterBeamPos.x /= divX;
317 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f);
318 ImGui::Combo(
"Palette", &
manager.
palette->whichPalette,
"VDP\000Custom\000Fixed\000");
319 if (ImGui::Button(
"Open palette editor")) {
323 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 3.0f);
324 ImGui::Combo(
"Zoom", &bitmapZoom,
"1x\0002x\0003x\0004x\0005x\0006x\0007x\0008x\000");
325 ImGui::Checkbox(
"grid", &bitmapGrid);
328 ImGui::ColorEdit4(
"Grid color", bitmapGridColor.data(),
329 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaBar);
332 ImGui::Checkbox(
"beam", &rasterBeam);
335 ImGui::ColorEdit4(
"raster beam color", rasterBeamColor.data(),
336 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaBar);
339 ImGui::StrCat(
'(', dec_string<4>(rasterBeamPos.x),
340 ',', dec_string<4>(rasterBeamPos.y),
')');
343 HelpMarker(
"Position of the raster beam, expressed in MSX coordinates.\n"
344 "Left/top border have negative x/y-coordinates.\n"
345 "Only practically useful when emulation is paused.");
349 auto& cmdEngine = vdp->getCmdEngine();
350 bool inProgress = cmdEngine.commandInProgress(motherBoard->
getCurrentTime());
352 auto [sx, sy, dx, dy, nx, ny, col, arg, cmdReg] = cmdEngine.getLastCommand();
353 auto cmd = cmdReg >> 4;
356 bool byteMode = cmd >= 12;
359 std::optional<static_vector<Rect, 2>> srcRect;
360 std::optional<static_vector<Rect, 2>> dstRect;
361 if (cmd ==
one_of(9, 10, 13)) {
364 if (cmd ==
one_of(8, 9, 11, 12, 13, 15)) {
374 ImGui::RadioButton(
"never", &showCmdOverlay, 0);
375 HelpMarker(
"Don't draw any overlay in the bitmap view");
377 ImGui::RadioButton(
"in progress", &showCmdOverlay, 1);
378 HelpMarker(
"Only show overlay for VDP block command that is currently executing");
380 ImGui::RadioButton(
"also finished", &showCmdOverlay, 2);
381 HelpMarker(
"Show overlay for the currently executing VDP block command, but also the last finished block command");
384 int colorFlags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel |
385 ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreview;
387 ImGui::AlignTextToFramePadding();
389 ImGui::AlignTextToFramePadding();
394 ImGui::ColorEdit4(
"src-done", colorSrcDone.data(), colorFlags);
395 ImGui::ColorEdit4(
"src-todo", colorSrcTodo.data(), colorFlags);
397 ImGui::SameLine(0.0f, ImGui::GetFontSize() * 3.0f);
399 ImGui::AlignTextToFramePadding();
401 ImGui::AlignTextToFramePadding();
406 ImGui::ColorEdit4(
"dst-done", colorDstDone.data(), colorFlags);
407 ImGui::ColorEdit4(
"dst-todo", colorDstTodo.data(), colorFlags);
430 static constexpr std::array<const char*, 16> logOps = {
431 "",
",AND",
",OR",
",XOR",
",NOT",
"",
"",
"",
432 ",TIMP",
",TAND",
",TOR",
",TXOR",
",TNOT",
"",
"",
""
434 const char* logOp = logOps[cmdReg & 15];
435 auto printRect = [](
const char* s, std::optional<static_vector<Rect, 2>>& r) {
438 ImGui::SameLine(0.0f, 0.0f);
439 ImGui::Text(
"(%d,%d)-(%d,%d)",
445 auto printSrcDstRect = [&](
const char* c) {
446 printRect(c, srcRect);
447 ImGui::SameLine(0.0f, 0.0f);
448 printRect(
" TO ", dstRect);
451 ImGui::SameLine(0.0f, 0.0f);
452 ImGui::Text(
",%d", col);
454 auto printLogOp = [&]{
455 ImGui::SameLine(0.0f, 0.0f);
459 case 0:
case 1:
case 2:
case 3:
463 ImGui::Text(
"POINT (%d,%d)", sx, sy);
466 ImGui::Text(
"PSET (%d,%d),%d%s", dx, dy, col, logOp);
469 ImGui::Text(
"SRCH (%d,%d) %s %d", sx, sy,
473 auto nx2 = nx;
auto ny2 = ny;
475 auto x = int(sx) + (dix ? -int(nx2) : int(nx2));
476 auto y = int(sy) + (diy ? -int(ny2) : int(ny2));
477 ImGui::Text(
"LINE (%d,%d)-(%d,%d),%d%s", sx, sy, x, y, col, logOp);
481 printRect(
"LMMV ", dstRect);
486 printSrcDstRect(
"LMMM ");
490 printRect(
"LMCM ", srcRect);
493 printRect(
"LMMC ", dstRect);
497 printRect(
"HMMV ", dstRect);
501 printSrcDstRect(
"HMMM ");
504 printSrcDstRect(
"YMMM ");
507 printRect(
"HMMC ", dstRect);
517 std::array<uint32_t, 16> palette;
520 [](uint16_t msx) { return ImGuiPalette::toRGBA(msx); });
521 if (color0 < 16) palette[0] = palette[color0];
524 renderBitmap(vram.getData(), palette, mode, height, page,
527 bitmapTex.emplace(
false,
false);
530 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
531 GL_RGBA, GL_UNSIGNED_BYTE, pixels.
data());
532 int zx = (1 + bitmapZoom) * divX;
533 int zy = (1 + bitmapZoom) * 2;
534 auto zm =
gl::vec2(
float(zx),
float(zy));
537 auto msxSize =
gl::vec2(
float(width),
float(height));
538 auto size = msxSize * zm;
539 auto availSize =
gl::vec2(ImGui::GetContentRegionAvail()) -
gl::vec2(0.0f, ImGui::GetTextLineHeightWithSpacing());
540 auto reqSize = size +
gl::vec2(ImGui::GetStyle().ScrollbarSize);
541 im::Child(
"##bitmap", min(availSize, reqSize), 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
542 scrnPos = ImGui::GetCursorScreenPos();
543 auto pos = ImGui::GetCursorPos();
544 ImGui::Image(bitmapTex->getImGui(), size);
546 if (bitmapGrid && (zx > 1) && (zy > 1)) {
547 auto color = ImGui::ColorConvertFloat4ToU32(bitmapGridColor);
548 for (
auto y :
xrange(zy)) {
549 auto* line = &pixels[y * zx];
550 for (
auto x :
xrange(zx)) {
551 line[x] = (x == 0 || y == 0) ? color : 0;
554 if (!bitmapGridTex) {
555 bitmapGridTex.emplace(
false,
true);
557 bitmapGridTex->bind();
558 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, zx, zy, 0,
559 GL_RGBA, GL_UNSIGNED_BYTE, pixels.
data());
560 ImGui::SetCursorPos(pos);
561 ImGui::Image(bitmapGridTex->getImGui(), size,
gl::vec2{}, msxSize);
563 auto* drawList = ImGui::GetWindowDrawList();
566 auto color = ImGui::ColorConvertFloat4ToU32(rasterBeamColor);
567 auto thickness = zm.y * 0.5f;
568 auto zm1 = 1.5f * zm;
569 auto zm3 = 3.5f * zm;
570 drawList->AddRect(center - zm, center + zm, color, 0.0f, 0, thickness);
571 drawList->AddLine(center -
gl::vec2{zm1.x, 0.0f}, center -
gl::vec2{zm3.x, 0.0f}, color, thickness);
572 drawList->AddLine(center +
gl::vec2{zm1.x, 0.0f}, center +
gl::vec2{zm3.x, 0.0f}, color, thickness);
573 drawList->AddLine(center -
gl::vec2{0.0f, zm1.y}, center -
gl::vec2{0.0f, zm3.y}, color, thickness);
574 drawList->AddLine(center +
gl::vec2{0.0f, zm1.y}, center +
gl::vec2{0.0f, zm3.y}, color, thickness);
578 auto offset =
gl::vec2(0.0f, height > 256 ? 0.0f : float(256 * page));
579 auto drawRect = [&](
const Rect& r, ImU32 color) {
582 drawList->AddRectFilled(scrnPos + tl, scrnPos + br, color);
584 auto drawSplit = [&](
const DoneTodo& dt, ImU32 colDone, ImU32 colTodo) {
585 for (
const auto& r : dt.
done) drawRect(r, colDone);
586 for (
const auto& r : dt.
todo) drawRect(r, colTodo);
590 drawSplit(split1, colDone, colTodo);
591 if (rects.
size() == 2) {
593 auto col1 = (split1.done.size() == 2) ? colTodo : colDone;
594 drawSplit(split2, col1, colTodo);
597 if ((showCmdOverlay == 2) || ((showCmdOverlay == 1) && inProgress)) {
598 auto [sx2, sy2, dx2, dy2] = cmdEngine.getInprogressPosition();
600 drawOverlay(*srcRect, sx2, sy2,
601 ImGui::ColorConvertFloat4ToU32(colorSrcDone),
602 ImGui::ColorConvertFloat4ToU32(colorSrcTodo));
605 drawOverlay(*dstRect, dx2, dy2,
606 ImGui::ColorConvertFloat4ToU32(colorDstDone),
607 ImGui::ColorConvertFloat4ToU32(colorDstTodo));
611 if (ImGui::IsItemHovered() && (mode !=
OTHER)) {
612 auto [x_, y_] = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / zm);
613 auto x = x_;
auto y = y_;
614 if ((0 <= x) && (x < width) && (0 <= y) && (y < height)) {
615 auto dec3 = [&](
int d) {
617 ImGui::SameLine(0.0f, 0.0f);
618 ImGui::Text(
"%3d", d);
620 auto hex2 = [&](
unsigned h) {
622 ImGui::SameLine(0.0f, 0.0f);
625 auto hex5 = [&](
unsigned h) {
627 ImGui::SameLine(0.0f, 0.0f);
635 if (bitmapPage == -1) {
639 ImGui::SameLine(0.0f, 0.0f);
643 unsigned physAddr = 0x8000 * page + 128 * y;
645 case SCR5: physAddr += x / 2;
break;
646 case SCR6: physAddr += x / 4;
break;
647 case SCR7: physAddr += x / 4 + 0x08000 * (x & 2);
break;
649 physAddr += x / 2 + 0x10000 * (x & 1);
break;
650 default: assert(
false);
653 auto value = vram.getData()[physAddr];
654 auto color = [&]() -> uint8_t {
657 return (value >> (4 * (1 - (x & 1)))) & 0x0f;
659 return (value >> (2 * (3 - (x & 3)))) & 0x03;
674 unsigned logAddr = (physAddr & 0x0ffff) << 1 | (physAddr >> 16);
676 ImGui::SameLine(0.0f, 0.0f);
678 ImGui::SameLine(0.0f, 0.0f);