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 im::TreeNode(
"Settings", ImGuiTreeNodeFlags_DefaultOpen, [&]{
113 static const char*
const color0Str =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000none\000";
115 ImGui::RadioButton(
"Use VDP settings", &manual, 0);
117 ImGui::AlignTextToFramePadding();
119 ImGui::AlignTextToFramePadding();
121 ImGui::AlignTextToFramePadding();
123 ImGui::AlignTextToFramePadding();
125 ImGui::AlignTextToFramePadding();
127 ImGui::AlignTextToFramePadding();
129 ImGui::AlignTextToFramePadding();
130 ImGui::StrCat(
"Pattern table: 0x", hex_string<5>(vdpPatBase));
131 ImGui::AlignTextToFramePadding();
133 ImGui::AlignTextToFramePadding();
135 ImGui::AlignTextToFramePadding();
136 ImGui::StrCat(
"Visible rows: ", (vdpLines == 192) ?
"24" :
"26.5");
137 ImGui::AlignTextToFramePadding();
143 ImGui::RadioButton(
"Manual override", &manual, 1);
146 ImGui::Combo(
"##mode", &manualMode,
"screen 0,40\000screen 0,80\000screen 1\000screen 2\000screen 3\000screen 4\000");
147 static const char*
const range0_15 =
"0\0001\0002\0003\0004\0005\0006\0007\0008\0009\00010\00011\00012\00013\00014\00015\000";
149 ImGui::Combo(
"##fgCol", &manualFgCol, range0_15);
150 ImGui::Combo(
"##bgCol", &manualBgCol, range0_15);
153 ImGui::Combo(
"##fgBlink", &manualFgBlink, range0_15);
154 ImGui::Combo(
"##bgBlink", &manualBgBlink, range0_15);
155 ImGui::Combo(
"##blink", &manualBlink,
"disabled\000enabled\000");
157 comboHexSequence<5>(
"##pattern", &manualPatBase, patMult(manualMode));
159 comboHexSequence<5>(
"##color", &manualColBase, colMult(manualMode));
161 comboHexSequence<5>(
"##name", &manualNamBase, namMult(manualMode));
162 ImGui::Combo(
"##rows", &manualRows,
"24\00026.5\00032\000");
163 ImGui::Combo(
"##Color 0 replacement", &manualColor0, color0Str);
169 ImGui::Dummy(ImVec2(25, 1));
172 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 10.0f);
173 ImGui::Combo(
"Palette", &
manager.
palette->whichPalette,
"VDP\000Custom\000Fixed\000");
174 if (ImGui::Button(
"Open palette editor")) {
178 ImGui::SetNextItemWidth(ImGui::GetFontSize() * 3.0f);
179 ImGui::Combo(
"Zoom", &zoom,
"1x\0002x\0003x\0004x\0005x\0006x\0007x\0008x\000");
180 ImGui::Checkbox(
"grid", &grid);
183 ImGui::ColorEdit4(
"Grid color", gridColor.data(),
184 ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaBar);
186 ImGui::Checkbox(
"Name table overlay", &nameTableOverlay);
189 int manualLines = (manualRows == 0) ? 192
190 : (manualRows == 1) ? 212
193 int mode = manual ? manualMode : vdpMode;
194 if (mode == SCR4) mode = SCR2;
196 int lines = manual ? manualLines : vdpLines;
197 int color0 = manual ? manualColor0 : vdpColor0;
200 unsigned patReg = (manual ? manualPatBase : vdp->getPatternTableBase()) >> 11;
204 unsigned colReg = (manual ? manualColBase : vdp->getColorTableBase()) >> 6;
208 unsigned namReg = (manual ? manualNamBase : vdp->getNameTableBase()) >> 10;
212 std::array<uint32_t, 16> palette;
215 [](uint16_t msx) { return ImGuiPalette::toRGBA(msx); });
216 if (color0 < 16) palette[0] = palette[color0];
218 auto fgCol = manual ? manualFgCol : vdpFgCol;
219 auto bgCol = manual ? manualBgCol : vdpBgCol;
220 auto fgBlink = manual ? manualFgBlink : vdpFgBlink;
221 auto bgBlink = manual ? manualBgBlink : vdpBgBlink;
222 auto blink = manual ? bool(manualBlink) : vdpBlink;
224 bool narrow = mode == TEXT80;
225 int zx = (1 + zoom) * (
narrow ? 1 : 2);
226 int zy = (1 + zoom) * 2;
230 auto patternTexSize = [&]() ->
gl::ivec2 {
231 if (mode == TEXT40)
return {192, 64};
232 if (mode == TEXT80)
return {192, 128};
233 if (mode == SCR1)
return {256, 64};
234 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
235 if (mode == SCR3)
return { 64, 64};
238 auto patternTexChars = [&]() ->
gl::vec2 {
239 if (mode == SCR3)
return {32.0f, 32.0f};
240 if (mode == SCR2)
return {32.0f, lines == 192 ? 24.0f : 32.0f};
241 if (mode == TEXT80)
return {32.0f, 16.0f};
242 return {32.0f, 8.0f};
244 auto recipPatTexChars = recip(patternTexChars);
245 auto patternDisplaySize = [&]() ->
gl::ivec2 {
246 if (mode == TEXT40)
return {192, 64};
247 if (mode == TEXT80)
return {192, 128};
248 if (mode == SCR2)
return {256, lines == 192 ? 192 : 256};
249 if (mode == SCR3)
return {256, 256};
252 std::array<uint32_t, 256 * 256> pixels;
253 renderPatterns(mode, palette, fgCol, bgCol, fgBlink, bgBlink, patTable, colTable, lines, pixels);
254 if (!patternTex.
get()) {
258 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, patternTexSize.x, patternTexSize.y, 0,
259 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
262 auto charWidth = mode ==
one_of(TEXT40, TEXT80) ? 6 : 8;
263 auto charSize =
gl::vec2{float(charWidth), 8.0f};
264 auto charZoom = zm * charSize;
265 auto zoomCharSize =
gl::vec2(
narrow ? 6.0f : 12.0f, 12.0f) * charSize;
267 auto gColor = ImGui::ColorConvertFloat4ToU32(gridColor);
268 auto gridWidth = charWidth * zx;
269 auto gridHeight = 8 * zy;
270 for (
auto y :
xrange(gridHeight)) {
271 auto* line = &pixels[y * gridWidth];
272 for (
auto x :
xrange(gridWidth)) {
273 line[x] = (x == 0 || y == 0) ? gColor : 0;
276 if (!gridTex.
get()) {
280 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gridWidth, gridHeight, 0,
281 GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
283 if (nameTableOverlay && !smallHexDigits.
get()) {
288 im::TreeNode(
"Pattern Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
289 auto size =
gl::vec2(patternDisplaySize) * zm;
290 im::Child(
"##pattern", {0, size.y}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
291 auto pos1 = ImGui::GetCursorPos();
292 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
293 ImGui::Image(patternTex.
getImGui(), size);
294 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
298 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
300 auto uv1 =
gl::vec2(gridPos) * recipPatTexChars;
301 auto uv2 = uv1 + recipPatTexChars;
302 auto pos2 = ImGui::GetCursorPos();
303 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
305 ImGui::SetCursorPos(pos2);
307 zoomCharSize, {}, charSize);
310 ImGui::Dummy(zoomCharSize);
314 ImGui::SetCursorPos(pos1);
315 ImGui::Image(gridTex.
getImGui(), size,
316 {}, patternTexChars);
321 im::TreeNode(
"Name Table", ImGuiTreeNodeFlags_DefaultOpen, [&]{
322 float rows = float(lines) * (1.0f / 8.0f);
324 if (mode == TEXT40)
return 40;
325 if (mode == TEXT80)
return 80;
328 auto charsSize =
gl::vec2(
float(columns), rows);
329 auto msxSize = charSize * charsSize;
330 auto hostSize = msxSize * zm;
332 im::Child(
"##name", {0.0f, hostSize.y}, 0, ImGuiWindowFlags_HorizontalScrollbar, [&]{
333 gl::vec2 scrnPos = ImGui::GetCursorScreenPos();
334 auto* drawList = ImGui::GetWindowDrawList();
336 auto getPattern = [&](
unsigned column,
unsigned row) {
337 auto block = [&]() ->
unsigned {
338 if (mode == TEXT80 && blink) {
339 auto colPat = colTable[10 * row + (column / 8)];
340 return (colPat & (0x80 >> (column % 8))) ? 1 : 0;
342 if (mode == SCR2)
return row / 8;
343 if (mode == SCR3)
return row % 4;
346 return namTable[columns * row + column] + 256 * block;
348 auto getPatternUV = [&](
unsigned pattern) -> std::pair<gl::vec2, gl::vec2> {
349 auto patRow = pattern / 32;
350 auto patCol = pattern % 32;
352 gl::vec2 uv2 = uv1 + recipPatTexChars;
359 auto rowsCeil = int(ceilf(rows));
360 auto numChars = rowsCeil * columns;
361 drawList->PushClipRect(scrnPos, scrnPos + hostSize,
true);
362 drawList->PushTextureID(patternTex.
getImGui());
363 drawList->PrimReserve(6 * numChars, 4 * numChars);
364 for (
auto row :
xrange(rowsCeil)) {
365 for (
auto column :
xrange(columns)) {
368 auto pattern = getPattern(column, row);
369 auto [uv1, uv2] = getPatternUV(pattern);
370 drawList->PrimRectUV(p1, p2, uv1, uv2, 0xffffffff);
373 drawList->PopTextureID();
374 if (nameTableOverlay) {
375 drawList->PushTextureID(smallHexDigits.
getImGui());
376 drawList->PrimReserve(12 * numChars, 8 * numChars);
377 static constexpr gl::vec2 digitSize{5.0f, 8.0f};
378 static constexpr float texDigitWidth = 1.0f / 16.0f;
379 static constexpr gl::vec2 texDigitSize{texDigitWidth, 1.0f};
381 for (
auto row :
xrange(rowsCeil)) {
382 for (
auto column :
xrange(columns)) {
385 auto pattern = getPattern(column, row);
386 gl::vec2 uv1{narrow_cast<float>((pattern >> 4) & 15) * texDigitWidth, 0.0f};
387 gl::vec2 uv2{narrow_cast<float>((pattern >> 0) & 15) * texDigitWidth, 0.0f};
388 drawList->PrimRectUV(p1, p1 + digitSize, uv1, uv1 + texDigitSize, 0xffffffff);
389 drawList->PrimRectUV(p2, p2 + digitSize, uv2, uv2 + texDigitSize, 0xffffffff);
392 drawList->PopTextureID();
394 drawList->PopClipRect();
397 auto pos1 = ImGui::GetCursorPos();
398 ImGui::Dummy(hostSize);
399 bool hovered = ImGui::IsItemHovered() && (mode != OTHER);
403 auto gridPos = trunc((
gl::vec2(ImGui::GetIO().MousePos) - scrnPos) / charZoom);
405 auto pattern = getPattern(gridPos.x, gridPos.y);
407 auto [uv1, uv2] = getPatternUV(pattern);
408 auto pos2 = ImGui::GetCursorPos();
409 ImGui::Image(patternTex.
getImGui(), zoomCharSize, uv1, uv2);
411 ImGui::SetCursorPos(pos2);
413 zoomCharSize, {}, charSize);
417 ImGui::Dummy(max(zoomCharSize, textSize));
421 ImGui::SetCursorPos(pos1);
422 ImGui::Image(gridTex.
getImGui(), hostSize, {}, charsSize);