69 if (!
show || !motherBoard)
return;
70 ImGui::SetNextWindowSize({686, 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
112 bool manMode = overrideAll || overrideMode;
113 bool manFgCol = overrideAll || overrideFgCol;
114 bool manBgCol = overrideAll || overrideBgCol;
115 bool manFgBlink = overrideAll || overrideFgBlink;
116 bool manBgBlink = overrideAll || overrideBgBlink;
117 bool manBlink = overrideAll || overrideBlink;
118 bool manPat = overrideAll || overridePat;
119 bool manCol = overrideAll || overrideCol;
120 bool manNam = overrideAll || overrideNam;
121 bool manRows = overrideAll || overrideRows;
122 bool manColor0 = overrideAll || overrideColor0;
124 im::TreeNode(
"Settings", ImGuiTreeNodeFlags_DefaultOpen, [&]{
125 static const char*
const color0Str =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000none\000";
129 ImGui::AlignTextToFramePadding();
133 ImGui::AlignTextToFramePadding();
137 ImGui::AlignTextToFramePadding();
141 ImGui::AlignTextToFramePadding();
145 ImGui::AlignTextToFramePadding();
149 ImGui::AlignTextToFramePadding();
153 ImGui::AlignTextToFramePadding();
154 ImGui::StrCat(
"Pattern table: 0x", hex_string<5>(vdpPatBase));
157 ImGui::AlignTextToFramePadding();
161 ImGui::AlignTextToFramePadding();
165 ImGui::AlignTextToFramePadding();
166 ImGui::StrCat(
"Visible rows: ", (vdpLines == 192) ?
"24" :
"26.5");
169 ImGui::AlignTextToFramePadding();
175 ImGui::Checkbox(
"Manual override", &overrideAll);
178 ImGui::Checkbox(
"##o-mode", overrideAll ? &overrideAll : &overrideMode);
179 ImGui::Checkbox(
"##o-fgCol", overrideAll ? &overrideAll : &overrideFgCol);
180 ImGui::Checkbox(
"##o-bgCol", overrideAll ? &overrideAll : &overrideBgCol);
181 ImGui::Checkbox(
"##o-fgBlink", overrideAll ? &overrideAll : &overrideFgBlink);
182 ImGui::Checkbox(
"##o-bgBlink", overrideAll ? &overrideAll : &overrideBgBlink);
183 ImGui::Checkbox(
"##o-blink", overrideAll ? &overrideAll : &overrideBlink);
184 ImGui::Checkbox(
"##o-pat", overrideAll ? &overrideAll : &overridePat);
185 ImGui::Checkbox(
"##o-col", overrideAll ? &overrideAll : &overrideCol);
186 ImGui::Checkbox(
"##o-nam", overrideAll ? &overrideAll : &overrideNam);
187 ImGui::Checkbox(
"##o-rows", overrideAll ? &overrideAll : &overrideRows);
188 ImGui::Checkbox(
"##o-color0", overrideAll ? &overrideAll : &overrideColor0);
195 ImGui::Combo(
"##mode", &manualMode,
"screen 0,40\000screen 0,80\000screen 1\000screen 2\000screen 3\000screen 4\000");
197 static const char*
const range0_15 =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000";
200 ImGui::Combo(
"##fgCol", &manualFgCol, range0_15);
203 ImGui::Combo(
"##bgCol", &manualBgCol, range0_15);
208 ImGui::Combo(
"##fgBlink", &manualFgBlink, range0_15);
211 ImGui::Combo(
"##bgBlink", &manualBgBlink, range0_15);
214 ImGui::Combo(
"##blink", &manualBlink,
"disabled\000enabled\000");
218 comboHexSequence<5>(
"##pattern", &manualPatBase, patMult(manualMode));
222 comboHexSequence<5>(
"##color", &manualColBase, colMult(manualMode));
226 comboHexSequence<5>(
"##name", &manualNamBase, namMult(manualMode));
229 ImGui::Combo(
"##rows", &manualRows,
"24\00026.5\00032\000");
232 ImGui::Combo(
"##Color 0 replacement", &manualColor0, color0Str);
239 ImGui::Dummy(ImVec2(25, 1));
242 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f);
243 ImGui::Combo(
"Palette", &
manager.
palette->whichPalette,
"VDP\000Custom\000Fixed\000");
244 if (ImGui::Button(
"Open palette editor")) {
248 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 3.0f);
249 ImGui::Combo(
"Zoom", &zoom,
"1x\0002x\0003x\0004x\0005x\0006x\0007x\0008x\000");
250 ImGui::Checkbox(
"grid", &grid);
253 ImGui::ColorEdit4(
"Grid color", gridColor.data(),
254 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaBar);
256 ImGui::Checkbox(
"Name table overlay", &nameTableOverlay);
259 int manualLines = (manualRows == 0) ? 192
260 : (manualRows == 1) ? 212
263 int mode = manMode ? manualMode : vdpMode;
264 if (mode == SCR4) mode = SCR2;
266 int lines = manRows ? manualLines : vdpLines;
267 int color0 = manColor0 ? manualColor0 : vdpColor0;
270 unsigned patReg = (manPat ? (manualPatBase | (patMult(manualMode) - 1)) : vdp->getPatternTableBase()) >> 11;
274 unsigned colReg = (manCol ? (manualColBase | (colMult(manualMode) - 1)) : vdp->getColorTableBase()) >> 6;
278 unsigned namReg = (manNam ? (manualNamBase | (namMult(manualMode) - 1)) : vdp->getNameTableBase()) >> 10;
282 std::array<uint32_t, 16> palette;
285 [](uint16_t msx) { return ImGuiPalette::toRGBA(msx); });
286 if (color0 < 16) palette[0] = palette[color0];
288 auto fgCol = manFgCol ? manualFgCol : vdpFgCol;
289 auto bgCol = manBgCol ? manualBgCol : vdpBgCol;
290 auto fgBlink = manFgBlink ? manualFgBlink : vdpFgBlink;
291 auto bgBlink = manBgBlink ? manualBgBlink : vdpBgBlink;
292 auto blink = manBlink ? bool(manualBlink) : vdpBlink;
294 bool narrow = mode == TEXT80;
295 int zx = (1 + zoom) * (
narrow ? 1 : 2);
296 int zy = (1 + zoom) * 2;
300 auto patternTexSize = [&]() ->
gl::ivec2 {
301 if (mode == TEXT40)
return {192, 64};
302 if (mode == TEXT80)
return {192, 128};
303 if (mode == SCR1)
return {256, 64};
304 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
305 if (mode == SCR3)
return { 64, 64};
308 auto patternTexChars = [&]() ->
gl::vec2 {
309 if (mode == SCR3)
return {32.0f, 32.0f};
310 if (mode == SCR2)
return {32.0f, lines == 192 ? 24.0f : 32.0f};
311 if (mode == TEXT80)
return {32.0f, 16.0f};
312 return {32.0f, 8.0f};
314 auto recipPatTexChars = recip(patternTexChars);
315 auto patternDisplaySize = [&]() ->
gl::ivec2 {
316 if (mode == TEXT40)
return {192, 64};
317 if (mode == TEXT80)
return {192, 128};
318 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
319 if (mode == SCR3)
return {256, 256};
322 std::array<uint32_t, 256 * 256> pixels;
323 renderPatterns(mode, palette, fgCol, bgCol, fgBlink, bgBlink, patTable, colTable, lines, pixels);
324 if (!patternTex.
get()) {
328 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, patternTexSize.x, patternTexSize.y, 0,
329 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
332 auto charWidth = mode ==
one_of(TEXT40, TEXT80) ? 6 : 8;
333 auto charSize =
gl::vec2{float(charWidth), 8.0f};
334 auto charZoom = zm * charSize;
335 auto zoomCharSize =
gl::vec2(
narrow ? 6.0f : 12.0f, 12.0f) * charSize;
337 auto gColor = ImGui::ColorConvertFloat4ToU32(gridColor);
338 auto gridWidth = charWidth * zx;
339 auto gridHeight = 8 * zy;
340 for (
auto y :
xrange(gridHeight)) {
341 auto* line = &pixels[y * gridWidth];
342 for (
auto x :
xrange(gridWidth)) {
343 line[x] = (x == 0 || y == 0) ? gColor : 0;
346 if (!gridTex.
get()) {
350 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gridWidth, gridHeight, 0,
351 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
353 if (nameTableOverlay && !smallHexDigits.
get()) {
358 im::TreeNode(
"Pattern Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
359 auto size =
gl::vec2(patternDisplaySize) * zm;
360 im::Child(
"##pattern", {0, size.y}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
361 auto pos1 = ImGui::GetCursorPos();
362 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
363 ImGui::Image(patternTex.
getImGui(), size);
364 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
368 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
370 auto uv1 =
gl::vec2(gridPos) * recipPatTexChars;
371 auto uv2 = uv1 + recipPatTexChars;
372 auto pos2 = ImGui::GetCursorPos();
373 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
375 ImGui::SetCursorPos(pos2);
377 zoomCharSize, {}, charSize);
380 ImGui::Dummy(zoomCharSize);
384 ImGui::SetCursorPos(pos1);
385 ImGui::Image(gridTex.
getImGui(), size,
386 {}, patternTexChars);
391 im::TreeNode(
"Name Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
392 float rows = float(lines) * (1.0f / 8.0f);
394 if (mode == TEXT40)
return 40;
395 if (mode == TEXT80)
return 80;
398 auto charsSize =
gl::vec2(
float(columns), rows);
399 auto msxSize = charSize * charsSize;
400 auto hostSize = msxSize * zm;
402 im::Child(
"##name", {0.0f, hostSize.y}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
403 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
404 auto* drawList = ImGui::GetWindowDrawList();
406 auto getPattern = [&](
unsigned column,
unsigned row) {
407 auto block = [&]() ->
unsigned {
408 if (mode == TEXT80 && blink) {
409 auto colPat = colTable[10 * row + (column / 8)];
410 return (colPat & (0x80 >> (column % 8))) ? 1 : 0;
412 if (mode == SCR2)
return row / 8;
413 if (mode == SCR3)
return row % 4;
416 return namTable[columns * row + column] + 256 * block;
418 auto getPatternUV = [&](
unsigned pattern) -> std::pair<gl::vec2, gl::vec2> {
419 auto patRow = pattern / 32;
420 auto patCol = pattern % 32;
422 gl::vec2 uv2 = uv1 + recipPatTexChars;
429 auto rowsCeil = int(ceilf(rows));
430 auto numChars = rowsCeil * columns;
431 drawList->PushClipRect(scrnPos, scrnPos + hostSize,
true);
432 drawList->PushTextureID(patternTex.
getImGui());
433 drawList->PrimReserve(6 * numChars, 4 * numChars);
434 for (
auto row :
xrange(rowsCeil)) {
435 for (
auto column :
xrange(columns)) {
438 auto pattern = getPattern(column, row);
439 auto [uv1, uv2] = getPatternUV(pattern);
440 drawList->PrimRectUV(p1, p2, uv1, uv2, 0xffffffff);
443 drawList->PopTextureID();
444 if (nameTableOverlay) {
445 drawList->PushTextureID(smallHexDigits.
getImGui());
446 drawList->PrimReserve(12 * numChars, 8 * numChars);
447 static constexpr gl::vec2 digitSize{5.0f, 8.0f};
448 static constexpr float texDigitWidth = 1.0f / 16.0f;
449 static constexpr gl::vec2 texDigitSize{texDigitWidth, 1.0f};
451 for (
auto row :
xrange(rowsCeil)) {
452 for (
auto column :
xrange(columns)) {
455 auto pattern = getPattern(column, row);
456 gl::vec2 uv1{narrow_cast<float>((pattern >> 4) & 15) * texDigitWidth, 0.0f};
457 gl::vec2 uv2{narrow_cast<float>((pattern >> 0) & 15) * texDigitWidth, 0.0f};
458 drawList->PrimRectUV(p1, p1 + digitSize, uv1, uv1 + texDigitSize, 0xffffffff);
459 drawList->PrimRectUV(p2, p2 + digitSize, uv2, uv2 + texDigitSize, 0xffffffff);
462 drawList->PopTextureID();
464 drawList->PopClipRect();
467 auto pos1 = ImGui::GetCursorPos();
468 ImGui::Dummy(hostSize);
469 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
473 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
475 auto pattern = getPattern(gridPos.x, gridPos.y);
477 auto [uv1, uv2] = getPatternUV(pattern);
478 auto pos2 = ImGui::GetCursorPos();
479 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
481 ImGui::SetCursorPos(pos2);
483 zoomCharSize, {}, charSize);
487 ImGui::Dummy(max(zoomCharSize, textSize));
491 ImGui::SetCursorPos(pos1);
492 ImGui::Image(gridTex.
getImGui(), hostSize, {}, charsSize);