79 if (!
show || !motherBoard)
return;
80 ImGui::SetNextWindowSize({692, 886}, ImGuiCond_FirstUseEver);
82 auto* vdp = dynamic_cast<VDP*>(motherBoard->findDevice(
"VDP"));
84 const auto& vram = vdp->getVRAM().getData();
87 auto base = vdp->getDisplayMode().getBase();
88 if (base == DisplayMode::TEXT1) return TEXT40;
89 if (base == DisplayMode::TEXT2) return TEXT80;
90 if (base == DisplayMode::GRAPHIC1) return SCR1;
91 if (base == DisplayMode::GRAPHIC2) return SCR2;
92 if (base == DisplayMode::GRAPHIC3) return SCR4;
93 if (base == DisplayMode::MULTICOLOR) return SCR3;
96 auto modeToStr = [](
int mode) {
97 if (mode == TEXT40)
return "screen 0, width 40";
98 if (mode == TEXT80)
return "screen 0, width 80";
99 if (mode == SCR1)
return "screen 1";
100 if (mode == SCR2)
return "screen 2";
101 if (mode == SCR3)
return "screen 3";
102 if (mode == SCR4)
return "screen 4";
103 if (mode == OTHER)
return "non-character";
104 assert(
false);
return "ERROR";
106 auto patMult = [](
int mode) {
return 1 << (mode ==
one_of(SCR2, SCR4) ? 13 : 11); };
107 auto colMult = [](
int mode) {
return 1 << (mode ==
one_of(SCR2, SCR4) ? 13 :
108 mode == TEXT80 ? 9 : 6); };
109 auto namMult = [](
int mode) {
return 1 << (mode == TEXT80 ? 12 : 10); };
110 int vdpFgCol = vdp->getForegroundColor() & 15;
111 int vdpBgCol = vdp->getBackgroundColor() & 15;
112 int vdpFgBlink = vdp->getBlinkForegroundColor() & 15;
113 int vdpBgBlink = vdp->getBlinkBackgroundColor() & 15;
114 bool vdpBlink = vdp->getBlinkState();
115 int vdpPatBase = vdp->getPatternTableBase() & ~(patMult(vdpMode) - 1);
116 int vdpColBase = vdp->getColorTableBase() & ~(colMult(vdpMode) - 1);
117 int vdpNamBase = vdp->getNameTableBase() & ~(namMult(vdpMode) - 1);
118 int vdpLines = vdp->getNumberOfLines();
119 int vdpColor0 = vdp->getTransparency() ? vdpBgCol
121 auto vramSize = std::min(vdp->getVRAM().getSize(), 0x20000u);
122 bool isMSX1 = vdp->isMSX1VDP();
124 bool manMode = overrideAll || overrideMode;
125 bool manFgCol = overrideAll || overrideFgCol;
126 bool manBgCol = overrideAll || overrideBgCol;
127 bool manFgBlink = overrideAll || overrideFgBlink;
128 bool manBgBlink = overrideAll || overrideBgBlink;
129 bool manBlink = overrideAll || overrideBlink;
130 bool manPat = overrideAll || overridePat;
131 bool manCol = overrideAll || overrideCol;
132 bool manNam = overrideAll || overrideNam;
133 bool manRows = overrideAll || overrideRows;
134 bool manColor0 = overrideAll || overrideColor0;
136 im::TreeNode(
"Settings", ImGuiTreeNodeFlags_DefaultOpen, [&]{
137 static const char*
const color0Str =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000none\000";
141 ImGui::AlignTextToFramePadding();
145 ImGui::AlignTextToFramePadding();
149 ImGui::AlignTextToFramePadding();
153 ImGui::AlignTextToFramePadding();
157 ImGui::AlignTextToFramePadding();
161 ImGui::AlignTextToFramePadding();
165 ImGui::AlignTextToFramePadding();
166 ImGui::StrCat(
"Pattern table: 0x", hex_string<5>(vdpPatBase));
169 ImGui::AlignTextToFramePadding();
173 ImGui::AlignTextToFramePadding();
177 ImGui::AlignTextToFramePadding();
178 ImGui::StrCat(
"Visible rows: ", (vdpLines == 192) ?
"24" :
"26.5");
181 ImGui::AlignTextToFramePadding();
187 ImGui::Checkbox(
"Manual override", &overrideAll);
190 ImGui::Checkbox(
"##o-mode", overrideAll ? &overrideAll : &overrideMode);
191 ImGui::Checkbox(
"##o-fgCol", overrideAll ? &overrideAll : &overrideFgCol);
192 ImGui::Checkbox(
"##o-bgCol", overrideAll ? &overrideAll : &overrideBgCol);
193 ImGui::Checkbox(
"##o-fgBlink", overrideAll ? &overrideAll : &overrideFgBlink);
194 ImGui::Checkbox(
"##o-bgBlink", overrideAll ? &overrideAll : &overrideBgBlink);
195 ImGui::Checkbox(
"##o-blink", overrideAll ? &overrideAll : &overrideBlink);
196 ImGui::Checkbox(
"##o-pat", overrideAll ? &overrideAll : &overridePat);
197 ImGui::Checkbox(
"##o-col", overrideAll ? &overrideAll : &overrideCol);
198 ImGui::Checkbox(
"##o-nam", overrideAll ? &overrideAll : &overrideNam);
199 ImGui::Checkbox(
"##o-rows", overrideAll ? &overrideAll : &overrideRows);
200 ImGui::Checkbox(
"##o-color0", overrideAll ? &overrideAll : &overrideColor0);
207 if (isMSX1 && (manualMode ==
one_of(TEXT80, SCR4))) manualMode = TEXT40;
208 im::Combo(
"##mode", modeToStr(manualMode), [&]{
209 if (ImGui::Selectable(
"screen 0,40")) manualMode = TEXT40;
210 if (!isMSX1 && ImGui::Selectable(
"screen 0,80")) manualMode = TEXT80;
211 if (ImGui::Selectable(
"screen 1")) manualMode = SCR1;
212 if (ImGui::Selectable(
"screen 2")) manualMode = SCR2;
213 if (ImGui::Selectable(
"screen 3")) manualMode = SCR3;
214 if (!isMSX1 && ImGui::Selectable(
"screen 4")) manualMode = SCR4;
217 static const char*
const range0_15 =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000";
220 ImGui::Combo(
"##fgCol", &manualFgCol, range0_15);
223 ImGui::Combo(
"##bgCol", &manualBgCol, range0_15);
228 ImGui::Combo(
"##fgBlink", &manualFgBlink, range0_15);
231 ImGui::Combo(
"##bgBlink", &manualBgBlink, range0_15);
234 ImGui::Combo(
"##blink", &manualBlink,
"disabled\000enabled\000");
238 comboHexSequence<5>(
"##pattern", &manualPatBase, patMult(manualMode), vramSize, 0);
242 comboHexSequence<5>(
"##color", &manualColBase, colMult(manualMode), vramSize, 0);
246 comboHexSequence<5>(
"##name", &manualNamBase, namMult(manualMode), vramSize, 0);
249 ImGui::Combo(
"##rows", &manualRows,
"24\00026.5\00032\000");
252 ImGui::Combo(
"##Color 0 replacement", &manualColor0, color0Str);
259 ImGui::Dummy(ImVec2(25, 1));
262 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f);
263 ImGui::Combo(
"Palette", &
manager.
palette->whichPalette,
"VDP\000Custom\000Fixed\000");
264 if (ImGui::Button(
"Open palette editor")) {
268 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 3.0f);
269 ImGui::Combo(
"Zoom", &zoom,
"1x\0002x\0003x\0004x\0005x\0006x\0007x\0008x\000");
270 ImGui::Checkbox(
"grid", &grid);
273 ImGui::ColorEdit4(
"Grid color", gridColor.data(),
274 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaBar);
276 ImGui::Checkbox(
"Name table overlay", &nameTableOverlay);
279 int manualLines = (manualRows == 0) ? 192
280 : (manualRows == 1) ? 212
283 int mode = manMode ? manualMode : vdpMode;
284 if (mode == SCR4) mode = SCR2;
286 int lines = manRows ? manualLines : vdpLines;
287 int color0 = manColor0 ? manualColor0 : vdpColor0;
290 unsigned patReg = (manPat ? (manualPatBase | (patMult(manualMode) - 1)) : vdp->getPatternTableBase()) >> 11;
294 unsigned colReg = (manCol ? (manualColBase | (colMult(manualMode) - 1)) : vdp->getColorTableBase()) >> 6;
298 unsigned namReg = (manNam ? (manualNamBase | (namMult(manualMode) - 1)) : vdp->getNameTableBase()) >> 10;
303 if (color0 < 16) palette[0] = palette[color0];
305 auto fgCol = manFgCol ? manualFgCol : vdpFgCol;
306 auto bgCol = manBgCol ? manualBgCol : vdpBgCol;
307 auto fgBlink = manFgBlink ? manualFgBlink : vdpFgBlink;
308 auto bgBlink = manBgBlink ? manualBgBlink : vdpBgBlink;
309 auto blink = manBlink ? bool(manualBlink) : vdpBlink;
311 bool narrow = mode == TEXT80;
312 int zx = (1 + zoom) * (
narrow ? 1 : 2);
313 int zy = (1 + zoom) * 2;
317 auto patternTexSize = [&]() ->
gl::ivec2 {
318 if (mode == TEXT40)
return {192, 64};
319 if (mode == TEXT80)
return {192, 128};
320 if (mode == SCR1)
return {256, 64};
321 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
322 if (mode == SCR3)
return { 64, 64};
325 auto patternTexChars = [&]() ->
gl::vec2 {
326 if (mode == SCR3)
return {32.0f, 32.0f};
327 if (mode == SCR2)
return {32.0f, lines == 192 ? 24.0f : 32.0f};
328 if (mode == TEXT80)
return {32.0f, 16.0f};
329 return {32.0f, 8.0f};
331 auto recipPatTexChars = recip(patternTexChars);
332 auto patternDisplaySize = [&]() ->
gl::ivec2 {
333 if (mode == TEXT40)
return {192, 64};
334 if (mode == TEXT80)
return {192, 128};
335 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
336 if (mode == SCR3)
return {256, 256};
339 std::array<uint32_t, 256 * 256> pixels;
340 renderPatterns(mode, palette, fgCol, bgCol, fgBlink, bgBlink, patTable, colTable, lines, pixels);
341 if (!patternTex.
get()) {
345 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, patternTexSize.x, patternTexSize.y, 0,
346 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
349 auto charWidth = mode ==
one_of(TEXT40, TEXT80) ? 6 : 8;
350 auto charSize =
gl::vec2{float(charWidth), 8.0f};
351 auto charZoom = zm * charSize;
352 auto zoomCharSize =
gl::vec2(
narrow ? 6.0f : 12.0f, 12.0f) * charSize;
354 auto gColor = ImGui::ColorConvertFloat4ToU32(gridColor);
355 auto gridWidth = charWidth * zx;
356 auto gridHeight = 8 * zy;
357 for (
auto y :
xrange(gridHeight)) {
358 auto* line = &pixels[y * gridWidth];
359 for (
auto x :
xrange(gridWidth)) {
360 line[x] = (x == 0 || y == 0) ? gColor : 0;
363 if (!gridTex.
get()) {
367 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gridWidth, gridHeight, 0,
368 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
370 if (nameTableOverlay && !smallHexDigits.
get()) {
374 auto printPatternNr = [](
unsigned pat) {
375 ImGui::StrCat(
"Pattern: ", pat,
" (0x", hex_string<3>(pat),
')');
377 auto printAddressName = [](std::string_view name) {
380 auto printAddress = [&](std::string_view name,
unsigned address) {
381 printAddressName(name);
384 auto printAddressRange8 = [&](std::string_view name,
unsigned address) {
385 printAddressName(name);
386 ImGui::StrCat(
" 0x", hex_string<5>(address),
"-0x", hex_string<5>(address + 7));
388 auto printPatternColorAddress = [&](
unsigned pattern) {
389 printAddressRange8(
"Pattern", patTable.
getAddress(8 * pattern));
390 if (mode ==
one_of(SCR1, SCR2, SCR4)) {
391 printAddressRange8(
"Color", colTable.
getAddress((mode == SCR1) ? (pattern / 8) : (8 * pattern)));
396 im::TreeNode(
"Pattern Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
397 auto size =
gl::vec2(patternDisplaySize) * zm;
398 auto textLines = (mode == TEXT40) ? 3.0f : 5.0f;
399 auto previewHeight = zoomCharSize.y + textLines * ImGui::GetTextLineHeightWithSpacing();
400 im::Child(
"##pattern", {0, std::max(size.y, previewHeight)}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
401 auto pos1 = ImGui::GetCursorPos();
402 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
403 ImGui::Image(patternTex.
getImGui(), size);
404 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
408 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
409 auto pat = gridPos.x + 32 * gridPos.y;
411 auto uv1 =
gl::vec2(gridPos) * recipPatTexChars;
412 auto uv2 = uv1 + recipPatTexChars;
413 auto pos2 = ImGui::GetCursorPos();
414 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
416 ImGui::SetCursorPos(pos2);
418 zoomCharSize, {}, charSize);
420 printPatternColorAddress(pat);
422 ImGui::Dummy(zoomCharSize);
426 ImGui::SetCursorPos(pos1);
427 ImGui::Image(gridTex.
getImGui(), size,
428 {}, patternTexChars);
433 im::TreeNode(
"Name Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
434 float rows = float(lines) * (1.0f / 8.0f);
436 if (mode == TEXT40)
return 40;
437 if (mode == TEXT80)
return 80;
440 auto charsSize =
gl::vec2(
float(columns), rows);
441 auto msxSize = charSize * charsSize;
442 auto hostSize = msxSize * zm;
444 im::Child(
"##name", {0.0f, hostSize.y}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
445 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
446 auto* drawList = ImGui::GetWindowDrawList();
448 auto getPattern = [&](
unsigned column,
unsigned row) {
449 auto block = [&]() ->
unsigned {
450 if (mode == TEXT80 && blink) {
451 auto colPat = colTable[10 * row + (column / 8)];
452 return (colPat & (0x80 >> (column % 8))) ? 1 : 0;
454 if (mode == SCR2)
return row / 8;
455 if (mode == SCR3)
return row % 4;
458 return namTable[columns * row + column] + 256 * block;
460 auto getPatternUV = [&](
unsigned pattern) -> std::pair<gl::vec2, gl::vec2> {
461 auto patRow = pattern / 32;
462 auto patCol = pattern % 32;
464 gl::vec2 uv2 = uv1 + recipPatTexChars;
471 auto rowsCeil = int(ceilf(rows));
472 auto numChars = rowsCeil * columns;
473 drawList->PushClipRect(scrnPos, scrnPos + hostSize,
true);
474 drawList->PushTextureID(patternTex.
getImGui());
475 drawList->PrimReserve(6 * numChars, 4 * numChars);
476 for (
auto row :
xrange(rowsCeil)) {
477 for (
auto column :
xrange(columns)) {
480 auto pattern = getPattern(column, row);
481 auto [uv1, uv2] = getPatternUV(pattern);
482 drawList->PrimRectUV(p1, p2, uv1, uv2, 0xffffffff);
485 drawList->PopTextureID();
486 if (nameTableOverlay) {
487 drawList->PushTextureID(smallHexDigits.
getImGui());
488 drawList->PrimReserve(12 * numChars, 8 * numChars);
489 static constexpr gl::vec2 digitSize{5.0f, 8.0f};
490 static constexpr float texDigitWidth = 1.0f / 16.0f;
491 static constexpr gl::vec2 texDigitSize{texDigitWidth, 1.0f};
493 for (
auto row :
xrange(rowsCeil)) {
494 for (
auto column :
xrange(columns)) {
497 auto pattern = getPattern(column, row);
498 gl::vec2 uv1{narrow_cast<float>((pattern >> 4) & 15) * texDigitWidth, 0.0f};
499 gl::vec2 uv2{narrow_cast<float>((pattern >> 0) & 15) * texDigitWidth, 0.0f};
500 drawList->PrimRectUV(p1, p1 + digitSize, uv1, uv1 + texDigitSize, 0xffffffff);
501 drawList->PrimRectUV(p2, p2 + digitSize, uv2, uv2 + texDigitSize, 0xffffffff);
504 drawList->PopTextureID();
506 drawList->PopClipRect();
509 auto pos1 = ImGui::GetCursorPos();
510 ImGui::Dummy(hostSize);
511 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
515 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
517 auto pattern = getPattern(gridPos.x, gridPos.y);
518 printPatternNr(pattern);
519 auto [uv1, uv2] = getPatternUV(pattern);
520 auto pos2 = ImGui::GetCursorPos();
521 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
523 ImGui::SetCursorPos(pos2);
525 zoomCharSize, {}, charSize);
527 auto nameIndex = columns * gridPos.y + gridPos.x;
528 printAddress(
"Name", namTable.
getAddress(nameIndex));
529 printPatternColorAddress(pattern);
530 if (mode == TEXT80) {
531 printAddress(
"Color", colTable.
getAddress(nameIndex / 8));
535 ImGui::Dummy(max(zoomCharSize, textSize));
539 ImGui::SetCursorPos(pos1);
540 ImGui::Image(gridTex.
getImGui(), hostSize, {}, charsSize);