src/platform/qt/IOViewer.cpp (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "IOViewer.h"
7
8#include "CoreController.h"
9#include "GBAApp.h"
10
11#include <QComboBox>
12#include <QGridLayout>
13#include <QSpinBox>
14
15#ifdef M_CORE_GBA
16#include <mgba/internal/gba/io.h>
17#include <mgba/internal/gba/memory.h>
18#endif
19
20#ifdef M_CORE_GB
21#include <mgba/internal/gb/io.h>
22#include <mgba/internal/gb/memory.h>
23#endif
24
25struct ARMCore;
26
27using namespace QGBA;
28
29QHash<mPlatform, QList<IOViewer::RegisterDescription>> IOViewer::s_registers;
30
31const QList<IOViewer::RegisterDescription>& IOViewer::registerDescriptions(mPlatform platform) {
32 if (!s_registers.isEmpty()) {
33 return s_registers[platform];
34 }
35#ifdef M_CORE_GBA
36 QList<IOViewer::RegisterDescription> regGBA;
37 // 0x04000000: DISPCNT
38 regGBA.append({
39 { tr("Background mode"), 0, 3, {
40 tr("Mode 0: 4 tile layers"),
41 tr("Mode 1: 2 tile layers + 1 rotated/scaled tile layer"),
42 tr("Mode 2: 2 rotated/scaled tile layers"),
43 tr("Mode 3: Full 15-bit bitmap"),
44 tr("Mode 4: Full 8-bit bitmap"),
45 tr("Mode 5: Small 15-bit bitmap"),
46 QString(),
47 QString()
48 } },
49 { tr("CGB Mode"), 3, 1, true },
50 { tr("Frame select"), 4 },
51 { tr("Unlocked HBlank"), 5 },
52 { tr("Linear OBJ tile mapping"), 6 },
53 { tr("Force blank screen"), 7 },
54 { tr("Enable background 0"), 8 },
55 { tr("Enable background 1"), 9 },
56 { tr("Enable background 2"), 10 },
57 { tr("Enable background 3"), 11 },
58 { tr("Enable OBJ"), 12 },
59 { tr("Enable Window 0"), 13 },
60 { tr("Enable Window 1"), 14 },
61 { tr("Enable OBJ Window"), 15 },
62 });
63 // 0x04000002: Green swap
64 regGBA.append({
65 { tr("Swap green components"), 0 },
66 });
67 // 0x04000004: DISPSTAT
68 regGBA.append({
69 { tr("Currently in VBlank"), 0, 1, true },
70 { tr("Currently in HBlank"), 1, 1, true },
71 { tr("Currently in VCounter"), 2, 1, true },
72 { tr("Enable VBlank IRQ generation"), 3 },
73 { tr("Enable HBlank IRQ generation"), 4 },
74 { tr("Enable VCounter IRQ generation"), 5 },
75 { tr("VCounter scanline"), 8, 8 },
76 });
77 // 0x04000006: VCOUNT
78 regGBA.append({
79 { tr("Current scanline"), 0, 8, true },
80 });
81 // 0x04000008: BG0CNT
82 regGBA.append({
83 { tr("Priority"), 0, 2 },
84 { tr("Tile data base (* 16kB)"), 2, 2 },
85 { tr("Enable mosaic"), 6 },
86 { tr("Enable 256-color"), 7 },
87 { tr("Tile map base (* 2kB)"), 8, 5 },
88 { tr("Background dimensions"), 14, 2 },
89 });
90 // 0x0400000A: BG1CNT
91 regGBA.append({
92 { tr("Priority"), 0, 2 },
93 { tr("Tile data base (* 16kB)"), 2, 2 },
94 { tr("Enable mosaic"), 6 },
95 { tr("Enable 256-color"), 7 },
96 { tr("Tile map base (* 2kB)"), 8, 5 },
97 { tr("Background dimensions"), 14, 2 },
98 });
99 // 0x0400000C: BG2CNT
100 regGBA.append({
101 { tr("Priority"), 0, 2 },
102 { tr("Tile data base (* 16kB)"), 2, 2 },
103 { tr("Enable mosaic"), 6 },
104 { tr("Enable 256-color"), 7 },
105 { tr("Tile map base (* 2kB)"), 8, 5 },
106 { tr("Overflow wraps"), 13 },
107 { tr("Background dimensions"), 14, 2 },
108 });
109 // 0x0400000E: BG3CNT
110 regGBA.append({
111 { tr("Priority"), 0, 2 },
112 { tr("Tile data base (* 16kB)"), 2, 2 },
113 { tr("Enable mosaic"), 6 },
114 { tr("Enable 256-color"), 7 },
115 { tr("Tile map base (* 2kB)"), 8, 5 },
116 { tr("Overflow wraps"), 13 },
117 { tr("Background dimensions"), 14, 2 },
118 });
119 // 0x04000010: BG0HOFS
120 regGBA.append({
121 { tr("Horizontal offset"), 0, 9 },
122 });
123 // 0x04000012: BG0VOFS
124 regGBA.append({
125 { tr("Vertical offset"), 0, 9 },
126 });
127 // 0x04000014: BG1HOFS
128 regGBA.append({
129 { tr("Horizontal offset"), 0, 9 },
130 });
131 // 0x04000016: BG1VOFS
132 regGBA.append({
133 { tr("Vertical offset"), 0, 9 },
134 });
135 // 0x04000018: BG2HOFS
136 regGBA.append({
137 { tr("Horizontal offset"), 0, 9 },
138 });
139 // 0x0400001A: BG2VOFS
140 regGBA.append({
141 { tr("Vertical offset"), 0, 9 },
142 });
143 // 0x0400001C: BG3HOFS
144 regGBA.append({
145 { tr("Horizontal offset"), 0, 9 },
146 });
147 // 0x0400001E: BG3VOFS
148 regGBA.append({
149 { tr("Vertical offset"), 0, 9 },
150 });
151 // 0x04000020: BG2PA
152 regGBA.append({
153 { tr("Fractional part"), 0, 8 },
154 { tr("Integer part"), 8, 8 },
155 });
156 // 0x04000022: BG2PB
157 regGBA.append({
158 { tr("Fractional part"), 0, 8 },
159 { tr("Integer part"), 8, 8 },
160 });
161 // 0x04000024: BG2PC
162 regGBA.append({
163 { tr("Fractional part"), 0, 8 },
164 { tr("Integer part"), 8, 8 },
165 });
166 // 0x04000026: BG2PD
167 regGBA.append({
168 { tr("Fractional part"), 0, 8 },
169 { tr("Integer part"), 8, 8 },
170 });
171 // 0x04000028: BG2X_LO
172 regGBA.append({
173 { tr("Fractional part"), 0, 8 },
174 { tr("Integer part (low)"), 8, 8 },
175 });
176 // 0x0400002A: BG2X_HI
177 regGBA.append({
178 { tr("Integer part (high)"), 0, 12 },
179 });
180 // 0x0400002C: BG2Y_LO
181 regGBA.append({
182 { tr("Fractional part"), 0, 8 },
183 { tr("Integer part (low)"), 8, 8 },
184 });
185 // 0x0400002E: BG2Y_HI
186 regGBA.append({
187 { tr("Integer part (high)"), 0, 12 },
188 });
189 // 0x04000030: BG3PA
190 regGBA.append({
191 { tr("Fractional part"), 0, 8 },
192 { tr("Integer part"), 8, 8 },
193 });
194 // 0x04000032: BG3PB
195 regGBA.append({
196 { tr("Fractional part"), 0, 8 },
197 { tr("Integer part"), 8, 8 },
198 });
199 // 0x04000034: BG3PC
200 regGBA.append({
201 { tr("Fractional part"), 0, 8 },
202 { tr("Integer part"), 8, 8 },
203 });
204 // 0x04000036: BG3PD
205 regGBA.append({
206 { tr("Fractional part"), 0, 8 },
207 { tr("Integer part"), 8, 8 },
208 });
209 // 0x04000038: BG3X_LO
210 regGBA.append({
211 { tr("Fractional part"), 0, 8 },
212 { tr("Integer part (low)"), 8, 8 },
213 });
214 // 0x0400003A: BG3X_HI
215 regGBA.append({
216 { tr("Integer part (high)"), 0, 12 },
217 });
218 // 0x0400003C: BG3Y_LO
219 regGBA.append({
220 { tr("Fractional part"), 0, 8 },
221 { tr("Integer part (low)"), 8, 8 },
222 });
223 // 0x0400003E: BG3Y_HI
224 regGBA.append({
225 { tr("Integer part (high)"), 0, 12 },
226 });
227 // 0x04000040: WIN0H
228 regGBA.append({
229 { tr("End x"), 0, 8 },
230 { tr("Start x"), 8, 8 },
231 });
232 // 0x04000042: WIN1H
233 regGBA.append({
234 { tr("End x"), 0, 8 },
235 { tr("Start x"), 8, 8 },
236 });
237 // 0x04000044: WIN0V
238 regGBA.append({
239 { tr("End y"), 0, 8 },
240 { tr("Start y"), 8, 8 },
241 });
242 // 0x04000046: WIN1V
243 regGBA.append({
244 { tr("End y"), 0, 8 },
245 { tr("Start y"), 8, 8 },
246 });
247 // 0x04000048: WININ
248 regGBA.append({
249 { tr("Window 0 enable BG 0"), 0 },
250 { tr("Window 0 enable BG 1"), 1 },
251 { tr("Window 0 enable BG 2"), 2 },
252 { tr("Window 0 enable BG 3"), 3 },
253 { tr("Window 0 enable OBJ"), 4 },
254 { tr("Window 0 enable blend"), 5 },
255 { tr("Window 1 enable BG 0"), 8 },
256 { tr("Window 1 enable BG 1"), 9 },
257 { tr("Window 1 enable BG 2"), 10 },
258 { tr("Window 1 enable BG 3"), 11 },
259 { tr("Window 1 enable OBJ"), 12 },
260 { tr("Window 1 enable blend"), 13 },
261 });
262 // 0x0400004A: WINOUT
263 regGBA.append({
264 { tr("Outside window enable BG 0"), 0 },
265 { tr("Outside window enable BG 1"), 1 },
266 { tr("Outside window enable BG 2"), 2 },
267 { tr("Outside window enable BG 3"), 3 },
268 { tr("Outside window enable OBJ"), 4 },
269 { tr("Outside window enable blend"), 5 },
270 { tr("OBJ window enable BG 0"), 8 },
271 { tr("OBJ window enable BG 1"), 9 },
272 { tr("OBJ window enable BG 2"), 10 },
273 { tr("OBJ window enable BG 3"), 11 },
274 { tr("OBJ window enable OBJ"), 12 },
275 { tr("OBJ window enable blend"), 13 },
276 });
277 // 0x0400004C: MOSAIC
278 regGBA.append({
279 { tr("Background mosaic size vertical"), 0, 4 },
280 { tr("Background mosaic size horizontal"), 4, 4 },
281 { tr("Object mosaic size vertical"), 8, 4 },
282 { tr("Object mosaic size horizontal"), 12, 4 },
283 });
284 // 0x0400004E: Unused
285 regGBA.append(RegisterDescription());
286 // 0x04000050: BLDCNT
287 regGBA.append({
288 { tr("BG 0 target 1"), 0 },
289 { tr("BG 1 target 1"), 1 },
290 { tr("BG 2 target 1"), 2 },
291 { tr("BG 3 target 1"), 3 },
292 { tr("OBJ target 1"), 4 },
293 { tr("Backdrop target 1"), 5 },
294 { tr("Blend mode"), 6, 2, {
295 tr("Disabled"),
296 tr("Additive blending"),
297 tr("Brighten"),
298 tr("Darken"),
299 } },
300 { tr("BG 0 target 2"), 8 },
301 { tr("BG 1 target 2"), 9 },
302 { tr("BG 2 target 2"), 10 },
303 { tr("BG 3 target 2"), 11 },
304 { tr("OBJ target 2"), 12 },
305 { tr("Backdrop target 2"), 13 },
306 });
307 // 0x04000052: BLDALPHA
308 regGBA.append({
309 { tr("Blend A (target 1)"), 0, 5 },
310 { tr("Blend B (target 2)"), 8, 5 },
311 });
312 // 0x04000054: BLDY
313 regGBA.append({
314 { tr("Blend Y"), 0, 5 },
315 });
316 // 0x04000056: Unused
317 regGBA.append(RegisterDescription());
318 // 0x04000058: Unused
319 regGBA.append(RegisterDescription());
320 // 0x0400005A: Unused
321 regGBA.append(RegisterDescription());
322 // 0x0400005C: Unused
323 regGBA.append(RegisterDescription());
324 // 0x0400005E: Unused
325 regGBA.append(RegisterDescription());
326 // 0x04000060: SOUND1CNT_LO
327 regGBA.append({
328 { tr("Sweep shifts"), 0, 3 },
329 { tr("Sweep subtract"), 3 },
330 { tr("Sweep time (in 1/128s)"), 4, 3 },
331 });
332 // 0x04000062: SOUND1CNT_HI
333 regGBA.append({
334 { tr("Sound length"), 0, 6 },
335 { tr("Duty cycle"), 6, 2 },
336 { tr("Envelope step time"), 8, 3 },
337 { tr("Envelope increase"), 11 },
338 { tr("Initial volume"), 12, 4 },
339 });
340 // 0x04000064: SOUND1CNT_X
341 regGBA.append({
342 { tr("Sound frequency"), 0, 11 },
343 { tr("Timed"), 14 },
344 { tr("Reset"), 15 },
345 });
346 // 0x04000066: Unused
347 regGBA.append(RegisterDescription());
348 // 0x04000068: SOUND2CNT_LO
349 regGBA.append({
350 { tr("Sound length"), 0, 6 },
351 { tr("Duty cycle"), 6, 2 },
352 { tr("Envelope step time"), 8, 3 },
353 { tr("Envelope increase"), 11 },
354 { tr("Initial volume"), 12, 4 },
355 });
356 // 0x0400006A: Unused
357 regGBA.append(RegisterDescription());
358 // 0x0400006C: SOUND2CNT_HI
359 regGBA.append({
360 { tr("Sound frequency"), 0, 11 },
361 { tr("Timed"), 14 },
362 { tr("Reset"), 15 },
363 });
364 // 0x0400006E: Unused
365 regGBA.append(RegisterDescription());
366 // 0x04000070: SOUND3CNT_LO
367 regGBA.append({
368 { tr("Double-size wave table"), 5 },
369 { tr("Active wave table"), 6 },
370 { tr("Enable channel 3"), 7 },
371 });
372 // 0x04000072: SOUND3CNT_HI
373 regGBA.append({
374 { tr("Sound length"), 0, 8 },
375 { tr("Volume"), 13, 3, {
376 tr("0%"),
377 tr("100%"),
378 tr("50%"),
379 tr("25%"),
380 tr("75%"),
381 tr("75%"),
382 tr("75%"),
383 tr("75%")
384 } },
385 });
386 // 0x04000074: SOUND3CNT_X
387 regGBA.append({
388 { tr("Sound frequency"), 0, 11 },
389 { tr("Timed"), 14 },
390 { tr("Reset"), 15 },
391 });
392 // 0x04000076: Unused
393 regGBA.append(RegisterDescription());
394 // 0x04000078: SOUND4CNT_LO
395 regGBA.append({
396 { tr("Sound length"), 0, 6 },
397 { tr("Envelope step time"), 8, 3 },
398 { tr("Envelope increase"), 11 },
399 { tr("Initial volume"), 12, 4 },
400 });
401 // 0x0400007A: Unused
402 regGBA.append(RegisterDescription());
403 // 0x0400007C: SOUND4CNT_HI
404 regGBA.append({
405 { tr("Clock divider"), 0, 3 },
406 { tr("Register stages"), 3, 1, {
407 tr("15"),
408 tr("7"),
409 } },
410 { tr("Shifter frequency"), 4, 4 },
411 { tr("Timed"), 14 },
412 { tr("Reset"), 15 },
413 });
414 // 0x0400007E: Unused
415 regGBA.append(RegisterDescription());
416 // 0x04000080: SOUNDCNT_LO
417 regGBA.append({
418 { tr("PSG volume right"), 0, 3 },
419 { tr("PSG volume left"), 4, 3 },
420 { tr("Enable channel 1 right"), 8 },
421 { tr("Enable channel 2 right"), 9 },
422 { tr("Enable channel 3 right"), 10 },
423 { tr("Enable channel 4 right"), 11 },
424 { tr("Enable channel 1 left"), 12 },
425 { tr("Enable channel 2 left"), 13 },
426 { tr("Enable channel 3 left"), 14 },
427 { tr("Enable channel 4 left"), 15 },
428 });
429 // 0x04000082: SOUNDCNT_HI
430 regGBA.append({
431 { tr("PSG master volume"), 0, 2, {
432 tr("25%"),
433 tr("50%"),
434 tr("100%"),
435 QString()
436 } },
437 { tr("Loud channel A"), 2 },
438 { tr("Loud channel B"), 3 },
439 { tr("Enable channel A right"), 8 },
440 { tr("Enable channel A left"), 9 },
441 { tr("Channel A timer"), 10, 1, {
442 tr("0"),
443 tr("1"),
444 } },
445 { tr("Channel A reset"), 11 },
446 { tr("Enable channel B right"), 12 },
447 { tr("Enable channel B left"), 13 },
448 { tr("Channel B timer"), 14, 1, {
449 tr("0"),
450 tr("1"),
451 } },
452 { tr("Channel B reset"), 15 },
453 });
454 // 0x04000084: SOUNDCNT_LO
455 regGBA.append({
456 { tr("Active channel 1"), 0, 1, true },
457 { tr("Active channel 2"), 1, 1, true },
458 { tr("Active channel 3"), 2, 1, true },
459 { tr("Active channel 4"), 3, 1, true },
460 { tr("Enable audio"), 7 },
461 });
462 // 0x04000086: Unused
463 regGBA.append(RegisterDescription());
464 // 0x04000088: SOUNDBIAS
465 regGBA.append({
466 { tr("Bias"), 0, 10 },
467 { tr("Resolution"), 14, 2 },
468 });
469 // 0x0400008A: Unused
470 regGBA.append(RegisterDescription());
471 // 0x0400008C: Unused
472 regGBA.append(RegisterDescription());
473 // 0x0400008E: Unused
474 regGBA.append(RegisterDescription());
475 // 0x04000090: WAVE_RAM0_LO
476 regGBA.append({
477 { tr("Sample"), 0, 4 },
478 { tr("Sample"), 4, 4 },
479 { tr("Sample"), 8, 4 },
480 { tr("Sample"), 12, 4 },
481 });
482 // 0x04000092: WAVE_RAM0_HI
483 regGBA.append({
484 { tr("Sample"), 0, 4 },
485 { tr("Sample"), 4, 4 },
486 { tr("Sample"), 8, 4 },
487 { tr("Sample"), 12, 4 },
488 });
489 // 0x04000094: WAVE_RAM1_LO
490 regGBA.append({
491 { tr("Sample"), 0, 4 },
492 { tr("Sample"), 4, 4 },
493 { tr("Sample"), 8, 4 },
494 { tr("Sample"), 12, 4 },
495 });
496 // 0x04000096: WAVE_RAM1_HI
497 regGBA.append({
498 { tr("Sample"), 0, 4 },
499 { tr("Sample"), 4, 4 },
500 { tr("Sample"), 8, 4 },
501 { tr("Sample"), 12, 4 },
502 });
503 // 0x04000098: WAVE_RAM2_LO
504 regGBA.append({
505 { tr("Sample"), 0, 4 },
506 { tr("Sample"), 4, 4 },
507 { tr("Sample"), 8, 4 },
508 { tr("Sample"), 12, 4 },
509 });
510 // 0x0400009A: WAVE_RAM2_HI
511 regGBA.append({
512 { tr("Sample"), 0, 4 },
513 { tr("Sample"), 4, 4 },
514 { tr("Sample"), 8, 4 },
515 { tr("Sample"), 12, 4 },
516 });
517 // 0x0400009C: WAVE_RAM3_LO
518 regGBA.append({
519 { tr("Sample"), 0, 4 },
520 { tr("Sample"), 4, 4 },
521 { tr("Sample"), 8, 4 },
522 { tr("Sample"), 12, 4 },
523 });
524 // 0x0400009E: WAVE_RAM0_HI
525 regGBA.append({
526 { tr("Sample"), 0, 4 },
527 { tr("Sample"), 4, 4 },
528 { tr("Sample"), 8, 4 },
529 { tr("Sample"), 12, 4 },
530 });
531 // 0x040000A0: FIFO_A_LO
532 regGBA.append({
533 { tr("Sample"), 0, 8 },
534 { tr("Sample"), 8, 8 },
535 });
536 // 0x040000A2: FIFO_A_HI
537 regGBA.append({
538 { tr("Sample"), 0, 8 },
539 { tr("Sample"), 8, 8 },
540 });
541 // 0x040000A4: FIFO_B_LO
542 regGBA.append({
543 { tr("Sample"), 0, 8 },
544 { tr("Sample"), 8, 8 },
545 });
546 // 0x040000A6: FIFO_B_HI
547 regGBA.append({
548 { tr("Sample"), 0, 8 },
549 { tr("Sample"), 8, 8 },
550 });
551 // 0x040000A8: Unused
552 regGBA.append(RegisterDescription());
553 // 0x040000AA: Unused
554 regGBA.append(RegisterDescription());
555 // 0x040000AC: Unused
556 regGBA.append(RegisterDescription());
557 // 0x040000AE: Unused
558 regGBA.append(RegisterDescription());
559 // 0x040000B0: DMA0SAD_LO
560 regGBA.append({
561 { tr("Address (low)"), 0, 16 },
562 });
563 // 0x040000B2: DMA0SAD_HI
564 regGBA.append({
565 { tr("Address (high)"), 0, 16 },
566 });
567 // 0x040000B4: DMA0DAD_LO
568 regGBA.append({
569 { tr("Address (low)"), 0, 16 },
570 });
571 // 0x040000B6: DMA0DAD_HI
572 regGBA.append({
573 { tr("Address (high)"), 0, 16 },
574 });
575 // 0x040000B8: DMA0CNT_LO
576 regGBA.append({
577 { tr("Word count"), 0, 16 },
578 });
579 // 0x040000BA: DMA0CNT_HI
580 regGBA.append({
581 { tr("Destination offset"), 5, 2, {
582 tr("Increment"),
583 tr("Decrement"),
584 tr("Fixed"),
585 tr("Increment and reload"),
586 } },
587 { tr("Source offset"), 7, 2, {
588 tr("Increment"),
589 tr("Decrement"),
590 tr("Fixed"),
591 QString(),
592 } },
593 { tr("Repeat"), 9 },
594 { tr("32-bit"), 10 },
595 { tr("Start timing"), 12, 2, {
596 tr("Immediate"),
597 tr("VBlank"),
598 tr("HBlank"),
599 QString(),
600 } },
601 { tr("IRQ"), 14 },
602 { tr("Enable"), 15 },
603 });
604 // 0x040000BC: DMA1SAD_LO
605 regGBA.append({
606 { tr("Address (low)"), 0, 16 },
607 });
608 // 0x040000BE: DMA1SAD_HI
609 regGBA.append({
610 { tr("Address (high)"), 0, 16 },
611 });
612 // 0x040000C0: DMA1DAD_LO
613 regGBA.append({
614 { tr("Address (low)"), 0, 16 },
615 });
616 // 0x040000C2: DMA1DAD_HI
617 regGBA.append({
618 { tr("Address (high)"), 0, 16 },
619 });
620 // 0x040000C4: DMA1CNT_LO
621 regGBA.append({
622 { tr("Word count"), 0, 16 },
623 });
624 // 0x040000C6: DMA1CNT_HI
625 regGBA.append({
626 { tr("Destination offset"), 5, 2, {
627 tr("Increment"),
628 tr("Decrement"),
629 tr("Fixed"),
630 tr("Increment and reload"),
631 } },
632 { tr("Source offset"), 7, 2, {
633 tr("Increment"),
634 tr("Decrement"),
635 tr("Fixed"),
636 QString(),
637 } },
638 { tr("Repeat"), 9 },
639 { tr("32-bit"), 10 },
640 { tr("Start timing"), 12, 2, {
641 tr("Immediate"),
642 tr("VBlank"),
643 tr("HBlank"),
644 tr("Audio FIFO"),
645 } },
646 { tr("IRQ"), 14 },
647 { tr("Enable"), 15 },
648 });
649 // 0x040000C8: DMA2SAD_LO
650 regGBA.append({
651 { tr("Address (low)"), 0, 16 },
652 });
653 // 0x040000CA: DMA2SAD_HI
654 regGBA.append({
655 { tr("Address (high)"), 0, 16 },
656 });
657 // 0x040000CC: DMA2DAD_LO
658 regGBA.append({
659 { tr("Address (low)"), 0, 16 },
660 });
661 // 0x040000CE: DMA2DAD_HI
662 regGBA.append({
663 { tr("Address (high)"), 0, 16 },
664 });
665 // 0x040000D0: DMA2CNT_LO
666 regGBA.append({
667 { tr("Word count"), 0, 16 },
668 });
669 // 0x040000D2: DMA2CNT_HI
670 regGBA.append({
671 { tr("Destination offset"), 5, 2, {
672 tr("Increment"),
673 tr("Decrement"),
674 tr("Fixed"),
675 tr("Increment and reload"),
676 } },
677 { tr("Source offset"), 7, 2, {
678 tr("Increment"),
679 tr("Decrement"),
680 tr("Fixed"),
681 QString(),
682 } },
683 { tr("Repeat"), 9 },
684 { tr("32-bit"), 10 },
685 { tr("Start timing"), 12, 2, {
686 tr("Immediate"),
687 tr("VBlank"),
688 tr("HBlank"),
689 tr("Audio FIFO"),
690 } },
691 { tr("IRQ"), 14 },
692 { tr("Enable"), 15 },
693 });
694 // 0x040000D4: DMA3SAD_LO
695 regGBA.append({
696 { tr("Address (low)"), 0, 16 },
697 });
698 // 0x040000D6: DMA3SAD_HI
699 regGBA.append({
700 { tr("Address (high)"), 0, 16 },
701 });
702 // 0x040000D8: DMA3DAD_LO
703 regGBA.append({
704 { tr("Address (low)"), 0, 16 },
705 });
706 // 0x040000DA: DMA3DAD_HI
707 regGBA.append({
708 { tr("Address (high)"), 0, 16 },
709 });
710 // 0x040000DC: DMA3CNT_LO
711 regGBA.append({
712 { tr("Word count"), 0, 16 },
713 });
714 // 0x040000DE: DMA3CNT_HI
715 regGBA.append({
716 { tr("Destination offset"), 5, 2, {
717 tr("Increment"),
718 tr("Decrement"),
719 tr("Fixed"),
720 tr("Increment and reload"),
721 } },
722 { tr("Source offset"), 7, 2, {
723 tr("Increment"),
724 tr("Decrement"),
725 tr("Fixed"),
726 tr("Video Capture"),
727 } },
728 { tr("DRQ"), 8 },
729 { tr("Repeat"), 9 },
730 { tr("32-bit"), 10 },
731 { tr("Start timing"), 12, 2, {
732 tr("Immediate"),
733 tr("VBlank"),
734 tr("HBlank"),
735 tr("Audio FIFO"),
736 } },
737 { tr("IRQ"), 14 },
738 { tr("Enable"), 15 },
739 });
740 // 0x040000E0: Unused
741 regGBA.append(RegisterDescription());
742 // 0x040000E2: Unused
743 regGBA.append(RegisterDescription());
744 // 0x040000E4: Unused
745 regGBA.append(RegisterDescription());
746 // 0x040000E6: Unused
747 regGBA.append(RegisterDescription());
748 // 0x040000E8: Unused
749 regGBA.append(RegisterDescription());
750 // 0x040000EA: Unused
751 regGBA.append(RegisterDescription());
752 // 0x040000EC: Unused
753 regGBA.append(RegisterDescription());
754 // 0x040000EE: Unused
755 regGBA.append(RegisterDescription());
756 // 0x040000F0: Unused
757 regGBA.append(RegisterDescription());
758 // 0x040000F2: Unused
759 regGBA.append(RegisterDescription());
760 // 0x040000F4: Unused
761 regGBA.append(RegisterDescription());
762 // 0x040000F6: Unused
763 regGBA.append(RegisterDescription());
764 // 0x040000F8: Unused
765 regGBA.append(RegisterDescription());
766 // 0x040000FA: Unused
767 regGBA.append(RegisterDescription());
768 // 0x040000FC: Unused
769 regGBA.append(RegisterDescription());
770 // 0x040000FE: Unused
771 regGBA.append(RegisterDescription());
772 // 0x04000100: TM0CNT_LO
773 regGBA.append({
774 { tr("Value"), 0, 16 },
775 });
776 // 0x04000102: TM0CNT_HI
777 regGBA.append({
778 { tr("Scale"), 0, 2, {
779 tr("1"),
780 tr("1/64"),
781 tr("1/256"),
782 tr("1/1024"),
783 } },
784 { tr("IRQ"), 6 },
785 { tr("Enable"), 7 },
786 });
787 // 0x04000104: TM1CNT_LO
788 regGBA.append({
789 { tr("Value"), 0, 16 },
790 });
791 // 0x04000106: TM1CNT_HI
792 regGBA.append({
793 { tr("Scale"), 0, 2, {
794 tr("1"),
795 tr("1/64"),
796 tr("1/256"),
797 tr("1/1024"),
798 } },
799 { tr("Cascade"), 2 },
800 { tr("IRQ"), 6 },
801 { tr("Enable"), 7 },
802 });
803 // 0x04000108: TM2CNT_LO
804 regGBA.append({
805 { tr("Value"), 0, 16 },
806 });
807 // 0x0400010A: TM2CNT_HI
808 regGBA.append({
809 { tr("Scale"), 0, 2, {
810 tr("1"),
811 tr("1/64"),
812 tr("1/256"),
813 tr("1/1024"),
814 } },
815 { tr("Cascade"), 2 },
816 { tr("IRQ"), 6 },
817 { tr("Enable"), 7 },
818 });
819 // 0x0400010C: TM3CNT_LO
820 regGBA.append({
821 { tr("Value"), 0, 16 },
822 });
823 // 0x0400010E: TM3CNT_HI
824 regGBA.append({
825 { tr("Scale"), 0, 2, {
826 tr("1"),
827 tr("1/64"),
828 tr("1/256"),
829 tr("1/1024"),
830 } },
831 { tr("Cascade"), 2 },
832 { tr("IRQ"), 6 },
833 { tr("Enable"), 7 },
834 });
835 // 0x04000110: Unused
836 regGBA.append(RegisterDescription());
837 // 0x04000112: Unused
838 regGBA.append(RegisterDescription());
839 // 0x04000114: Unused
840 regGBA.append(RegisterDescription());
841 // 0x04000116: Unused
842 regGBA.append(RegisterDescription());
843 // 0x04000118: Unused
844 regGBA.append(RegisterDescription());
845 // 0x0400011A: Unused
846 regGBA.append(RegisterDescription());
847 // 0x0400011C: Unused
848 regGBA.append(RegisterDescription());
849 // 0x0400011E: Unused
850 regGBA.append(RegisterDescription());
851 // 0x04000120: SIOMULTI0
852 regGBA.append(RegisterDescription());
853 // 0x04000122: SIOMULTI1
854 regGBA.append(RegisterDescription());
855 // 0x04000124: SIOMULTI2
856 regGBA.append(RegisterDescription());
857 // 0x04000126: SIOMULTI3
858 regGBA.append(RegisterDescription());
859 // 0x04000128: SIOCNT
860 regGBA.append(RegisterDescription());
861 // 0x0400012A: SIOMLT_SEND
862 regGBA.append(RegisterDescription());
863 // 0x0400012C: Unused
864 regGBA.append(RegisterDescription());
865 // 0x0400012E: Unused
866 regGBA.append(RegisterDescription());
867 // 0x04000130: KEYINPUT
868 regGBA.append({
869 { tr("A"), 0 },
870 { tr("B"), 1 },
871 { tr("Select"), 2 },
872 { tr("Start"), 3 },
873 { tr("Right"), 4 },
874 { tr("Left"), 5 },
875 { tr("Up"), 6 },
876 { tr("Down"), 7 },
877 { tr("R"), 8 },
878 { tr("L"), 9 },
879 });
880 // 0x04000132: KEYCNT
881 regGBA.append({
882 { tr("A"), 0 },
883 { tr("B"), 1 },
884 { tr("Select"), 2 },
885 { tr("Start"), 3 },
886 { tr("Right"), 4 },
887 { tr("Left"), 5 },
888 { tr("Up"), 6 },
889 { tr("Down"), 7 },
890 { tr("R"), 8 },
891 { tr("L"), 9 },
892 { tr("IRQ"), 14 },
893 { tr("Condition"), 15 },
894 });
895 // 0x04000134: RCNT
896 regGBA.append({
897 { tr("SC"), 0 },
898 { tr("SD"), 1 },
899 { tr("SI"), 2 },
900 { tr("SO"), 3 },
901 });
902 // 0x04000136: Unused
903 regGBA.append(RegisterDescription());
904 // 0x04000138: SIOCNT
905 regGBA.append(RegisterDescription());
906 // 0x0400013A: Unused
907 regGBA.append(RegisterDescription());
908 // 0x0400013C: Unused
909 regGBA.append(RegisterDescription());
910 // 0x0400013E: Unused
911 regGBA.append(RegisterDescription());
912 // 0x04000140: JOYCNT
913 regGBA.append(RegisterDescription());
914 // 0x04000142: Unused
915 regGBA.append(RegisterDescription());
916 // 0x04000144: Unused
917 regGBA.append(RegisterDescription());
918 // 0x04000146: Unused
919 regGBA.append(RegisterDescription());
920 // 0x04000148: Unused
921 regGBA.append(RegisterDescription());
922 // 0x0400014A: Unused
923 regGBA.append(RegisterDescription());
924 // 0x0400014C: Unused
925 regGBA.append(RegisterDescription());
926 // 0x0400014E: Unused
927 regGBA.append(RegisterDescription());
928 // 0x04000150: JOY_RECV_LO
929 regGBA.append(RegisterDescription());
930 // 0x04000152: JOY_RECV_HI
931 regGBA.append(RegisterDescription());
932 // 0x04000154: JOY_TRANS_LO
933 regGBA.append(RegisterDescription());
934 // 0x04000156: JOY_TRANS_HI
935 regGBA.append(RegisterDescription());
936 // 0x04000158: JOYSTAT
937 regGBA.append(RegisterDescription());
938 for (int i = 0x15A; i < 0x200; i += 2) {
939 // Unused
940 regGBA.append(RegisterDescription());
941 }
942 // 0x04000200: IE
943 regGBA.append({
944 { tr("VBlank"), 0 },
945 { tr("HBlank"), 1 },
946 { tr("VCounter"), 2 },
947 { tr("Timer 0"), 3 },
948 { tr("Timer 1"), 4 },
949 { tr("Timer 2"), 5 },
950 { tr("Timer 3"), 6 },
951 { tr("SIO"), 7 },
952 { tr("DMA 0"), 8 },
953 { tr("DMA 1"), 9 },
954 { tr("DMA 2"), 10 },
955 { tr("DMA 3"), 11 },
956 { tr("Keypad"), 12 },
957 { tr("Gamepak"), 13 },
958 });
959 // 0x04000202: IF
960 regGBA.append({
961 { tr("VBlank"), 0 },
962 { tr("HBlank"), 1 },
963 { tr("VCounter"), 2 },
964 { tr("Timer 0"), 3 },
965 { tr("Timer 1"), 4 },
966 { tr("Timer 2"), 5 },
967 { tr("Timer 3"), 6 },
968 { tr("SIO"), 7 },
969 { tr("DMA 0"), 8 },
970 { tr("DMA 1"), 9 },
971 { tr("DMA 2"), 10 },
972 { tr("DMA 3"), 11 },
973 { tr("Keypad"), 12 },
974 { tr("Gamepak"), 13 },
975 });
976 // 0x04000204: WAITCNT
977 regGBA.append({
978 { tr("SRAM wait"), 0, 2, {
979 tr("4"),
980 tr("3"),
981 tr("2"),
982 tr("8"),
983 } },
984 { tr("Cart 0 non-sequential"), 2, 2, {
985 tr("4"),
986 tr("3"),
987 tr("2"),
988 tr("8"),
989 } },
990 { tr("Cart 0 sequential"), 4, 1, {
991 tr("2"),
992 tr("1"),
993 } },
994 { tr("Cart 1 non-sequential"), 5, 2, {
995 tr("4"),
996 tr("3"),
997 tr("2"),
998 tr("8"),
999 } },
1000 { tr("Cart 1 sequential"), 7, 1, {
1001 tr("4"),
1002 tr("1"),
1003 } },
1004 { tr("Cart 2 non-sequential"), 8, 2, {
1005 tr("4"),
1006 tr("3"),
1007 tr("2"),
1008 tr("8"),
1009 } },
1010 { tr("Cart 2 sequential"), 10, 1, {
1011 tr("8"),
1012 tr("1"),
1013 } },
1014 { tr("PHI terminal"), 11, 2, {
1015 tr("Disable"),
1016 tr("4.19MHz"),
1017 tr("8.38MHz"),
1018 tr("16.78MHz"),
1019 } },
1020 { tr("Gamepak prefetch"), 14 },
1021 });
1022 // 0x04000206: Unused
1023 regGBA.append(RegisterDescription());
1024 // 0x04000208: IME
1025 regGBA.append({
1026 { tr("Enable IRQs"), 0 },
1027 });
1028 s_registers[mPLATFORM_GBA] = regGBA;
1029#endif
1030#ifdef M_CORE_GB
1031 QList<IOViewer::RegisterDescription> regGB;
1032 // 0xFF00: JOYP
1033 regGB.append({
1034 { tr("Right/A"), 0, 1, true },
1035 { tr("Left/B"), 1, 1, true },
1036 { tr("Up/Select"), 2, 1, true },
1037 { tr("Down/Start"), 3, 1, true },
1038 { tr("Active D-pad"), 4 },
1039 { tr("Active face buttons"), 5 },
1040 });
1041 // 0xFF01: SB
1042 regGB.append({
1043 { tr("Value"), 0, 8 },
1044 });
1045 // 0xFF02: SC
1046 regGB.append({
1047 { tr("Internal clock"), 0 },
1048 { tr("32× clocking (CGB only)"), 1 },
1049 { tr("Transfer active"), 7 },
1050 });
1051 // 0xFF03: Unused
1052 regGB.append(RegisterDescription());
1053 // 0xFF04: DIV
1054 regGB.append({
1055 { tr("Value"), 0, 8 },
1056 });
1057 // 0xFF05: TIMA
1058 regGB.append({
1059 { tr("Value"), 0, 8 },
1060 });
1061 // 0xFF06: TMA
1062 regGB.append({
1063 { tr("Value"), 0, 8 },
1064 });
1065 // 0xFF07: TAC
1066 regGB.append({
1067 { tr("Divider"), 0, 2, {
1068 tr("1/1024"),
1069 tr("1/16"),
1070 tr("1/64"),
1071 tr("1/256"),
1072 } },
1073 { tr("Enable"), 2 },
1074 });
1075 // 0xFF08: Unused
1076 regGB.append(RegisterDescription());
1077 // 0xFF09: Unused
1078 regGB.append(RegisterDescription());
1079 // 0xFF0A: Unused
1080 regGB.append(RegisterDescription());
1081 // 0xFF0B: Unused
1082 regGB.append(RegisterDescription());
1083 // 0xFF0C: Unused
1084 regGB.append(RegisterDescription());
1085 // 0xFF0D: Unused
1086 regGB.append(RegisterDescription());
1087 // 0xFF0E: Unused
1088 regGB.append(RegisterDescription());
1089 // 0xFF0F: IF
1090 regGB.append({
1091 { tr("VBlank"), 0 },
1092 { tr("LCD STAT"), 1 },
1093 { tr("Timer"), 2 },
1094 { tr("Serial"), 3 },
1095 { tr("Joypad"), 4 },
1096 });
1097 // 0xFF10: NR10
1098 regGB.append({
1099 { tr("Sweep shifts"), 0, 3 },
1100 { tr("Sweep subtract"), 3 },
1101 { tr("Sweep time (in 1/128s)"), 4, 3 },
1102 });
1103 // 0xFF11: NR11
1104 regGB.append({
1105 { tr("Sound length"), 0, 6 },
1106 { tr("Duty cycle"), 6, 2 },
1107 });
1108 // 0xFF12: NR12
1109 regGB.append({
1110 { tr("Envelope step time"), 0, 3 },
1111 { tr("Envelope increase"), 3 },
1112 { tr("Initial volume"), 4, 4 },
1113 });
1114 // 0xFF13: NR13
1115 regGB.append({
1116 { tr("Sound frequency (low)"), 0, 8 },
1117 });
1118 // 0xFF14: NR14
1119 regGB.append({
1120 { tr("Sound frequency (high)"), 0, 3 },
1121 { tr("Timed"), 6 },
1122 { tr("Reset"), 7 },
1123 });
1124 // 0xFF15: Unused (NR20)
1125 regGB.append(RegisterDescription());
1126 // 0xFF16: NR21
1127 regGB.append({
1128 { tr("Sound length"), 0, 6 },
1129 { tr("Duty cycle"), 6, 2 },
1130 });
1131 // 0xFF17: NR22
1132 regGB.append({
1133 { tr("Envelope step time"), 0, 3 },
1134 { tr("Envelope increase"), 3 },
1135 { tr("Initial volume"), 4, 4 },
1136 });
1137 // 0xFF18: NR23
1138 regGB.append({
1139 { tr("Sound frequency (low)"), 0, 8 },
1140 });
1141 // 0xFF19: NR24
1142 regGB.append({
1143 { tr("Sound frequency (high)"), 0, 3 },
1144 { tr("Timed"), 6 },
1145 { tr("Reset"), 7 },
1146 });
1147 // 0xFF1A: NR30
1148 regGB.append({
1149 { tr("Enable channel 3"), 7 },
1150 });
1151 // 0xFF1B: NR31
1152 regGB.append({
1153 { tr("Sound length"), 0, 8 },
1154 });
1155 // 0xFF1C: NR32
1156 regGB.append({
1157 { tr("Volume"), 5, 2, {
1158 tr("0%"),
1159 tr("100%"),
1160 tr("50%"),
1161 tr("25%"),
1162 } },
1163 });
1164 // 0xFF1D: NR33
1165 regGB.append({
1166 { tr("Sound frequency (low)"), 0, 8 },
1167 });
1168 // 0xFF1E: NR34
1169 regGB.append({
1170 { tr("Sound frequency (high)"), 0, 3 },
1171 { tr("Timed"), 6 },
1172 { tr("Reset"), 7 },
1173 });
1174 // 0xFF1F: Unusued (NR40)
1175 regGB.append(RegisterDescription());
1176 // 0xFF20: NR41
1177 regGB.append({
1178 { tr("Sound length"), 0, 6 },
1179 });
1180 // 0xFF21: NR42
1181 regGB.append({
1182 { tr("Envelope step time"), 0, 3 },
1183 { tr("Envelope increase"), 3 },
1184 { tr("Initial volume"), 4, 4 },
1185 });
1186 // 0xFF22: NR43
1187 regGB.append({
1188 { tr("Clock divider"), 0, 3 },
1189 { tr("Register stages"), 3, 1, {
1190 tr("15"),
1191 tr("7"),
1192 } },
1193 { tr("Shifter frequency"), 4, 4 },
1194 });
1195 // 0xFF23: NR44
1196 regGB.append({
1197 { tr("Timed"), 6 },
1198 { tr("Reset"), 7 },
1199 });
1200 // 0xFF24: NR50
1201 regGB.append({
1202 { tr("Volume right"), 0, 3 },
1203 { tr("Output right"), 3 },
1204 { tr("Volume left"), 4, 3 },
1205 { tr("Output left"), 7 },
1206 });
1207 // 0xFF25: NR51
1208 regGB.append({
1209 { tr("Enable channel 1 right"), 0 },
1210 { tr("Enable channel 2 right"), 1 },
1211 { tr("Enable channel 3 right"), 2 },
1212 { tr("Enable channel 4 right"), 3 },
1213 { tr("Enable channel 1 left"), 4 },
1214 { tr("Enable channel 2 left"), 5 },
1215 { tr("Enable channel 3 left"), 6 },
1216 { tr("Enable channel 4 left"), 7 },
1217 });
1218 // 0xFF26: NR52
1219 regGB.append({
1220 { tr("Active channel 1"), 0, 1, true },
1221 { tr("Active channel 2"), 1, 1, true },
1222 { tr("Active channel 3"), 2, 1, true },
1223 { tr("Active channel 4"), 3, 1, true },
1224 { tr("Enable audio"), 7 },
1225 });
1226 // 0xFF27: Unused
1227 regGB.append(RegisterDescription());
1228 // 0xFF28: Unused
1229 regGB.append(RegisterDescription());
1230 // 0xFF29: Unused
1231 regGB.append(RegisterDescription());
1232 // 0xFF2A: Unused
1233 regGB.append(RegisterDescription());
1234 // 0xFF2B: Unused
1235 regGB.append(RegisterDescription());
1236 // 0xFF2C: Unused
1237 regGB.append(RegisterDescription());
1238 // 0xFF2D: Unused
1239 regGB.append(RegisterDescription());
1240 // 0xFF2E: Unused
1241 regGB.append(RegisterDescription());
1242 // 0xFF2F: Unused
1243 regGB.append(RegisterDescription());
1244 // 0xFF30: WAVE_0
1245 regGB.append({
1246 { tr("Sample"), 0, 4 },
1247 { tr("Sample"), 4, 4 },
1248 });
1249 // 0xFF31: WAVE_1
1250 regGB.append({
1251 { tr("Sample"), 0, 4 },
1252 { tr("Sample"), 4, 4 },
1253 });
1254 // 0xFF32: WAVE_2
1255 regGB.append({
1256 { tr("Sample"), 0, 4 },
1257 { tr("Sample"), 4, 4 },
1258 });
1259 // 0xFF33: WAVE_3
1260 regGB.append({
1261 { tr("Sample"), 0, 4 },
1262 { tr("Sample"), 4, 4 },
1263 });
1264 // 0xFF34: WAVE_4
1265 regGB.append({
1266 { tr("Sample"), 0, 4 },
1267 { tr("Sample"), 4, 4 },
1268 });
1269 // 0xFF35: WAVE_5
1270 regGB.append({
1271 { tr("Sample"), 0, 4 },
1272 { tr("Sample"), 4, 4 },
1273 });
1274 // 0xFF36: WAVE_6
1275 regGB.append({
1276 { tr("Sample"), 0, 4 },
1277 { tr("Sample"), 4, 4 },
1278 });
1279 // 0xFF37: WAVE_7
1280 regGB.append({
1281 { tr("Sample"), 0, 4 },
1282 { tr("Sample"), 4, 4 },
1283 });
1284 // 0xFF38: WAVE_8
1285 regGB.append({
1286 { tr("Sample"), 0, 4 },
1287 { tr("Sample"), 4, 4 },
1288 });
1289 // 0xFF39: WAVE_9
1290 regGB.append({
1291 { tr("Sample"), 0, 4 },
1292 { tr("Sample"), 4, 4 },
1293 });
1294 // 0xFF3A: WAVE_A
1295 regGB.append({
1296 { tr("Sample"), 0, 4 },
1297 { tr("Sample"), 4, 4 },
1298 });
1299 // 0xFF3B: WAVE_B
1300 regGB.append({
1301 { tr("Sample"), 0, 4 },
1302 { tr("Sample"), 4, 4 },
1303 });
1304 // 0xFF3C: WAVE_C
1305 regGB.append({
1306 { tr("Sample"), 0, 4 },
1307 { tr("Sample"), 4, 4 },
1308 });
1309 // 0xFF3D: WAVE_D
1310 regGB.append({
1311 { tr("Sample"), 0, 4 },
1312 { tr("Sample"), 4, 4 },
1313 });
1314 // 0xFF3E: WAVE_E
1315 regGB.append({
1316 { tr("Sample"), 0, 4 },
1317 { tr("Sample"), 4, 4 },
1318 });
1319 // 0xFF3F: WAVE_F
1320 regGB.append({
1321 { tr("Sample"), 0, 4 },
1322 { tr("Sample"), 4, 4 },
1323 });
1324 // 0xFF40: LCDC
1325 regGB.append({
1326 { tr("Background enable/priority"), 1 },
1327 { tr("Enable sprites"), 1 },
1328 { tr("Double-height sprites"), 2 },
1329 { tr("Background tile map"), 3, 1, {
1330 tr("0x9800 – 0x9BFF"),
1331 tr("0x9C00 – 0x9FFF"),
1332 } },
1333 { tr("Background tile data"), 4, 1, {
1334 tr("0x8800 – 0x87FF"),
1335 tr("0x8000 – 0x8FFF"),
1336 } },
1337 { tr("Enable window"), 5 },
1338 { tr("Window tile map"), 6, 1, {
1339 tr("0x9800 – 0x9BFF"),
1340 tr("0x9C00 – 0x9FFF"),
1341 } },
1342 { tr("Enable LCD"), 7 },
1343 });
1344 // 0xFF41: STAT
1345 regGB.append({
1346 { tr("Mode"), 0, 2, {
1347 tr("0: HBlank"),
1348 tr("1: VBlank"),
1349 tr("2: OAM scan"),
1350 tr("3: HDraw"),
1351 }, true },
1352 { tr("In LYC"), 2, 1, true },
1353 { tr("Enable HBlank (mode 0) IRQ"), 3 },
1354 { tr("Enable VBlank (mode 1) IRQ"), 4 },
1355 { tr("Enable OAM (mode 2) IRQ"), 4 },
1356 { tr("Enable LYC IRQ"), 4 },
1357 });
1358 // 0xFF42: SCY
1359 regGB.append({
1360 { tr("Vertical offset"), 0, 8 },
1361 });
1362 // 0xFF43: SCX
1363 regGB.append({
1364 { tr("Horizontal offset"), 0, 8 },
1365 });
1366 // 0xFF44: LY
1367 regGB.append({
1368 { tr("Current Y coordinate"), 0, 8, true },
1369 });
1370 // 0xFF45: LYC
1371 regGB.append({
1372 { tr("Comparison Y coordinate"), 0, 8 },
1373 });
1374 // 0xFF46: DMA
1375 regGB.append({
1376 { tr("Start upper byte"), 0, 8 },
1377 });
1378 // 0xFF47: BGP
1379 regGB.append({
1380 { tr("Color 0 shade"), 0, 2 },
1381 { tr("Color 1 shade"), 2, 2 },
1382 { tr("Color 2 shade"), 4, 2 },
1383 { tr("Color 3 shade"), 6, 2 },
1384 });
1385 // 0xFF48: OBP0
1386 regGB.append({
1387 { tr("Color 0 shade"), 0, 2 },
1388 { tr("Color 1 shade"), 2, 2 },
1389 { tr("Color 2 shade"), 4, 2 },
1390 { tr("Color 3 shade"), 6, 2 },
1391 });
1392 // 0xFF49: OBP1
1393 regGB.append({
1394 { tr("Color 0 shade"), 0, 2 },
1395 { tr("Color 1 shade"), 2, 2 },
1396 { tr("Color 2 shade"), 4, 2 },
1397 { tr("Color 3 shade"), 6, 2 },
1398 });
1399 // 0xFF4A: WY
1400 regGB.append({
1401 { tr("Vertical offset"), 0, 8 },
1402 });
1403 // 0xFF4B: WX
1404 regGB.append({
1405 { tr("Horizontal offset"), 0, 8 },
1406 });
1407 // 0xFF4C: KEY0
1408 regGB.append(RegisterDescription());
1409 // 0xFF4D: KEY1
1410 regGB.append({
1411 { tr("Prepare to switch speed"), 0 },
1412 { tr("Double speed"), 7, 1, true },
1413 });
1414 // 0xFF4E: Unknown/unused
1415 regGB.append(RegisterDescription());
1416 // 0xFF4F: VBK
1417 regGB.append({
1418 { tr("VRAM bank"), 0 },
1419 });
1420 // 0xFF50: BANK
1421 regGB.append(RegisterDescription());
1422 // 0xFF51: HDMA1
1423 regGB.append({
1424 { tr("Source (high)"), 0, 8 },
1425 });
1426 // 0xFF52: HDMA2
1427 regGB.append({
1428 { tr("Source (low)"), 0, 8 },
1429 });
1430 // 0xFF53: HDMA3
1431 regGB.append({
1432 { tr("Destination (high)"), 0, 8 },
1433 });
1434 // 0xFF54: HDMA4
1435 regGB.append({
1436 { tr("Destination (low)"), 0, 8 },
1437 });
1438 // 0xFF55: HDMA5
1439 regGB.append({
1440 { tr("Length"), 0, 7 },
1441 { tr("Timing"), 7, 1, {
1442 tr("Immediate"),
1443 tr("HBlank"),
1444 } },
1445 });
1446 // 0xFF56: RP
1447 regGB.append({
1448 { tr("Write bit"), 0 },
1449 { tr("Read bit"), 1, 1, true },
1450 { tr("Enable"), 6, 2, {
1451 tr("Disable"),
1452 tr("Unknown"),
1453 tr("Unknown"),
1454 tr("Enable"),
1455 } },
1456 });
1457 // 0xFF57: Unknown/unused
1458 regGB.append(RegisterDescription());
1459 // 0xFF58: Unknown/unused
1460 regGB.append(RegisterDescription());
1461 // 0xFF59: Unknown/unused
1462 regGB.append(RegisterDescription());
1463 // 0xFF5A: Unknown/unused
1464 regGB.append(RegisterDescription());
1465 // 0xFF5B: Unknown/unused
1466 regGB.append(RegisterDescription());
1467 // 0xFF5C: Unknown/unused
1468 regGB.append(RegisterDescription());
1469 // 0xFF5D: Unknown/unused
1470 regGB.append(RegisterDescription());
1471 // 0xFF5E: Unknown/unused
1472 regGB.append(RegisterDescription());
1473 // 0xFF5F: Unknown/unused
1474 regGB.append(RegisterDescription());
1475 // 0xFF60: Unknown/unused
1476 regGB.append(RegisterDescription());
1477 // 0xFF61: Unknown/unused
1478 regGB.append(RegisterDescription());
1479 // 0xFF62: Unknown/unused
1480 regGB.append(RegisterDescription());
1481 // 0xFF63: Unknown/unused
1482 regGB.append(RegisterDescription());
1483 // 0xFF64: Unknown/unused
1484 regGB.append(RegisterDescription());
1485 // 0xFF65: Unknown/unused
1486 regGB.append(RegisterDescription());
1487 // 0xFF66: Unknown/unused
1488 regGB.append(RegisterDescription());
1489 // 0xFF67: Unknown/unused
1490 regGB.append(RegisterDescription());
1491 // 0xFF68: BCPS
1492 regGB.append({
1493 { tr("Current index"), 0, 6 },
1494 { tr("Auto-increment"), 7 },
1495 });
1496 // 0xFF69: BCPD
1497 regGB.append({
1498 { tr("Red"), 0, 5 },
1499 { tr("Green (low)"), 5, 3 },
1500 { tr("Green (high)"), 0, 2 },
1501 { tr("Blue"), 2, 5 },
1502 });
1503 // 0xFF6A: OCPS
1504 regGB.append({
1505 { tr("Current index"), 0, 6 },
1506 { tr("Auto-increment"), 7 },
1507 });
1508 // 0xFF6B: OCPD
1509 regGB.append({
1510 { tr("Red"), 0, 5 },
1511 { tr("Green (low)"), 5, 3 },
1512 { tr("Green (high)"), 0, 2 },
1513 { tr("Blue"), 2, 5 },
1514 });
1515 // 0xFF6C: OPRI
1516 regGB.append({
1517 { tr("Sprite ordering"), 0, 1, {
1518 tr("OAM order"),
1519 tr("x coordinate sorting"),
1520 } },
1521 });
1522 // 0xFF6D: Unknown/unused
1523 regGB.append(RegisterDescription());
1524 // 0xFF6E: Unknown/unused
1525 regGB.append(RegisterDescription());
1526 // 0xFF6F: Unknown/unused
1527 regGB.append(RegisterDescription());
1528 // 0xFF70: SVBK
1529 regGB.append({
1530 { tr("WRAM bank"), 0, 3 },
1531 });
1532 // 0xFF71: Unknown/unused
1533 regGB.append(RegisterDescription());
1534 // 0xFF72: Unknown
1535 regGB.append(RegisterDescription());
1536 // 0xFF73: Unknown
1537 regGB.append(RegisterDescription());
1538 // 0xFF74: Unknown
1539 regGB.append(RegisterDescription());
1540 // 0xFF75: Unknown
1541 regGB.append(RegisterDescription());
1542 // 0xFF76: PCM12
1543 regGB.append(RegisterDescription());
1544 // 0xFF77: PCM34
1545 regGB.append(RegisterDescription());
1546 for (int i = 0x78; i < 0xFF; ++i) {
1547 // Unused
1548 regGB.append(RegisterDescription());
1549 }
1550 // 0xFFFF: IE
1551 regGB.append({
1552 { tr("VBlank"), 0 },
1553 { tr("LCD STAT"), 1 },
1554 { tr("Timer"), 2 },
1555 { tr("Serial"), 3 },
1556 { tr("Joypad"), 4 },
1557 });
1558 s_registers[mPLATFORM_GB] = regGB;
1559#endif
1560 return s_registers[platform];
1561}
1562
1563IOViewer::IOViewer(std::shared_ptr<CoreController> controller, QWidget* parent)
1564 : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
1565 , m_controller(controller)
1566{
1567 m_ui.setupUi(this);
1568 const char* const* regs;
1569 unsigned maxRegs;
1570 switch (controller->platform()) {
1571#ifdef M_CORE_GB
1572 case mPLATFORM_GB:
1573 regs = GBIORegisterNames;
1574 maxRegs = GB_REG_MAX;
1575 m_base = GB_BASE_IO;
1576 m_width = 0;
1577 break;
1578#endif
1579#ifdef M_CORE_GBA
1580 case mPLATFORM_GBA:
1581 regs = GBAIORegisterNames;
1582 maxRegs = REG_MAX >> 1;
1583 m_base = BASE_IO;
1584 m_width = 1;
1585 break;
1586#endif
1587 }
1588
1589 for (unsigned i = 0; i < maxRegs; ++i) {
1590 const char* reg = regs[i];
1591 if (!reg) {
1592 continue;
1593 }
1594 m_ui.regSelect->addItem("0x" + QString("%1: %2").arg((i << m_width) + m_base, 4, 16, QChar('0')).toUpper().arg(reg), i << m_width);
1595 }
1596
1597 const QFont font = GBAApp::app()->monospaceFont();
1598 m_ui.regValue->setFont(font);
1599
1600 connect(m_ui.buttonBox, &QDialogButtonBox::clicked, this, &IOViewer::buttonPressed);
1601 connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close);
1602 connect(m_ui.regSelect, &QComboBox::currentTextChanged,
1603 this, static_cast<void (IOViewer::*)()>(&IOViewer::selectRegister));
1604
1605 m_b[0] = m_ui.b0;
1606 m_b[1] = m_ui.b1;
1607 m_b[2] = m_ui.b2;
1608 m_b[3] = m_ui.b3;
1609 m_b[4] = m_ui.b4;
1610 m_b[5] = m_ui.b5;
1611 m_b[6] = m_ui.b6;
1612 m_b[7] = m_ui.b7;
1613 m_b[8] = m_ui.b8;
1614 m_b[9] = m_ui.b9;
1615 m_b[10] = m_ui.bA;
1616 m_b[11] = m_ui.bB;
1617 m_b[12] = m_ui.bC;
1618 m_b[13] = m_ui.bD;
1619 m_b[14] = m_ui.bE;
1620 m_b[15] = m_ui.bF;
1621
1622 QWidget* l[16] = {
1623 m_ui.l0,
1624 m_ui.l1,
1625 m_ui.l2,
1626 m_ui.l3,
1627 m_ui.l4,
1628 m_ui.l5,
1629 m_ui.l6,
1630 m_ui.l7,
1631 m_ui.l8,
1632 m_ui.l9,
1633 m_ui.lA,
1634 m_ui.lB,
1635 m_ui.lC,
1636 m_ui.lD,
1637 m_ui.lE,
1638 m_ui.lF
1639 };
1640
1641 for (int i = 0; i < (8 << m_width); ++i) {
1642 connect(m_b[i], &QAbstractButton::toggled, this, &IOViewer::bitFlipped);
1643 }
1644
1645 for (int i = (8 << m_width) ; i < 16; ++i) {
1646 m_b[i]->hide();
1647 l[i]->hide();
1648 }
1649
1650 selectRegister(0);
1651
1652 connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
1653}
1654
1655void IOViewer::updateRegister() {
1656 {
1657 CoreController::Interrupter interrupter(m_controller);
1658 mCore* core = m_controller->thread()->core;
1659 switch (m_width) {
1660 case 0:
1661 m_value = core->rawRead8(core, m_base + m_register, -1);
1662 break;
1663 case 1:
1664 m_value = core->rawRead16(core, m_base + m_register, -1);
1665 break;
1666 }
1667 }
1668
1669 for (int i = 0; i < (8 << m_width); ++i) {
1670 QSignalBlocker blocker(m_b[i]);
1671 m_b[i]->setChecked(m_value & (1 << i));
1672 }
1673 m_ui.regValue->setText("0x" + QString("%1").arg(m_value, (2 << m_width), 16, QChar('0')).toUpper());
1674 emit valueChanged();
1675}
1676
1677void IOViewer::bitFlipped() {
1678 m_value = 0;
1679 for (int i = 0; i < (8 << m_width); ++i) {
1680 m_value |= m_b[i]->isChecked() << i;
1681 }
1682 m_ui.regValue->setText("0x" + QString("%1").arg(m_value, (2 << m_width), 16, QChar('0')).toUpper());
1683 emit valueChanged();
1684}
1685
1686void IOViewer::writeback() {
1687 {
1688 CoreController::Interrupter interrupter(m_controller);
1689 GBAIOWrite(static_cast<GBA*>(m_controller->thread()->core->board), m_register, m_value);
1690 }
1691 updateRegister();
1692}
1693
1694void IOViewer::selectRegister(int address) {
1695 m_register = address;
1696 QGridLayout* box = static_cast<QGridLayout*>(m_ui.regDescription->layout());
1697 if (box) {
1698 // I can't believe there isn't a real way to do this...
1699 while (!box->isEmpty()) {
1700 QLayoutItem* item = box->takeAt(0);
1701 if (item->widget()) {
1702 delete item->widget();
1703 }
1704 delete item;
1705 }
1706 } else {
1707 box = new QGridLayout;
1708 }
1709 if (registerDescriptions(m_controller->platform()).count() <= address >> m_width) {
1710 return;
1711 }
1712 const RegisterDescription& description = registerDescriptions(m_controller->platform()).at(address >> m_width);
1713 int i = 0;
1714 for (const RegisterItem& ri : description) {
1715 QLabel* label = new QLabel(ri.description);
1716 box->addWidget(label, i, 0);
1717 if (ri.size == 1) {
1718 QCheckBox* check = new QCheckBox;
1719 check->setEnabled(!ri.readonly);
1720 box->addWidget(check, i, 1, Qt::AlignRight);
1721 connect(check, &QAbstractButton::toggled, m_b[ri.start], &QAbstractButton::setChecked);
1722 connect(this, &IOViewer::valueChanged, check, [check, this, &ri] {
1723 QSignalBlocker blocker(check);
1724 check->setChecked(bool(m_value & (1 << ri.start)));
1725 });
1726 } else if (ri.items.empty()) {
1727 QSpinBox* sbox = new QSpinBox;
1728 sbox->setEnabled(!ri.readonly);
1729 sbox->setMaximum((1 << ri.size) - 1);
1730 sbox->setAccelerated(true);
1731 box->addWidget(sbox, i, 1, Qt::AlignRight);
1732
1733 connect(sbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this, &ri](int v) {
1734 for (int o = 0; o < ri.size; ++o) {
1735 QSignalBlocker blocker(m_b[o + ri.start]);
1736 m_b[o + ri.start]->setChecked(v & (1 << o));
1737 }
1738 bitFlipped();
1739 });
1740
1741 connect(this, &IOViewer::valueChanged, sbox, [sbox, this, &ri]() {
1742 QSignalBlocker blocker(sbox);
1743 int v = (m_value >> ri.start) & ((1 << ri.size) - 1);
1744 sbox->setValue(v);
1745 });
1746 } else {
1747 QComboBox* cbox = new QComboBox;
1748 cbox->setEnabled(!ri.readonly);
1749 ++i;
1750 box->addWidget(cbox, i, 0, 1, 2, Qt::AlignRight);
1751 for (int o = 0; o < 1 << ri.size; ++o) {
1752 if (ri.items.at(o).isNull()) {
1753 continue;
1754 }
1755 cbox->addItem(ri.items.at(o), o);
1756 }
1757
1758 connect(cbox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [cbox, this, &ri](int index) {
1759 unsigned v = cbox->itemData(index).toUInt();
1760 for (int o = 0; o < ri.size; ++o) {
1761 QSignalBlocker blocker(m_b[o + ri.start]);
1762 m_b[o + ri.start]->setChecked(v & (1 << o));
1763 }
1764 bitFlipped();
1765 });
1766
1767 connect(this, &IOViewer::valueChanged, cbox, [cbox, this, &ri]() {
1768 QSignalBlocker blocker(cbox);
1769 unsigned v = (m_value >> ri.start) & ((1 << ri.size) - 1);
1770 for (int i = 0; i < 1 << ri.size; ++i) {
1771 if (cbox->itemData(i) == v) {
1772 cbox->setCurrentIndex(i);
1773 break;
1774 }
1775 }
1776 });
1777 }
1778 ++i;
1779 }
1780 m_ui.regDescription->setLayout(box);
1781 updateRegister();
1782}
1783
1784void IOViewer::selectRegister() {
1785 selectRegister(m_ui.regSelect->currentData().toUInt());
1786}
1787
1788void IOViewer::buttonPressed(QAbstractButton* button) {
1789 switch (m_ui.buttonBox->standardButton(button)) {
1790 case QDialogButtonBox::Reset:
1791 updateRegister();
1792 break;
1793 case QDialogButtonBox::Apply:
1794 writeback();
1795 break;
1796 default:
1797 break;
1798 }
1799}