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