69 if (!
show || !motherBoard)
return;
70 ImGui::SetNextWindowSize({692, 886}, ImGuiCond_FirstUseEver);
72 auto* vdp =
dynamic_cast<VDP*
>(motherBoard->
findDevice(
"VDP"));
77 auto base = vdp->getDisplayMode().getBase();
86 auto modeToStr = [](
int mode) {
87 if (mode == TEXT40)
return "screen 0, width 40";
88 if (mode == TEXT80)
return "screen 0, width 80";
89 if (mode == SCR1)
return "screen 1";
90 if (mode == SCR2)
return "screen 2";
91 if (mode == SCR3)
return "screen 3";
92 if (mode == SCR4)
return "screen 4";
93 if (mode == OTHER)
return "non-character";
94 assert(
false);
return "ERROR";
96 auto patMult = [](
int mode) {
return 1 << (mode ==
one_of(SCR2, SCR4) ? 13 : 11); };
97 auto colMult = [](
int mode) {
return 1 << (mode ==
one_of(SCR2, SCR4) ? 13 :
98 mode == TEXT80 ? 9 : 6); };
99 auto namMult = [](
int mode) {
return 1 << (mode == TEXT80 ? 12 : 10); };
100 int vdpFgCol = vdp->getForegroundColor() & 15;
101 int vdpBgCol = vdp->getBackgroundColor() & 15;
102 int vdpFgBlink = vdp->getBlinkForegroundColor() & 15;
103 int vdpBgBlink = vdp->getBlinkBackgroundColor() & 15;
104 bool vdpBlink = vdp->getBlinkState();
105 int vdpPatBase = vdp->getPatternTableBase() & ~(patMult(vdpMode) - 1);
106 int vdpColBase = vdp->getColorTableBase() & ~(colMult(vdpMode) - 1);
107 int vdpNamBase = vdp->getNameTableBase() & ~(namMult(vdpMode) - 1);
108 int vdpLines = vdp->getNumberOfLines();
109 int vdpColor0 = vdp->getTransparency() ? vdpBgCol
111 auto vramSize = std::min(vdp->getVRAM().getSize(), 0x20000u);
112 bool isMSX1 = vdp->isMSX1VDP();
114 bool manMode = overrideAll || overrideMode;
115 bool manFgCol = overrideAll || overrideFgCol;
116 bool manBgCol = overrideAll || overrideBgCol;
117 bool manFgBlink = overrideAll || overrideFgBlink;
118 bool manBgBlink = overrideAll || overrideBgBlink;
119 bool manBlink = overrideAll || overrideBlink;
120 bool manPat = overrideAll || overridePat;
121 bool manCol = overrideAll || overrideCol;
122 bool manNam = overrideAll || overrideNam;
123 bool manRows = overrideAll || overrideRows;
124 bool manColor0 = overrideAll || overrideColor0;
126 im::TreeNode(
"Settings", ImGuiTreeNodeFlags_DefaultOpen, [&]{
127 static const char*
const color0Str =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000none\000";
131 ImGui::AlignTextToFramePadding();
135 ImGui::AlignTextToFramePadding();
139 ImGui::AlignTextToFramePadding();
143 ImGui::AlignTextToFramePadding();
147 ImGui::AlignTextToFramePadding();
151 ImGui::AlignTextToFramePadding();
155 ImGui::AlignTextToFramePadding();
156 ImGui::StrCat(
"Pattern table: 0x", hex_string<5>(vdpPatBase));
159 ImGui::AlignTextToFramePadding();
163 ImGui::AlignTextToFramePadding();
167 ImGui::AlignTextToFramePadding();
168 ImGui::StrCat(
"Visible rows: ", (vdpLines == 192) ?
"24" :
"26.5");
171 ImGui::AlignTextToFramePadding();
177 ImGui::Checkbox(
"Manual override", &overrideAll);
180 ImGui::Checkbox(
"##o-mode", overrideAll ? &overrideAll : &overrideMode);
181 ImGui::Checkbox(
"##o-fgCol", overrideAll ? &overrideAll : &overrideFgCol);
182 ImGui::Checkbox(
"##o-bgCol", overrideAll ? &overrideAll : &overrideBgCol);
183 ImGui::Checkbox(
"##o-fgBlink", overrideAll ? &overrideAll : &overrideFgBlink);
184 ImGui::Checkbox(
"##o-bgBlink", overrideAll ? &overrideAll : &overrideBgBlink);
185 ImGui::Checkbox(
"##o-blink", overrideAll ? &overrideAll : &overrideBlink);
186 ImGui::Checkbox(
"##o-pat", overrideAll ? &overrideAll : &overridePat);
187 ImGui::Checkbox(
"##o-col", overrideAll ? &overrideAll : &overrideCol);
188 ImGui::Checkbox(
"##o-nam", overrideAll ? &overrideAll : &overrideNam);
189 ImGui::Checkbox(
"##o-rows", overrideAll ? &overrideAll : &overrideRows);
190 ImGui::Checkbox(
"##o-color0", overrideAll ? &overrideAll : &overrideColor0);
197 if (isMSX1 && (manualMode ==
one_of(TEXT80, SCR4))) manualMode = TEXT40;
198 im::Combo(
"##mode", modeToStr(manualMode), [&]{
199 if (ImGui::Selectable(
"screen 0,40")) manualMode = TEXT40;
200 if (!isMSX1 && ImGui::Selectable(
"screen 0,80")) manualMode = TEXT80;
201 if (ImGui::Selectable(
"screen 1")) manualMode = SCR1;
202 if (ImGui::Selectable(
"screen 2")) manualMode = SCR2;
203 if (ImGui::Selectable(
"screen 3")) manualMode = SCR3;
204 if (!isMSX1 && ImGui::Selectable(
"screen 4")) manualMode = SCR4;
207 static const char*
const range0_15 =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000";
210 ImGui::Combo(
"##fgCol", &manualFgCol, range0_15);
213 ImGui::Combo(
"##bgCol", &manualBgCol, range0_15);
218 ImGui::Combo(
"##fgBlink", &manualFgBlink, range0_15);
221 ImGui::Combo(
"##bgBlink", &manualBgBlink, range0_15);
224 ImGui::Combo(
"##blink", &manualBlink,
"disabled\000enabled\000");
228 comboHexSequence<5>(
"##pattern", &manualPatBase, patMult(manualMode), vramSize, 0);
232 comboHexSequence<5>(
"##color", &manualColBase, colMult(manualMode), vramSize, 0);
236 comboHexSequence<5>(
"##name", &manualNamBase, namMult(manualMode), vramSize, 0);
239 ImGui::Combo(
"##rows", &manualRows,
"24\00026.5\00032\000");
242 ImGui::Combo(
"##Color 0 replacement", &manualColor0, color0Str);
249 ImGui::Dummy(ImVec2(25, 1));
252 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f);
253 ImGui::Combo(
"Palette", &
manager.
palette->whichPalette,
"VDP\000Custom\000Fixed\000");
254 if (ImGui::Button(
"Open palette editor")) {
258 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 3.0f);
259 ImGui::Combo(
"Zoom", &zoom,
"1x\0002x\0003x\0004x\0005x\0006x\0007x\0008x\000");
260 ImGui::Checkbox(
"grid", &grid);
263 ImGui::ColorEdit4(
"Grid color", gridColor.data(),
264 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaBar);
266 ImGui::Checkbox(
"Name table overlay", &nameTableOverlay);
269 int manualLines = (manualRows == 0) ? 192
270 : (manualRows == 1) ? 212
273 int mode = manMode ? manualMode : vdpMode;
274 if (mode == SCR4) mode = SCR2;
276 int lines = manRows ? manualLines : vdpLines;
277 int color0 = manColor0 ? manualColor0 : vdpColor0;
280 unsigned patReg = (manPat ? (manualPatBase | (patMult(manualMode) - 1)) : vdp->getPatternTableBase()) >> 11;
284 unsigned colReg = (manCol ? (manualColBase | (colMult(manualMode) - 1)) : vdp->getColorTableBase()) >> 6;
288 unsigned namReg = (manNam ? (manualNamBase | (namMult(manualMode) - 1)) : vdp->getNameTableBase()) >> 10;
292 std::array<uint32_t, 16> palette;
295 [](uint16_t msx) { return ImGuiPalette::toRGBA(msx); });
296 if (color0 < 16) palette[0] = palette[color0];
298 auto fgCol = manFgCol ? manualFgCol : vdpFgCol;
299 auto bgCol = manBgCol ? manualBgCol : vdpBgCol;
300 auto fgBlink = manFgBlink ? manualFgBlink : vdpFgBlink;
301 auto bgBlink = manBgBlink ? manualBgBlink : vdpBgBlink;
302 auto blink = manBlink ? bool(manualBlink) : vdpBlink;
304 bool narrow = mode == TEXT80;
305 int zx = (1 + zoom) * (
narrow ? 1 : 2);
306 int zy = (1 + zoom) * 2;
310 auto patternTexSize = [&]() ->
gl::ivec2 {
311 if (mode == TEXT40)
return {192, 64};
312 if (mode == TEXT80)
return {192, 128};
313 if (mode == SCR1)
return {256, 64};
314 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
315 if (mode == SCR3)
return { 64, 64};
318 auto patternTexChars = [&]() ->
gl::vec2 {
319 if (mode == SCR3)
return {32.0f, 32.0f};
320 if (mode == SCR2)
return {32.0f, lines == 192 ? 24.0f : 32.0f};
321 if (mode == TEXT80)
return {32.0f, 16.0f};
322 return {32.0f, 8.0f};
324 auto recipPatTexChars = recip(patternTexChars);
325 auto patternDisplaySize = [&]() ->
gl::ivec2 {
326 if (mode == TEXT40)
return {192, 64};
327 if (mode == TEXT80)
return {192, 128};
328 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
329 if (mode == SCR3)
return {256, 256};
332 std::array<uint32_t, 256 * 256> pixels;
333 renderPatterns(mode, palette, fgCol, bgCol, fgBlink, bgBlink, patTable, colTable, lines, pixels);
334 if (!patternTex.
get()) {
338 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, patternTexSize.x, patternTexSize.y, 0,
339 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
342 auto charWidth = mode ==
one_of(TEXT40, TEXT80) ? 6 : 8;
343 auto charSize =
gl::vec2{float(charWidth), 8.0f};
344 auto charZoom = zm * charSize;
345 auto zoomCharSize =
gl::vec2(
narrow ? 6.0f : 12.0f, 12.0f) * charSize;
347 auto gColor = ImGui::ColorConvertFloat4ToU32(gridColor);
348 auto gridWidth = charWidth * zx;
349 auto gridHeight = 8 * zy;
350 for (
auto y :
xrange(gridHeight)) {
351 auto* line = &pixels[y * gridWidth];
352 for (
auto x :
xrange(gridWidth)) {
353 line[x] = (x == 0 || y == 0) ? gColor : 0;
356 if (!gridTex.
get()) {
360 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gridWidth, gridHeight, 0,
361 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
363 if (nameTableOverlay && !smallHexDigits.
get()) {
367 auto printPatternNr = [](
unsigned pat) {
368 ImGui::StrCat(
"Pattern: ", pat,
" (0x", hex_string<3>(pat),
')');
370 auto printAddressName = [](std::string_view name) {
373 auto printAddress = [&](std::string_view name,
unsigned address) {
374 printAddressName(name);
377 auto printAddressRange8 = [&](std::string_view name,
unsigned address) {
378 printAddressName(name);
379 ImGui::StrCat(
" 0x", hex_string<5>(address),
"-0x", hex_string<5>(address + 7));
381 auto printPatternColorAddress = [&](
unsigned pattern) {
382 printAddressRange8(
"Pattern", patTable.
getAddress(8 * pattern));
383 if (mode ==
one_of(SCR1, SCR2, SCR4)) {
384 printAddressRange8(
"Color", colTable.
getAddress((mode == SCR1) ? (pattern / 8) : (8 * pattern)));
389 im::TreeNode(
"Pattern Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
390 auto size =
gl::vec2(patternDisplaySize) * zm;
391 auto textLines = (mode == TEXT40) ? 3.0f : 5.0f;
392 auto previewHeight = zoomCharSize.y + textLines * ImGui::GetTextLineHeightWithSpacing();
393 im::Child(
"##pattern", {0, std::max(size.y, previewHeight)}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
394 auto pos1 = ImGui::GetCursorPos();
395 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
396 ImGui::Image(patternTex.
getImGui(), size);
397 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
401 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
402 auto pat = gridPos.x + 32 * gridPos.y;
404 auto uv1 =
gl::vec2(gridPos) * recipPatTexChars;
405 auto uv2 = uv1 + recipPatTexChars;
406 auto pos2 = ImGui::GetCursorPos();
407 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
409 ImGui::SetCursorPos(pos2);
411 zoomCharSize, {}, charSize);
413 printPatternColorAddress(pat);
415 ImGui::Dummy(zoomCharSize);
419 ImGui::SetCursorPos(pos1);
420 ImGui::Image(gridTex.
getImGui(), size,
421 {}, patternTexChars);
426 im::TreeNode(
"Name Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
427 float rows = float(lines) * (1.0f / 8.0f);
429 if (mode == TEXT40)
return 40;
430 if (mode == TEXT80)
return 80;
433 auto charsSize =
gl::vec2(
float(columns), rows);
434 auto msxSize = charSize * charsSize;
435 auto hostSize = msxSize * zm;
437 im::Child(
"##name", {0.0f, hostSize.y}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
438 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
439 auto* drawList = ImGui::GetWindowDrawList();
441 auto getPattern = [&](
unsigned column,
unsigned row) {
442 auto block = [&]() ->
unsigned {
443 if (mode == TEXT80 && blink) {
444 auto colPat = colTable[10 * row + (column / 8)];
445 return (colPat & (0x80 >> (column % 8))) ? 1 : 0;
447 if (mode == SCR2)
return row / 8;
448 if (mode == SCR3)
return row % 4;
451 return namTable[columns * row + column] + 256 * block;
453 auto getPatternUV = [&](
unsigned pattern) -> std::pair<gl::vec2, gl::vec2> {
454 auto patRow = pattern / 32;
455 auto patCol = pattern % 32;
457 gl::vec2 uv2 = uv1 + recipPatTexChars;
464 auto rowsCeil = int(ceilf(rows));
465 auto numChars = rowsCeil * columns;
466 drawList->PushClipRect(scrnPos, scrnPos + hostSize,
true);
467 drawList->PushTextureID(patternTex.
getImGui());
468 drawList->PrimReserve(6 * numChars, 4 * numChars);
469 for (
auto row :
xrange(rowsCeil)) {
470 for (
auto column :
xrange(columns)) {
473 auto pattern = getPattern(column, row);
474 auto [uv1, uv2] = getPatternUV(pattern);
475 drawList->PrimRectUV(p1, p2, uv1, uv2, 0xffffffff);
478 drawList->PopTextureID();
479 if (nameTableOverlay) {
480 drawList->PushTextureID(smallHexDigits.
getImGui());
481 drawList->PrimReserve(12 * numChars, 8 * numChars);
482 static constexpr gl::vec2 digitSize{5.0f, 8.0f};
483 static constexpr float texDigitWidth = 1.0f / 16.0f;
484 static constexpr gl::vec2 texDigitSize{texDigitWidth, 1.0f};
486 for (
auto row :
xrange(rowsCeil)) {
487 for (
auto column :
xrange(columns)) {
490 auto pattern = getPattern(column, row);
491 gl::vec2 uv1{narrow_cast<float>((pattern >> 4) & 15) * texDigitWidth, 0.0f};
492 gl::vec2 uv2{narrow_cast<float>((pattern >> 0) & 15) * texDigitWidth, 0.0f};
493 drawList->PrimRectUV(p1, p1 + digitSize, uv1, uv1 + texDigitSize, 0xffffffff);
494 drawList->PrimRectUV(p2, p2 + digitSize, uv2, uv2 + texDigitSize, 0xffffffff);
497 drawList->PopTextureID();
499 drawList->PopClipRect();
502 auto pos1 = ImGui::GetCursorPos();
503 ImGui::Dummy(hostSize);
504 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
508 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
510 auto pattern = getPattern(gridPos.x, gridPos.y);
511 printPatternNr(pattern);
512 auto [uv1, uv2] = getPatternUV(pattern);
513 auto pos2 = ImGui::GetCursorPos();
514 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
516 ImGui::SetCursorPos(pos2);
518 zoomCharSize, {}, charSize);
520 auto nameIndex = columns * gridPos.y + gridPos.x;
521 printAddress(
"Name", namTable.
getAddress(nameIndex));
522 printPatternColorAddress(pattern);
523 if (mode == TEXT80) {
524 printAddress(
"Color", colTable.
getAddress(nameIndex / 8));
528 ImGui::Dummy(max(zoomCharSize, textSize));
532 ImGui::SetCursorPos(pos1);
533 ImGui::Image(gridTex.
getImGui(), hostSize, {}, charsSize);