78 if (!
show || !motherBoard)
return;
79 ImGui::SetNextWindowSize({692, 886}, ImGuiCond_FirstUseEver);
81 auto* vdp = dynamic_cast<VDP*>(motherBoard->findDevice(
"VDP"));
83 const auto& vram = vdp->getVRAM().getData();
86 auto base = vdp->getDisplayMode().getBase();
87 if (base == DisplayMode::TEXT1) return TEXT40;
88 if (base == DisplayMode::TEXT2) return TEXT80;
89 if (base == DisplayMode::GRAPHIC1) return SCR1;
90 if (base == DisplayMode::GRAPHIC2) return SCR2;
91 if (base == DisplayMode::GRAPHIC3) return SCR4;
92 if (base == DisplayMode::MULTICOLOR) return SCR3;
95 auto modeToStr = [](
int mode) {
96 if (mode == TEXT40)
return "screen 0, width 40";
97 if (mode == TEXT80)
return "screen 0, width 80";
98 if (mode == SCR1)
return "screen 1";
99 if (mode == SCR2)
return "screen 2";
100 if (mode == SCR3)
return "screen 3";
101 if (mode == SCR4)
return "screen 4";
102 if (mode == OTHER)
return "non-character";
103 assert(
false);
return "ERROR";
105 auto patMult = [](
int mode) {
return 1 << (mode ==
one_of(SCR2, SCR4) ? 13 : 11); };
106 auto colMult = [](
int mode) {
return 1 << (mode ==
one_of(SCR2, SCR4) ? 13 :
107 mode == TEXT80 ? 9 : 6); };
108 auto namMult = [](
int mode) {
return 1 << (mode == TEXT80 ? 12 : 10); };
109 int vdpFgCol = vdp->getForegroundColor() & 15;
110 int vdpBgCol = vdp->getBackgroundColor() & 15;
111 int vdpFgBlink = vdp->getBlinkForegroundColor() & 15;
112 int vdpBgBlink = vdp->getBlinkBackgroundColor() & 15;
113 bool vdpBlink = vdp->getBlinkState();
114 int vdpPatBase = vdp->getPatternTableBase() & ~(patMult(vdpMode) - 1);
115 int vdpColBase = vdp->getColorTableBase() & ~(colMult(vdpMode) - 1);
116 int vdpNamBase = vdp->getNameTableBase() & ~(namMult(vdpMode) - 1);
117 int vdpLines = vdp->getNumberOfLines();
118 int vdpColor0 = vdp->getTransparency() ? vdpBgCol
120 auto vramSize = std::min(vdp->getVRAM().getSize(), 0x20000u);
121 bool isMSX1 = vdp->isMSX1VDP();
123 bool manMode = overrideAll || overrideMode;
124 bool manFgCol = overrideAll || overrideFgCol;
125 bool manBgCol = overrideAll || overrideBgCol;
126 bool manFgBlink = overrideAll || overrideFgBlink;
127 bool manBgBlink = overrideAll || overrideBgBlink;
128 bool manBlink = overrideAll || overrideBlink;
129 bool manPat = overrideAll || overridePat;
130 bool manCol = overrideAll || overrideCol;
131 bool manNam = overrideAll || overrideNam;
132 bool manRows = overrideAll || overrideRows;
133 bool manColor0 = overrideAll || overrideColor0;
135 im::TreeNode(
"Settings", ImGuiTreeNodeFlags_DefaultOpen, [&]{
136 static const char*
const color0Str =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000none\000";
140 ImGui::AlignTextToFramePadding();
144 ImGui::AlignTextToFramePadding();
148 ImGui::AlignTextToFramePadding();
152 ImGui::AlignTextToFramePadding();
156 ImGui::AlignTextToFramePadding();
160 ImGui::AlignTextToFramePadding();
164 ImGui::AlignTextToFramePadding();
165 ImGui::StrCat(
"Pattern table: 0x", hex_string<5>(vdpPatBase));
168 ImGui::AlignTextToFramePadding();
172 ImGui::AlignTextToFramePadding();
176 ImGui::AlignTextToFramePadding();
177 ImGui::StrCat(
"Visible rows: ", (vdpLines == 192) ?
"24" :
"26.5");
180 ImGui::AlignTextToFramePadding();
186 ImGui::Checkbox(
"Manual override", &overrideAll);
189 ImGui::Checkbox(
"##o-mode", overrideAll ? &overrideAll : &overrideMode);
190 ImGui::Checkbox(
"##o-fgCol", overrideAll ? &overrideAll : &overrideFgCol);
191 ImGui::Checkbox(
"##o-bgCol", overrideAll ? &overrideAll : &overrideBgCol);
192 ImGui::Checkbox(
"##o-fgBlink", overrideAll ? &overrideAll : &overrideFgBlink);
193 ImGui::Checkbox(
"##o-bgBlink", overrideAll ? &overrideAll : &overrideBgBlink);
194 ImGui::Checkbox(
"##o-blink", overrideAll ? &overrideAll : &overrideBlink);
195 ImGui::Checkbox(
"##o-pat", overrideAll ? &overrideAll : &overridePat);
196 ImGui::Checkbox(
"##o-col", overrideAll ? &overrideAll : &overrideCol);
197 ImGui::Checkbox(
"##o-nam", overrideAll ? &overrideAll : &overrideNam);
198 ImGui::Checkbox(
"##o-rows", overrideAll ? &overrideAll : &overrideRows);
199 ImGui::Checkbox(
"##o-color0", overrideAll ? &overrideAll : &overrideColor0);
206 if (isMSX1 && (manualMode ==
one_of(TEXT80, SCR4))) manualMode = TEXT40;
207 im::Combo(
"##mode", modeToStr(manualMode), [&]{
208 if (ImGui::Selectable(
"screen 0,40")) manualMode = TEXT40;
209 if (!isMSX1 && ImGui::Selectable(
"screen 0,80")) manualMode = TEXT80;
210 if (ImGui::Selectable(
"screen 1")) manualMode = SCR1;
211 if (ImGui::Selectable(
"screen 2")) manualMode = SCR2;
212 if (ImGui::Selectable(
"screen 3")) manualMode = SCR3;
213 if (!isMSX1 && ImGui::Selectable(
"screen 4")) manualMode = SCR4;
216 static const char*
const range0_15 =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000";
219 ImGui::Combo(
"##fgCol", &manualFgCol, range0_15);
222 ImGui::Combo(
"##bgCol", &manualBgCol, range0_15);
227 ImGui::Combo(
"##fgBlink", &manualFgBlink, range0_15);
230 ImGui::Combo(
"##bgBlink", &manualBgBlink, range0_15);
233 ImGui::Combo(
"##blink", &manualBlink,
"disabled\000enabled\000");
237 comboHexSequence<5>(
"##pattern", &manualPatBase, patMult(manualMode), vramSize, 0);
241 comboHexSequence<5>(
"##color", &manualColBase, colMult(manualMode), vramSize, 0);
245 comboHexSequence<5>(
"##name", &manualNamBase, namMult(manualMode), vramSize, 0);
248 ImGui::Combo(
"##rows", &manualRows,
"24\00026.5\00032\000");
251 ImGui::Combo(
"##Color 0 replacement", &manualColor0, color0Str);
258 ImGui::Dummy(ImVec2(25, 1));
261 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f);
262 ImGui::Combo(
"Palette", &
manager.
palette->whichPalette,
"VDP\000Custom\000Fixed\000");
263 if (ImGui::Button(
"Open palette editor")) {
267 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 3.0f);
268 ImGui::Combo(
"Zoom", &zoom,
"1x\0002x\0003x\0004x\0005x\0006x\0007x\0008x\000");
269 ImGui::Checkbox(
"grid", &grid);
272 ImGui::ColorEdit4(
"Grid color", gridColor.data(),
273 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaBar);
275 ImGui::Checkbox(
"Name table overlay", &nameTableOverlay);
278 int manualLines = (manualRows == 0) ? 192
279 : (manualRows == 1) ? 212
282 int mode = manMode ? manualMode : vdpMode;
283 if (mode == SCR4) mode = SCR2;
285 int lines = manRows ? manualLines : vdpLines;
286 int color0 = manColor0 ? manualColor0 : vdpColor0;
289 unsigned patReg = (manPat ? (manualPatBase | (patMult(manualMode) - 1)) : vdp->getPatternTableBase()) >> 11;
293 unsigned colReg = (manCol ? (manualColBase | (colMult(manualMode) - 1)) : vdp->getColorTableBase()) >> 6;
297 unsigned namReg = (manNam ? (manualNamBase | (namMult(manualMode) - 1)) : vdp->getNameTableBase()) >> 10;
301 std::array<uint32_t, 16> palette;
304 [](uint16_t msx) { return ImGuiPalette::toRGBA(msx); });
305 if (color0 < 16) palette[0] = palette[color0];
307 auto fgCol = manFgCol ? manualFgCol : vdpFgCol;
308 auto bgCol = manBgCol ? manualBgCol : vdpBgCol;
309 auto fgBlink = manFgBlink ? manualFgBlink : vdpFgBlink;
310 auto bgBlink = manBgBlink ? manualBgBlink : vdpBgBlink;
311 auto blink = manBlink ? bool(manualBlink) : vdpBlink;
313 bool narrow = mode == TEXT80;
314 int zx = (1 + zoom) * (
narrow ? 1 : 2);
315 int zy = (1 + zoom) * 2;
319 auto patternTexSize = [&]() ->
gl::ivec2 {
320 if (mode == TEXT40)
return {192, 64};
321 if (mode == TEXT80)
return {192, 128};
322 if (mode == SCR1)
return {256, 64};
323 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
324 if (mode == SCR3)
return { 64, 64};
327 auto patternTexChars = [&]() ->
gl::vec2 {
328 if (mode == SCR3)
return {32.0f, 32.0f};
329 if (mode == SCR2)
return {32.0f, lines == 192 ? 24.0f : 32.0f};
330 if (mode == TEXT80)
return {32.0f, 16.0f};
331 return {32.0f, 8.0f};
333 auto recipPatTexChars = recip(patternTexChars);
334 auto patternDisplaySize = [&]() ->
gl::ivec2 {
335 if (mode == TEXT40)
return {192, 64};
336 if (mode == TEXT80)
return {192, 128};
337 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
338 if (mode == SCR3)
return {256, 256};
341 std::array<uint32_t, 256 * 256> pixels;
342 renderPatterns(mode, palette, fgCol, bgCol, fgBlink, bgBlink, patTable, colTable, lines, pixels);
343 if (!patternTex.
get()) {
347 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, patternTexSize.x, patternTexSize.y, 0,
348 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
351 auto charWidth = mode ==
one_of(TEXT40, TEXT80) ? 6 : 8;
352 auto charSize =
gl::vec2{float(charWidth), 8.0f};
353 auto charZoom = zm * charSize;
354 auto zoomCharSize =
gl::vec2(
narrow ? 6.0f : 12.0f, 12.0f) * charSize;
356 auto gColor = ImGui::ColorConvertFloat4ToU32(gridColor);
357 auto gridWidth = charWidth * zx;
358 auto gridHeight = 8 * zy;
359 for (
auto y :
xrange(gridHeight)) {
360 auto* line = &pixels[y * gridWidth];
361 for (
auto x :
xrange(gridWidth)) {
362 line[x] = (x == 0 || y == 0) ? gColor : 0;
365 if (!gridTex.
get()) {
369 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gridWidth, gridHeight, 0,
370 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
372 if (nameTableOverlay && !smallHexDigits.
get()) {
376 auto printPatternNr = [](
unsigned pat) {
377 ImGui::StrCat(
"Pattern: ", pat,
" (0x", hex_string<3>(pat),
')');
379 auto printAddressName = [](std::string_view name) {
382 auto printAddress = [&](std::string_view name,
unsigned address) {
383 printAddressName(name);
386 auto printAddressRange8 = [&](std::string_view name,
unsigned address) {
387 printAddressName(name);
388 ImGui::StrCat(
" 0x", hex_string<5>(address),
"-0x", hex_string<5>(address + 7));
390 auto printPatternColorAddress = [&](
unsigned pattern) {
391 printAddressRange8(
"Pattern", patTable.
getAddress(8 * pattern));
392 if (mode ==
one_of(SCR1, SCR2, SCR4)) {
393 printAddressRange8(
"Color", colTable.
getAddress((mode == SCR1) ? (pattern / 8) : (8 * pattern)));
398 im::TreeNode(
"Pattern Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
399 auto size =
gl::vec2(patternDisplaySize) * zm;
400 auto textLines = (mode == TEXT40) ? 3.0f : 5.0f;
401 auto previewHeight = zoomCharSize.y + textLines * ImGui::GetTextLineHeightWithSpacing();
402 im::Child(
"##pattern", {0, std::max(size.y, previewHeight)}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
403 auto pos1 = ImGui::GetCursorPos();
404 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
405 ImGui::Image(patternTex.
getImGui(), size);
406 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
410 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
411 auto pat = gridPos.x + 32 * gridPos.y;
413 auto uv1 =
gl::vec2(gridPos) * recipPatTexChars;
414 auto uv2 = uv1 + recipPatTexChars;
415 auto pos2 = ImGui::GetCursorPos();
416 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
418 ImGui::SetCursorPos(pos2);
420 zoomCharSize, {}, charSize);
422 printPatternColorAddress(pat);
424 ImGui::Dummy(zoomCharSize);
428 ImGui::SetCursorPos(pos1);
429 ImGui::Image(gridTex.
getImGui(), size,
430 {}, patternTexChars);
435 im::TreeNode(
"Name Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
436 float rows = float(lines) * (1.0f / 8.0f);
438 if (mode == TEXT40)
return 40;
439 if (mode == TEXT80)
return 80;
442 auto charsSize =
gl::vec2(
float(columns), rows);
443 auto msxSize = charSize * charsSize;
444 auto hostSize = msxSize * zm;
446 im::Child(
"##name", {0.0f, hostSize.y}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
447 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
448 auto* drawList = ImGui::GetWindowDrawList();
450 auto getPattern = [&](
unsigned column,
unsigned row) {
451 auto block = [&]() ->
unsigned {
452 if (mode == TEXT80 && blink) {
453 auto colPat = colTable[10 * row + (column / 8)];
454 return (colPat & (0x80 >> (column % 8))) ? 1 : 0;
456 if (mode == SCR2)
return row / 8;
457 if (mode == SCR3)
return row % 4;
460 return namTable[columns * row + column] + 256 * block;
462 auto getPatternUV = [&](
unsigned pattern) -> std::pair<gl::vec2, gl::vec2> {
463 auto patRow = pattern / 32;
464 auto patCol = pattern % 32;
466 gl::vec2 uv2 = uv1 + recipPatTexChars;
473 auto rowsCeil = int(ceilf(rows));
474 auto numChars = rowsCeil * columns;
475 drawList->PushClipRect(scrnPos, scrnPos + hostSize,
true);
476 drawList->PushTextureID(patternTex.
getImGui());
477 drawList->PrimReserve(6 * numChars, 4 * numChars);
478 for (
auto row :
xrange(rowsCeil)) {
479 for (
auto column :
xrange(columns)) {
482 auto pattern = getPattern(column, row);
483 auto [uv1, uv2] = getPatternUV(pattern);
484 drawList->PrimRectUV(p1, p2, uv1, uv2, 0xffffffff);
487 drawList->PopTextureID();
488 if (nameTableOverlay) {
489 drawList->PushTextureID(smallHexDigits.
getImGui());
490 drawList->PrimReserve(12 * numChars, 8 * numChars);
491 static constexpr gl::vec2 digitSize{5.0f, 8.0f};
492 static constexpr float texDigitWidth = 1.0f / 16.0f;
493 static constexpr gl::vec2 texDigitSize{texDigitWidth, 1.0f};
495 for (
auto row :
xrange(rowsCeil)) {
496 for (
auto column :
xrange(columns)) {
499 auto pattern = getPattern(column, row);
500 gl::vec2 uv1{narrow_cast<float>((pattern >> 4) & 15) * texDigitWidth, 0.0f};
501 gl::vec2 uv2{narrow_cast<float>((pattern >> 0) & 15) * texDigitWidth, 0.0f};
502 drawList->PrimRectUV(p1, p1 + digitSize, uv1, uv1 + texDigitSize, 0xffffffff);
503 drawList->PrimRectUV(p2, p2 + digitSize, uv2, uv2 + texDigitSize, 0xffffffff);
506 drawList->PopTextureID();
508 drawList->PopClipRect();
511 auto pos1 = ImGui::GetCursorPos();
512 ImGui::Dummy(hostSize);
513 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
517 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
519 auto pattern = getPattern(gridPos.x, gridPos.y);
520 printPatternNr(pattern);
521 auto [uv1, uv2] = getPatternUV(pattern);
522 auto pos2 = ImGui::GetCursorPos();
523 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
525 ImGui::SetCursorPos(pos2);
527 zoomCharSize, {}, charSize);
529 auto nameIndex = columns * gridPos.y + gridPos.x;
530 printAddress(
"Name", namTable.
getAddress(nameIndex));
531 printPatternColorAddress(pattern);
532 if (mode == TEXT80) {
533 printAddress(
"Color", colTable.
getAddress(nameIndex / 8));
537 ImGui::Dummy(max(zoomCharSize, textSize));
541 ImGui::SetCursorPos(pos1);
542 ImGui::Image(gridTex.
getImGui(), hostSize, {}, charsSize);