openMSX
ImGuiVdpRegs.cc
Go to the documentation of this file.
1#include "ImGuiVdpRegs.hh"
2
3#include "ImGuiCpp.hh"
4#include "ImGuiUtils.hh"
5
6#include "VDP.hh"
7
8#include "strCat.hh"
9#include "xrange.hh"
10#include "zstring_view.hh"
11
12#include "imgui.h"
13#include "imgui_internal.h"
14
15#include <array>
16#include <bit>
17
18namespace openmsx {
19
20void ImGuiVdpRegs::save(ImGuiTextBuffer& buf)
21{
22 savePersistent(buf, *this, persistentElements);
23}
24
25void ImGuiVdpRegs::loadLine(std::string_view name, zstring_view value)
26{
27 loadOnePersistent(name, value, *this, persistentElements);
28}
29
30static const char* modeName(uint32_t v)
31{
32 // NOTE: bits M1 and M2 are swapped!!
33 static constexpr std::array<const char*, 32> modeNames = {
34 "Graphic 1 (screen 1)", // 0
35 "Multicolor (screen 3)", // 2
36 "Text 1 (screen 0, width 40)", // 1
37 "3 - invalid", // 3
38
39 "Graphic 2 (screen 2)", // 4
40 "Multicolor-Q", // 6
41 "Text 1-Q", // 5
42 "7 - invalid", // 7
43
44 "Graphic 3 (screen 4)", // 8
45 "10 - invalid", // 10
46 "Text 2 (screen 0, width 80)", // 9
47 "11 - invalid", // 11
48
49 "Graphic 4 (screen 5)", // 12
50 "14 - invalid", // 14
51 "13 - invalid", // 13
52 "15 - invalid", // 15
53
54 "Graphic 5 (screen 6)", // 16
55 "18 - invalid", // 18
56 "17 - invalid", // 17
57 "19 - invalid", // 19
58
59 "Graphic 6 (screen 7)", // 20
60 "22 - invalid", // 22
61 "21 - invalid", // 21
62 "23 - invalid", // 23
63
64 "24 - invalid", // 24
65 "26 - invalid", // 26
66 "25 - invalid", // 25
67 "27 - invalid", // 27
68
69 "Graphic 7 (screen 8)", // 28
70 "30 - invalid", // 30
71 "29 - invalid", // 29
72 "31 - invalid", // 31
73 };
74 assert(v < 32);
75 return modeNames[v];
76}
77
78using Bits = std::array<const char*, 8>;
81 const char* name;
82};
84static constexpr auto registerDescriptions = std::array{
85 RD{{" 0 ", "DG ", "IE2", "IE1", "M5 ", "M4 ", "M3 ", " 0 "}, "Mode register 0"}, // R#0
86 RD{{" 0 ", "BL ", "IE0", "M1 ", "M2 ", " 0 ", "SI ", "MAG"}, "Mode register 1"}, // R#1
87 RD{{" 0 ", "A16", "A15", "A14", "A13", "A12", "A11", "A10"}, "Pattern name table base address register"}, // R#2
88 RD{{"A13", "A12", "A11", "A10", "A9 ", "A8 ", "A7 ", "A6 "}, "Color table base address register low"}, // R#3
89 RD{{" 0 ", " 0 ", "A16", "A15", "A14", "A13", "A12", "A11"}, "Pattern generator table base address register"}, // R#4
90 RD{{"A14", "A13", "A12", "A11", "A10", "A9 ", "A8 ", "A7 "}, "Sprite attribute table base address register low"}, // R#5
91 RD{{" 0 ", " 0 ", "A16", "A15", "A14", "A13", "A12", "A11"}, "Sprite pattern generator table base address register"}, // R#6
92 RD{{"TC3", "TC2", "TC1", "TC0", "BD3", "BD2", "BD1", "BD0"}, "Text color/Back drop color register"}, // R#7
93
94 RD{{"MS ", "LP ", "TP ", "CB ", "VR ", " 0 ", "SPD", "BW "}, "Mode register 2"}, // R#8
95 RD{{"LN ", " 0 ", "S1 ", "S2 ", "IL ", "EO ", "*NT", "DC "}, "Mode register 3"}, // R#9
96 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "A16", "A15", "A14"}, "Color table base address register high"}, // R#10
97 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "A16", "A15"}, "Sprite attribute table base address register high"}, // R#11
98 RD{{"T23", "T22", "T21", "T20", "BC3", "BC2", "BC1", "BC0"}, "Text color/Back color register"}, // R#12
99 RD{{"ON3", "ON2", "ON1", "ON0", "OF3", "OF2", "OF1", "OF0"}, "Blinking period register"}, // R#13
100 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "A16", "A15", "A14"}, "VRAM Access base address register"}, // R#14
101 RD{{" 0 ", " 0 ", " 0 ", " 0 ", "S3 ", "S2 ", "S1 ", "S0 "}, "Status register pointer"}, // R#15
102
103 RD{{" 0 ", " 0 ", " 0 ", " 0 ", "C3 ", "C2 ", "C1 ", "C0 "}, "Color palette address register"}, // R#16
104 RD{{"AII", " 0 ", "RS5", "RS4", "RS3", "RS2", "RS1", "RS0"}, "Control register pointer"}, // R#17
105 RD{{"V3 ", "V2 ", "V1 ", "V0 ", "H3 ", "H2 ", "H1 ", "H0 "}, "Display adjust register"}, // R#18
106 RD{{"IL7", "IL6", "IL5", "IL4", "IL3", "IL2", "IL1", "IL0"}, "Interrupt line register"}, // R#19
107 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 "}, "Color burst register 1"}, // R#20
108 RD{{" 0 ", " 0 ", " 1 ", " 1 ", " 1 ", " 0 ", " 1 ", " 1 "}, "Color burst register 2"}, // R#21
109 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 1 ", " 0 ", " 1 "}, "Color burst register 3"}, // R#22
110 RD{{"DO7", "DO6", "DO5", "DO4", "DO3", "DO2", "DO1", "DO0"}, "Display offset register"}, // R#23
111
112 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#24
113 RD{{" 0 ", "CMD", "VDS", "YAE", "YJK", "WTE", "MSK", "SP2"}, "V9958 control register"}, // R#25
114 RD{{" 0 ", " 0 ", "HO8", "HO7", "HO6", "HO5", "HO4", "HO3"}, "Horizontal scroll register high"}, // R#26
115 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "HO2", "HO1", "HO0"}, "Horizontal scroll register low"}, // R#27
116 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#28
117 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#29
118 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#30
119 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#31
120
121 RD{{"SX7", "SX6", "SX5", "SX4", "SX3", "SX2", "SX1", "SX0"}, "Source X low register"}, // R#32
122 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "SX8"}, "Source X high register"}, // R#33
123 RD{{"SY7", "SY6", "SY5", "SY4", "SY3", "SY2", "SY1", "SY0"}, "Source Y low register"}, // R#34
124 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "SY9", "SY8"}, "Source Y high register"}, // R#35
125 RD{{"DX7", "DX6", "DX5", "DX4", "DX3", "DX2", "DX1", "DX0"}, "Destination X low register"}, // R#36
126 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "DX8"}, "Destination X high register"}, // R#37
127 RD{{"DY7", "DY6", "DY5", "DY4", "DY3", "DY2", "DY1", "DY0"}, "Destination Y low register"}, // R#38
128 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "DY9", "DY8"}, "Destination Y high register"}, // R#39
129
130 RD{{"NX7", "NX6", "NX5", "NX4", "NX3", "NX2", "NX1", "NX0"}, "Number of dots X low register"}, // R#40
131 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "NX8"}, "Number of dots X high register"}, // R#41
132 RD{{"NY7", "NY6", "NY5", "NY4", "NY3", "NY2", "NY1", "NY0"}, "Number of dots Y low register"}, // R#42
133 RD{{" 0 ", " 0 ", " 0 ", " 0 ", " 0 ", " 0 ", "NY9", "NY8"}, "Number of dots Y high register"}, // R#43
134 RD{{"CH3", "CH2", "CH1", "CH0", "CL3", "CL2", "CL1", "CL0"}, "Color register"}, // R#44
135 RD{{" 0 ", "MXC", "MXD", "MXS", "DIY", "DIX", "EQ ", "MAJ"}, "Argument register"}, // R#45
136 RD{{"CM3", "CM2", "CM1", "CM0", "LO3", "LO2", "LO1", "LO0"}, "Command register"}, // R#46
137 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#47
138
139 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#48
140 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#49
141 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#50
142 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#51
143 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#52
144 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#53
145 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#54
146 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#55
147
148 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#56
149 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#57
150 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#58
151 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#59
152 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#60
153 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#61
154 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#62
155 RD{{" - ", " - ", " - ", " - ", " - ", " - ", " - ", " - "}, ""}, // R#63
156
157 // We put info on the status registers in the same table as the normal VDP registers,
158 // (as-if they were registers R#64-R73, but that's nonsense of course)
159 RD{{" F ", "5S ", " C ", "FS4", "FS3", "FS2", "FS1", "FS0"}, "Status register 0"}, // S#0
160 RD{{"FL ", "LPS", "ID4", "ID3", "ID2", "ID1", "ID0", "FH "}, "Status register 1"}, // S#1
161 RD{{"TR ", "VR ", "HR ", "BD ", " 1 ", " 1 ", "EO ", "CE "}, "Status register 2"}, // S#2
162 RD{{"X7 ", "X6 ", "X5 ", "X4 ", "X3 ", "X2 ", "X1 ", "X0 "}, "Column register low"}, // S#3
163 RD{{" 1 ", " 1 ", " 1 ", " 1 ", " 1 ", " 1 ", " 1 ", "X8 "}, "Column register high"}, // S#4
164 RD{{"Y7 ", "Y6 ", "Y5 ", "Y4 ", "Y3 ", "Y2 ", "Y1 ", "Y0 "}, "Row register low"}, // S#5
165 RD{{" 1 ", " 1 ", " 1 ", " 1 ", " 1 ", " 1 ", "EO ", "Y8 "}, "Row register high"}, // S#6
166 RD{{"C7 ", "C6 ", "C5 ", "C4 ", "C3 ", "C2 ", "C1 ", "C0 "}, "Color register"}, // S#7
167
168 RD{{"BX7", "BX6", "BX5", "BX4", "BX3", "BX2", "BX1", "BX0"}, "Border X register low"}, // S#8
169 RD{{" 1 ", " 1 ", " 1 ", " 1 ", " 1 ", " 1 ", " 1 ", "BX8"}, "Border X register high"}, // S#9
170};
171
173 uint8_t reg = uint8_t(-1);
174 uint8_t mask = 0;
175};
177 // drawSection() draws this item when 'subs[0].reg' or 'subs[1].reg' is part of 'showRegisters'
178 // when 'subs[0].mask == 0' the item cannot be highlighted (typically such items show a fixed string)
179 std::array<SubFunction, 2> subs;
180 std::string_view toolTip;
182};
185using namespace std::literals;
186static TemporaryString noExplanation(uint32_t) { return TemporaryString{}; }
187static TemporaryString spacing(uint32_t) { return TemporaryString("\n"); }
188
189static const VDP* g_vdp; // HACK: global!!
190static std::array<uint8_t, 64 + 10> registerValues; // HACK: global!!
191static uint8_t getMode() { return uint8_t(((registerValues[1] & 0x18) >> 3) | ((registerValues[0] & 0x0E) << 1)); }
192static bool isText2Mode() { return getMode() == 0b01010; } // Note: M1 and M2 swapped!
193static bool isGraph23Mode() { return getMode() == one_of(0b00100, 0b01000); }
194static bool isSprite2Mode() { return getMode() == one_of(0b01000, 0b01100, 0b10000, 0b10100, 0b11100); }
195static bool isBitmapMode() { return getMode() >= 0x0C; }
196static bool isPlanarMode() { return (getMode() & 0x14) == 0x14; }
197
198static constexpr auto regFunctions = std::array{
199 // mode registers
200 R{{S{0, 0}}, "", [](uint32_t) { return TemporaryString("screen: "); }},
201 R{{S{1, 0x40}}, "screen display enable/disable",
202 [](uint32_t v) { return TemporaryString(v ? "enabled" : "disabled"); }},
203 R{{S{0, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
204 R{{S{1, 0x18}, {0, 0x0E}}, "display mode",
205 [](uint32_t v) { return TemporaryString(modeName(v)); }},
206 R{{S{9, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
207 R{{S{9, 0x02}}, "select PAL or NTSC",
208 [](uint32_t v) { return TemporaryString(v ? "PAL (50Hz)" : "NTSC (60Hz)"); }},
209 R{{S{0, 0}}, "", [](uint32_t) { return TemporaryString("\n"); }},
210
211 R{{S{9, 0x80}}, "select 192 or 212 lines",
212 [](uint32_t v) { return TemporaryString(v ? "212 lines" : "192 lines"); }},
213 R{{S{9, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
214 R{{S{9, 0x08}}, "interlace enable/disable",
215 [](uint32_t v) { return TemporaryString(v ? "interlaced" : "non-interlaced"); }},
216 R{{S{9, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
217 R{{S{9, 0x04}}, "alternate odd/even pages enable/disable",
218 [](uint32_t v) { return tmpStrCat(v ? "" : "do not", " alternate odd/even pages\n"); }},
219
220 R{{S{1, 0}}, "", [](uint32_t) { return TemporaryString("sprites: "); }},
221 R{{S{8, 0x02}}, "sprite enable/disable",
222 [](uint32_t v) { return TemporaryString(v ? "disabled" : "enabled"); }},
223 R{{S{8, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
224 R{{S{1, 0x02}}, "sprite size, 8x8 or 16x16",
225 [](uint32_t v) { return tmpStrCat("size=", v ? "16x16" : "8x8"); }},
226 R{{S{1, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
227 R{{S{1, 0x01}}, "sprite magnification enable/disable",
228 [](uint32_t v) { return TemporaryString(v ? "magnified\n" : "not-magnified\n"); }},
229
230 R{{S{8, 0x20}}, "color 0 transparent or not",
231 [](uint32_t v) { return tmpStrCat("color 0 is ", v ? "a regular color\n" : "transparent\n"); }},
232
233 R{{S{1, 0x20}}, "V-Blank interrupt enable/disable",
234 [](uint32_t v) { return tmpStrCat("V-Blank-IRQ: ", v ? "enabled" : "disabled"); }},
235 R{{S{1, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
236 R{{S{0, 0x10}}, "line interrupt enable/disable",
237 [](uint32_t v) { return tmpStrCat("line-IRQ: ", v ? "enabled" : "disabled", '\n'); }},
238
239 R{{S{0, 0x40}}, "digitize", &noExplanation},
240 R{{S{0, 0x20}}, "light pen interrupt enable", &noExplanation},
241 R{{S{1, 0x04}}, "fast blink mode", &noExplanation},
242 R{{S{8, 0x80}}, "mouse", &noExplanation},
243 R{{S{8, 0x40}}, "light pen", &noExplanation},
244 R{{S{8, 0x10}}, "set color bus input/output", &noExplanation},
245 R{{S{8, 0x08}}, "select VRAM type", &noExplanation},
246 R{{S{8, 0x01}}, "select black&white output", &noExplanation},
247 R{{S{9, 0x30}}, "simultaneous mode", &noExplanation},
248 R{{S{9, 0x01}}, "set DLCLK input/output", &noExplanation},
249 R{{S{0, 0}}, "", &spacing},
250
251 // Table base registers
252 R{{S{2, 0x7F}}, "name table address", [](uint32_t v) {
253 if (isText2Mode()) {
254 v &= ~0x03;
255 } else if (isBitmapMode()) {
256 v &= ~0x1F;
257 if (isPlanarMode()) {
258 v *= 2;
259 }
260 }
261 return tmpStrCat("name table: 0x", hex_string<5>(v << 10), '\n');
262 }},
263 R{{S{3, 0xFF}, S{10, 0x07}}, "color table address", [](uint32_t v) {
264 if (isText2Mode()) v &= ~0x07;
265 if (isGraph23Mode()) v &= ~0x7F;
266 return tmpStrCat("color table: 0x", hex_string<5>(v << 6), '\n');
267 }},
268 R{{S{4, 0x3F}}, "pattern table address", [](uint32_t v) {
269 if (isGraph23Mode()) v &= ~0x03;
270 return tmpStrCat("pattern table: 0x", hex_string<5>(v << 11), '\n');
271 }},
272 R{{S{5, 0xFF}, S{11, 0x03}}, "sprite attribute table address", [](uint32_t v) {
273 if (isSprite2Mode()) {
274 v &= ~0x03; // note: '3' instead of '7' because we want to display 'addr + 512'
275 }
276 return tmpStrCat("sprite attribute table: 0x", hex_string<5>(v << 7), '\n');
277 }},
278 R{{S{6, 0x3F}}, "sprite pattern table address", [](uint32_t v) {
279 return tmpStrCat("sprite pattern table: 0x", hex_string<5>(v << 11), '\n');
280 }},
281 R{{S{2, 0}}, "", &spacing},
282
283 // Color registers
284 R{{S{13, 0}}, "", [](uint32_t) { return TemporaryString("text color:"); }},
285 R{{S{7, 0xF0}}, "text color",
286 [](uint32_t v) { return tmpStrCat(" foreground=", v); }},
287 R{{S{7, 0x0F}}, "background color",
288 [](uint32_t v) { return tmpStrCat(" background=", v, '\n'); }},
289 R{{S{13, 0}}, "", [](uint32_t) { return TemporaryString("text blink color:"); }},
290 R{{S{12, 0xF0}}, "text blink color",
291 [](uint32_t v) { return tmpStrCat(" foreground=", v); }},
292 R{{S{12, 0x0F}}, "background blink color",
293 [](uint32_t v) { return tmpStrCat(" background=", v, '\n'); }},
294 R{{S{13, 0}}, "", [](uint32_t) { return TemporaryString("blink period:"); }},
295 R{{S{13, 0xF0}}, "blink period on",
296 [](uint32_t v) { return tmpStrCat(" on=", 10 * v); }},
297 R{{S{13, 0x0F}}, "blink period off",
298 [](uint32_t v) { return tmpStrCat(" off=", 10 * v); }},
299 R{{S{13, 0}}, "", [](uint32_t) { return TemporaryString(" frames\n"); }},
300 R{{S{7, 0}}, "", &spacing},
301
302 // Display registers
303 R{{S{18, 0}}, "", [](uint32_t) { return TemporaryString("set-adjust:"); }},
304 R{{S{18, 0x0F}}, "horizontal set-adjust",
305 [](uint32_t v) { return tmpStrCat(" horizontal=", int(v ^ 7) - 7); }},
306 R{{S{18, 0xF0}}, "vertical set-adjust",
307 [](uint32_t v) { return tmpStrCat(" vertical=", int(v ^ 7) - 7, '\n'); }},
308 R{{S{19, 0xFF}}, "line number for line-interrupt",
309 [](uint32_t v) { return tmpStrCat("line interrupt=", v, '\n'); }},
310 R{{S{23, 0xFF}}, "vertical scroll (line number for 1st line)",
311 [](uint32_t v) { return tmpStrCat("vertical scroll=", v, '\n'); }},
312 R{{S{18, 0}}, "", &spacing},
313
314 // Access registers
315 R{{S{14, 0x07}}, "VRAM access base address",
316 [](uint32_t v) { return tmpStrCat("VRAM access base address: ", hex_string<5>(v << 14)); }},
317 R{{S{14, 0}}, "", [](uint32_t) {
318 auto addr = (registerValues[14] << 14) | g_vdp->getVramPointer();
319 return tmpStrCat(", full address: ", hex_string<5>(addr), '\n');
320 }},
321 R{{S{15, 0x0F}}, "select status register",
322 [](uint32_t v) { return tmpStrCat("selected status register: ", v, '\n'); }},
323 R{{S{16, 0x0F}}, "select palette entry",
324 [](uint32_t v) { return tmpStrCat("selected palette entry: ", v, '\n'); }},
325 R{{S{17, 0x3F}}, "indirect register access",
326 [](uint32_t v) { return tmpStrCat("selected indirect register: ", v); }},
327 R{{S{17, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
328 R{{S{17, 0x80}}, "auto-increment",
329 [](uint32_t v) { return tmpStrCat(v ? "no " : "", "auto-increment\n"); }},
330 R{{S{14, 0}}, "", &spacing},
331
332 // V9958 registers
333 R{{S{27, 0x07}, S{26, 0x3F}}, "horizontal scroll", [](uint32_t v) {
334 auto s = (v & ~7) - (v & 7);
335 return tmpStrCat("horizontal scroll: ", s);
336 }},
337 R{{S{25, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
338 R{{S{25, 0x01}}, "2-page scroll enable/disable",
339 [](uint32_t v) { return tmpStrCat(v + 1, "-page"); }},
340 R{{S{25, 0}}, "", [](uint32_t) { return TemporaryString(", "); }},
341 R{{S{25, 0x02}}, "mask 8 left dots",
342 [](uint32_t v) { return tmpStrCat(v ? "" : "not-", "masked\n"); }},
343 R{{S{25, 0x18}}, "YJK/YAE mode",
344 [](uint32_t v) { return tmpStrCat((v & 1) ? ((v & 2) ? "YJK+YAE" : "YJK") : "RGB", "-mode\n"); }},
345 R{{S{25, 0x40}}, "expand command mode",
346 [](uint32_t v) { return tmpStrCat("commands ", (v ? "work in all " : "only work in bitmap-"), "modes\n"); }},
347 R{{S{25, 0x20}}, "output select between CPUCLK and /VDS", &noExplanation},
348 R{{S{25, 0x04}}, "enable/disable WAIT (not used on MSX)", &noExplanation},
349 R{{S{25, 0}}, "", &spacing},
350
351 // Command registers
352 R{{S{46, 0xF0}}, "command code", [](uint32_t v) {
353 static constexpr std::array<zstring_view, 16> COMMANDS = {
354 " ABRT"," ????"," ????"," ????","POINT"," PSET"," SRCH"," LINE",
355 " LMMV"," LMMM"," LMCM"," LMMC"," HMMV"," HMMM"," YMMM"," HMMC"
356 };
357 return TemporaryString(COMMANDS[v]);
358 }},
359 R{{S{46, 0}}, "", [](uint32_t) { return TemporaryString("-"); }},
360 R{{S{46, 0x0F}}, "command code", [](uint32_t v) {
361 static constexpr std::array<zstring_view, 16> OPS = {
362 "IMP ","AND ","OR ","XOR ","NOT ","NOP ","NOP ","NOP ",
363 "TIMP","TAND","TOR ","TXOR","TNOT","NOP ","NOP ","NOP "
364 };
365 return TemporaryString(OPS[v]);
366 }},
367 R{{S{32, 0}}, "", [](uint32_t) { return TemporaryString("("); }},
368 R{{S{32, 0xFF}, S{33, 0x01}}, "source X", [](uint32_t v) { return tmpStrCat(v); }},
369 R{{S{32, 0}}, "", [](uint32_t) { return TemporaryString(","); }},
370 R{{S{34, 0xFF}, S{35, 0x03}}, "source Y", [](uint32_t v) { return tmpStrCat(v); }},
371 R{{S{32, 0}}, "", [](uint32_t) { return TemporaryString(")->("); }},
372 R{{S{36, 0xFF}, S{37, 0x01}}, "destination X", [](uint32_t v) { return tmpStrCat(v); }},
373 R{{S{32, 0}}, "", [](uint32_t) { return TemporaryString(","); }},
374 R{{S{38, 0xFF}, S{39, 0x03}}, "destination Y", [](uint32_t v) { return tmpStrCat(v); }},
375 R{{S{32, 0}}, "", [](uint32_t) { return TemporaryString("),"); }},
376 R{{S{44, 0xFF}}, "color", [](uint32_t v) { return tmpStrCat(v); }},
377 R{{S{32, 0}}, "", [](uint32_t) { return TemporaryString(" ["); }},
378 R{{S{45, 0x04}}, "direction X", [](uint32_t v) { return TemporaryString(v ? "-" : "+"); }},
379 R{{S{40, 0xFF}, S{41, 0x01}}, "number of dots X", [](uint32_t v) { return tmpStrCat(v); }},
380 R{{S{32, 0}}, "", [](uint32_t) { return TemporaryString(","); }},
381 R{{S{45, 0x08}}, "direction Y", [](uint32_t v) { return TemporaryString(v ? "-" : "+"); }},
382 R{{S{42, 0xFF}, S{43, 0x03}}, "number of dots Y", [](uint32_t v) { return tmpStrCat(v); }},
383 R{{S{32, 0}}, "", [](uint32_t) { return TemporaryString("]\n"); }},
384
385 R{{S{45, 0x40}}, "expanded VRAM for CPU access", &noExplanation},
386 R{{S{45, 0x20}}, "expanded VRAM for destination", &noExplanation},
387 R{{S{45, 0x10}}, "expanded VRAM for source", &noExplanation},
388 R{{S{45, 0x02}}, "select equal/not-equal for SRCH command", &noExplanation},
389 R{{S{45, 0x01}}, "select major direction (X/Y) for LINE command", &noExplanation},
390 R{{S{32, 0}}, "", &spacing},
391
392 // Status registers
393 R{{S{64, 0x80}}, "VBLANK interrupt pending",
394 [](uint32_t v) { return tmpStrCat("VBLANK-IRQ: ", v); }},
395 R{{S{65, 0}}, "", [](uint32_t) { return TemporaryString(" "); }},
396 R{{S{65, 0x01}}, "line interrupt pending",
397 [](uint32_t v) { return tmpStrCat("line-IRQ: ", v); }},
398 R{{S{64, 0}}, "", [](uint32_t) { return TemporaryString("\n"); }},
399
400 R{{S{64, 0x40}}, "5th/9th sprite detected",
401 [](uint32_t v) { return tmpStrCat(v ? "" : "no ", "5th/9th sprite per line"); }},
402 R{{S{64, 0x1F}}, "5th/9th sprite number",
403 [](uint32_t v) { return tmpStrCat(" (sprite number=", v, ")\n"); }},
404
405 R{{S{64, 0x20}}, "sprite collision",
406 [](uint32_t v) { return tmpStrCat(v ? "" : "no ", "sprite collision"); }},
407 R{{S{67, 0}}, "", [](uint32_t) { return TemporaryString(" (at coordinate "); }},
408 R{{S{67, 0xFF}, S{68, 0x01}}, "x-coordinate",
409 [](uint32_t v) { return tmpStrCat("x=", v); }},
410 R{{S{69, 0xFF}, S{70, 0x03}}, "y-coordinate",
411 [](uint32_t v) { return tmpStrCat("y=", v); }},
412 R{{S{67, 0}}, "", [](uint32_t) { return TemporaryString(")"); }},
413 R{{S{64, 0}}, "", [](uint32_t) { return TemporaryString("\n"); }},
414
415 R{{S{66, 0}}, "", [](uint32_t) { return TemporaryString("command: "); }},
416 R{{S{66, 0x01}}, "command executing",
417 [](uint32_t v) { return tmpStrCat(v ? "" : "not ", "running"); }},
418 R{{S{66, 0}}, "", [](uint32_t) { return TemporaryString(" "); }},
419 R{{S{66, 0x80}}, "transfer ready flag",
420 [](uint32_t v) { return tmpStrCat(v ? "" : "not ", "ready to transfer\n"); }},
421
422 R{{S{66, 0}}, "", [](uint32_t) { return TemporaryString("scan position: "); }},
423 R{{S{66, 0x20}}, "horizontal border",
424 [](uint32_t v) { return tmpStrCat(v ? "" : "not ", "in horizontal border"); }},
425 R{{S{66, 0}}, "", [](uint32_t) { return TemporaryString(" "); }},
426 R{{S{66, 0x40}}, "vertical border",
427 [](uint32_t v) { return tmpStrCat(v ? "" : "not ", "in vertical border\n"); }},
428
429 R{{S{65, 0x80}}, "light pen (not used on MSX)", &noExplanation},
430 R{{S{65, 0x40}}, "light pen switch (not used on MSX)", &noExplanation},
431 R{{S{65, 0x3E}}, "VDP identification (V9938 or V9958)", &noExplanation},
432 R{{S{66, 0x10}}, "SRCH command detected border color", &noExplanation},
433 R{{S{66, 0x02}}, "odd/even field", &noExplanation},
434 R{{S{71, 0xFF}}, "result of POINT command", &noExplanation},
435 R{{S{72, 0xFF}, S{73, 0x01}}, "result of SRCH command", &noExplanation},
436};
437
438static int lookupFunction(uint8_t reg, uint8_t mask)
439{
440 int i = 0;
441 for (const auto& f : regFunctions) {
442 for (const auto& s : f.subs) {
443 if ((reg == s.reg) && (mask & s.mask)) {
444 return i;
445 }
446 }
447 ++i;
448 }
449 return -1;
450}
451
452void ImGuiVdpRegs::drawSection(std::span<const uint8_t> showRegisters, std::span<const uint8_t> regValues,
453 VDP& vdp, EmuTime::param time)
454{
455 ImGui::SameLine();
456 HelpMarker("Click to toggle bits, or edit the hex-field.\n"
457 "Right-click to show/hide register explanation.");
458
459 im::Table("table", 10, 0, [&]{
460 // isCellHovered:
461 // This uses an internal API, see here for a discussion
462 // https://github.com/ocornut/imgui/issues/6347
463 // In other words: this solution is suggested by the Dear ImGui
464 // developers until something better gets implemented.
465 auto mouse_pos = ImGui::GetMousePos();
466 const auto* table = ImGui::GetCurrentTable();
467 auto isCellHovered = [&]() {
468 return ImGui::TableGetCellBgRect(table, table->CurrentColumn).Contains(mouse_pos);
469 };
470
471 im::ID_for_range(showRegisters.size(), [&](int i) {
472 auto reg = showRegisters[i];
473 // note: 0..63 regular register
474 // 64..73 status register
475 const auto& rd = registerDescriptions[reg];
476 if (ImGui::TableNextColumn()) {
477 auto name = tmpStrCat(reg < 64 ? "R#" : "S#", reg < 64 ? reg : reg - 64);
478 ImGui::AlignTextToFramePadding();
479 ImGui::TextUnformatted(static_cast<std::string_view>(name));
480 simpleToolTip(rd.name);
481 }
482 uint8_t value = regValues[reg];
483 bool writeReg = false;
484 if (ImGui::TableNextColumn()) {
485 if (reg < 64) {
486 if (ImGui::InputScalar("##value", ImGuiDataType_U8, &value, nullptr, nullptr, "%02X", ImGuiInputTextFlags_CharsHexadecimal)) {
487 writeReg = true;
488 }
489 } else {
490 ImGui::Text("%02X", value);
491 }
492 }
493 const auto& bits = rd.bits;
494 im::ID_for_range(8, [&](int bit_) {
495 if (ImGui::TableNextColumn()) {
496 int bit = 7 - bit_;
497 auto mask = narrow<uint8_t>(1 << bit);
498 int f = lookupFunction(reg, mask);
499 if (f != -1 && f == hoveredFunction) {
500 ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,
502 }
503 im::StyleColor(ImGuiCol_Button, getColor(value & mask ? imColor::KEY_ACTIVE : imColor::KEY_NOT_ACTIVE), [&]{
504 if (ImGui::Button(bits[bit_], {40.0f, 0.0f})) {
505 value ^= mask;
506 writeReg = true;
507 }
508 });
509 if ((f != -1) && isCellHovered()) {
510 newHoveredFunction = f;
511 if (auto tip = regFunctions[f].toolTip; !tip.empty()) {
512 simpleToolTip(tip);
513 }
514 }
515 }
516 });
517 if (writeReg && (reg < 64)) {
518 vdp.changeRegister(reg, value, time);
519 }
520 });
521 });
522 if (explanation) {
523 auto* drawList = ImGui::GetWindowDrawList();
524 gl::vec2 framePadding = ImGui::GetStyle().FramePadding;
525 for (auto f : xrange(narrow<int>(regFunctions.size()))) {
526 const auto& func = regFunctions[f];
527 if (!(contains(showRegisters, func.subs[0].reg) ||
528 contains(showRegisters, func.subs[1].reg))) {
529 continue;
530 }
531 uint32_t value = 0;
532 int shift = 0;
533 for (const auto& sub : func.subs) {
534 if (sub.reg == uint8_t(-1)) break;
535 auto tmp = ((regValues[sub.reg] & sub.mask) >> std::countr_zero(sub.mask));
536 value |= tmp << shift;
537 shift += std::popcount(sub.mask);
538 }
539 if (auto s = func.displayFunc(value); !s.empty()) {
540 auto textColor = getColor(imColor::TEXT);
541 bool canHover = func.subs[0].mask != 0;
542 if (canHover && (f == hoveredFunction)) {
543 gl::vec2 textSize = ImGui::CalcTextSize(s);
544 gl::vec2 pos = ImGui::GetCursorScreenPos();
545 textColor = getColor(imColor::BLACK);
546 drawList->AddRectFilled(pos - framePadding, pos + textSize + 2.0f * framePadding, getColor(imColor::YELLOW));
547 }
548 im::StyleColor(ImGuiCol_Text, textColor, [&]{
550 });
551 if (canHover) {
552 if (ImGui::IsItemHovered()) {
553 newHoveredFunction = f;
554 }
555 if (shift == 1) { // single bit
556 if (auto reg = func.subs[0].reg; reg < 64) { // non-status-register
557 if (ImGui::IsItemClicked()) {
558 auto mask = func.subs[0].mask;
559 vdp.changeRegister(reg, regValues[reg] ^ mask, time);
560 }
561 }
562 }
563 }
564 if (!s.ends_with('\n')) ImGui::SameLine();
565 }
566 }
567 }
568}
569
570void ImGuiVdpRegs::paint(MSXMotherBoard* motherBoard)
571{
572 if (!show || !motherBoard) return;
573
574 ImGui::SetNextWindowSize(gl::vec2{41, 29} * ImGui::GetFontSize(), ImGuiCond_FirstUseEver);
575 im::Window("VDP registers", &show, [&]{
576 auto* vdp = dynamic_cast<VDP*>(motherBoard->findDevice("VDP")); // TODO name based OK?
577 if (!vdp) return;
578 auto time = motherBoard->getCurrentTime();
579 const bool tms99x8 = vdp->isMSX1VDP();
580 const bool v9958 = vdp->hasYJK();
581
582 g_vdp = vdp;
583 for (auto reg : xrange(64)) {
584 registerValues[reg] = vdp->peekRegister(reg);
585 }
586 for (auto reg : xrange(uint8_t(10))) {
587 registerValues[reg + 64] = vdp->peekStatusReg(reg, time);
588 }
589
590 newHoveredFunction = -1;
591 auto make_span = [](const auto& array) {
592 return std::span<const uint8_t, std::dynamic_extent>(std::span(array));
593 };
594 im::TreeNode("Control registers", &openControl, [&]{
595 im::TreeNode("Mode registers", &openMode, [&]{
596 static constexpr auto modeRegs1 = std::to_array<uint8_t>({0, 1});
597 static constexpr auto modeRegs2 = std::to_array<uint8_t>({0, 1, 8, 9});
598 auto modeRegs = tms99x8 ? make_span(modeRegs1) : make_span(modeRegs2);
599 drawSection(modeRegs, registerValues, *vdp, time);
600 });
601 im::TreeNode("Table base registers", &openBase, [&]{
602 static constexpr auto tableRegs1 = std::to_array<uint8_t>({2, 3, 4, 5, 6});
603 static constexpr auto tableRegs2 = std::to_array<uint8_t>({2, 3, 10, 4, 5, 11, 6});
604 auto tableRegs = tms99x8 ? make_span(tableRegs1) : make_span(tableRegs2);
605 drawSection(tableRegs, registerValues, *vdp, time);
606 });
607 im::TreeNode("Color registers", &openColor, [&]{
608 static constexpr auto colorRegs1 = std::to_array<uint8_t>({7});
609 static constexpr auto colorRegs2 = std::to_array<uint8_t>({7, 12, 13, 20, 21, 22});
610 auto colorRegs = tms99x8 ? make_span(colorRegs1) : make_span(colorRegs2);
611 drawSection(colorRegs, registerValues, *vdp, time);
612 });
613 if (!tms99x8) {
614 im::TreeNode("Display registers", &openDisplay, [&]{
615 static constexpr auto displayRegs = std::to_array<uint8_t>({18, 19, 23});
616 drawSection(displayRegs, registerValues, *vdp, time);
617 });
618 im::TreeNode("Access registers", &openAccess, [&]{
619 static constexpr auto accessRegs = std::to_array<uint8_t>({14, 15, 16, 17});
620 drawSection(accessRegs, registerValues, *vdp, time);
621 });
622 }
623 if (v9958) {
624 im::TreeNode("V9958 registers", &openV9958, [&]{
625 static constexpr auto v9958Regs = std::to_array<uint8_t>({25, 26, 27});
626 drawSection(v9958Regs, registerValues, *vdp, time);
627 });
628 }
629 });
630 if (!tms99x8) {
631 im::TreeNode("Command registers", &openCommand, [&]{
632 static constexpr auto cmdRegs = std::to_array<uint8_t>({
633 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46
634 });
635 drawSection(cmdRegs, registerValues, *vdp, time);
636 });
637 }
638 im::TreeNode("Status registers", &openStatus, [&]{
639 static constexpr auto statusRegs1 = std::to_array<uint8_t>({64});
640 static constexpr auto statusRegs2 = std::to_array<uint8_t>({
641 64, 65, 66, 67, 68, 69, 70, 71, 72, 73
642 });
643 auto statusRegs = tms99x8 ? make_span(statusRegs1) : make_span(statusRegs2);
644 drawSection(statusRegs, registerValues, *vdp, time);
645 });
646 hoveredFunction = newHoveredFunction;
647
648 if (ImGui::IsWindowHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Right)) {
649 ImGui::OpenPopup("context");
650 }
651 im::Popup("context", [&]{
652 if (ImGui::Checkbox("Show explanation ", &explanation)) {
653 ImGui::CloseCurrentPopup();
654 }
655 });
656 });
657}
658
659} // namespace openmsx
TemporaryString.
void loadLine(std::string_view name, zstring_view value) override
void save(ImGuiTextBuffer &buf) override
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
MSXDevice * findDevice(std::string_view name)
Find a MSXDevice by name.
Unified implementation of MSX Video Display Processors (VDPs).
Definition VDP.hh:67
int getVramPointer() const
Get vram pointer (14-bit) (only for debugger)
Definition VDP.hh:270
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
auto CalcTextSize(std::string_view str)
Definition ImGuiUtils.hh:39
void TextUnformatted(const std::string &str)
Definition ImGuiUtils.hh:26
void Table(const char *str_id, int column, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width, std::invocable<> auto next)
Definition ImGuiCpp.hh:455
void Window(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:63
bool TreeNode(const char *label, ImGuiTreeNodeFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:302
void StyleColor(bool active, Args &&...args)
Definition ImGuiCpp.hh:175
void Popup(const char *str_id, ImGuiWindowFlags flags, std::invocable<> auto next)
Definition ImGuiCpp.hh:391
void ID_for_range(std::integral auto count, std::invocable< int > auto next)
Definition ImGuiCpp.hh:281
This file implemented 3 utility functions:
Definition Autofire.cc:11
bool loadOnePersistent(std::string_view name, zstring_view value, C &c, const std::tuple< Elements... > &tup)
void simpleToolTip(std::string_view desc)
Definition ImGuiUtils.hh:79
void savePersistent(ImGuiTextBuffer &buf, C &c, const std::tuple< Elements... > &tup)
void HelpMarker(std::string_view desc)
Definition ImGuiUtils.cc:23
ImU32 getColor(imColor col)
RegFunction R
std::array< const char *, 8 > Bits
size_t size(std::string_view utf8)
constexpr To narrow(From from) noexcept
Definition narrow.hh:37
constexpr auto make_span(Range &&range)
Definition ranges.hh:462
constexpr bool contains(ITER first, ITER last, const VAL &val)
Check if a range contains a given value, using linear search.
Definition stl.hh:35
TemporaryString tmpStrCat(Ts &&... ts)
Definition strCat.hh:742
Definition stl_test.cc:7
std::array< SubFunction, 2 > subs
std::string_view toolTip
TemporaryString(* displayFunc)(uint32_t)
constexpr auto xrange(T e)
Definition xrange.hh:132