openMSX
VDPCmdEngine.cc
Go to the documentation of this file.
1/*
2TODO:
3- How is 64K VRAM handled?
4 VRAM size is never inspected by the command engine.
5 How does a real MSX handle it?
6 Mirroring of first 64K or empty memory space?
7- How is extended VRAM handled?
8 The current VDP implementation does not support it.
9 Since it is not accessed by the renderer, it is possible allocate
10 it here.
11 But maybe it makes more sense to have all RAM managed by the VDP?
12- Currently all VRAM access is done at the start time of a series of
13 updates: currentTime is not increased until the very end of the sync
14 method. It should of course be updated after every read and write.
15 An acceptable approximation would be an update after every pixel/byte
16 operation.
17*/
18
19/*
20 About NX, NY
21 - for block commands NX = 0 is equivalent to NX = 512 (TODO recheck this)
22 and NY = 0 is equivalent to NY = 1024
23 - when NX or NY is too large and the VDP command hits the border, the
24 following happens:
25 - when the left or right border is hit, the line terminates
26 - when the top border is hit (line 0) the command terminates
27 - when the bottom border (line 511 or 1023) the command continues
28 (wraps to the top)
29 - in 512 lines modes (e.g. screen 7) NY is NOT limited to 512, so when
30 NY > 512, part of the screen is overdrawn twice
31 - in 256 columns modes (e.g. screen 5) when "SX/DX >= 256", only 1 element
32 (pixel or byte) is processed per horizontal line. The real x-coordinate
33 is "SX/DX & 255".
34*/
35
36#include "VDPCmdEngine.hh"
37
38#include "VDPVRAM.hh"
39
40#include "EmuTime.hh"
41#include "serialize.hh"
42
43#include "unreachable.hh"
44
45#include <algorithm>
46#include <array>
47#include <cassert>
48#include <iostream>
49#include <string_view>
50
51namespace openmsx {
52
53using namespace VDPAccessSlots;
54
55template<typename Mode>
56static constexpr unsigned clipNX_1_pixel(unsigned DX, unsigned NX, byte ARG)
57{
58 if (DX >= Mode::PIXELS_PER_LINE) [[unlikely]] {
59 return 1;
60 }
61 NX = NX ? NX : Mode::PIXELS_PER_LINE;
62 return (ARG & VDPCmdEngine::DIX)
63 ? std::min(NX, DX + 1)
64 : std::min(NX, Mode::PIXELS_PER_LINE - DX);
65}
66
67template<typename Mode>
68static constexpr unsigned clipNX_1_byte(unsigned DX, unsigned NX, byte ARG)
69{
70 constexpr unsigned BYTES_PER_LINE =
71 Mode::PIXELS_PER_LINE >> Mode::PIXELS_PER_BYTE_SHIFT;
72
73 DX >>= Mode::PIXELS_PER_BYTE_SHIFT;
74 if (BYTES_PER_LINE <= DX) [[unlikely]] {
75 return 1;
76 }
77 NX >>= Mode::PIXELS_PER_BYTE_SHIFT;
78 NX = NX ? NX : BYTES_PER_LINE;
79 return (ARG & VDPCmdEngine::DIX)
80 ? std::min(NX, DX + 1)
81 : std::min(NX, BYTES_PER_LINE - DX);
82}
83
84template<typename Mode>
85static constexpr unsigned clipNX_2_pixel(unsigned SX, unsigned DX, unsigned NX, byte ARG)
86{
87 if ((SX >= Mode::PIXELS_PER_LINE) ||
88 (DX >= Mode::PIXELS_PER_LINE)) [[unlikely]] {
89 return 1;
90 }
91 NX = NX ? NX : Mode::PIXELS_PER_LINE;
92 return (ARG & VDPCmdEngine::DIX)
93 ? std::min(NX, std::min(SX, DX) + 1)
94 : std::min(NX, Mode::PIXELS_PER_LINE - std::max(SX, DX));
95}
96
97template<typename Mode>
98static constexpr unsigned clipNX_2_byte(unsigned SX, unsigned DX, unsigned NX, byte ARG)
99{
100 constexpr unsigned BYTES_PER_LINE =
101 Mode::PIXELS_PER_LINE >> Mode::PIXELS_PER_BYTE_SHIFT;
102
103 SX >>= Mode::PIXELS_PER_BYTE_SHIFT;
104 DX >>= Mode::PIXELS_PER_BYTE_SHIFT;
105 if ((BYTES_PER_LINE <= SX) ||
106 (BYTES_PER_LINE <= DX)) [[unlikely]] {
107 return 1;
108 }
109 NX >>= Mode::PIXELS_PER_BYTE_SHIFT;
110 NX = NX ? NX : BYTES_PER_LINE;
111 return (ARG & VDPCmdEngine::DIX)
112 ? std::min(NX, std::min(SX, DX) + 1)
113 : std::min(NX, BYTES_PER_LINE - std::max(SX, DX));
114}
115
116static constexpr unsigned clipNY_1(unsigned DY, unsigned NY, byte ARG)
117{
118 NY = NY ? NY : 1024;
119 return (ARG & VDPCmdEngine::DIY) ? std::min(NY, DY + 1) : NY;
120}
121
122static constexpr unsigned clipNY_2(unsigned SY, unsigned DY, unsigned NY, byte ARG)
123{
124 NY = NY ? NY : 1024;
125 return (ARG & VDPCmdEngine::DIY) ? std::min(NY, std::min(SY, DY) + 1) : NY;
126}
127
128
129//struct IncrByteAddr4;
130//struct IncrByteAddr5;
131//struct IncrByteAddr6;
132//struct IncrByteAddr7;
133//struct IncrPixelAddr4;
134//struct IncrPixelAddr5;
135//struct IncrPixelAddr6;
136//struct IncrMask4;
137//struct IncrMask5;
138//struct IncrMask7;
139//struct IncrShift4;
140//struct IncrShift5;
141//struct IncrShift7;
142//using IncrPixelAddr7 = IncrByteAddr7;
143//using IncrMask6 = IncrMask4;
144//using IncrShift6 = IncrShift4;
145
146
147template<typename LogOp> static void psetFast(
148 EmuTime::param time, VDPVRAM& vram, unsigned addr,
149 byte color, byte mask, LogOp op)
150{
151 byte src = vram.cmdWriteWindow.readNP(addr);
152 op(time, vram, addr, src, color, mask);
153}
154
158{
159 //using IncrByteAddr = IncrByteAddr4;
160 //using IncrPixelAddr = IncrPixelAddr4;
161 //using IncrMask = IncrMask4;
162 //using IncrShift = IncrShift4;
163 static constexpr byte COLOR_MASK = 0x0F;
164 static constexpr byte PIXELS_PER_BYTE = 2;
165 static constexpr byte PIXELS_PER_BYTE_SHIFT = 1;
166 static constexpr unsigned PIXELS_PER_LINE = 256;
167 static unsigned addressOf(unsigned x, unsigned y, bool extVRAM);
168 static byte point(const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM);
169 template<typename LogOp>
170 static void pset(EmuTime::param time, VDPVRAM& vram,
171 unsigned x, unsigned addr, byte src, byte color, LogOp op);
172 static byte duplicate(byte color);
173};
174
176 unsigned x, unsigned y, bool extVRAM)
177{
178 if (!extVRAM) [[likely]] {
179 return ((y & 1023) << 7) | ((x & 255) >> 1);
180 } else {
181 return ((y & 511) << 7) | ((x & 255) >> 1) | 0x20000;
182 }
183}
184
186 const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM)
187{
188 return (vram.cmdReadWindow.readNP(addressOf(x, y, extVRAM))
189 >> (((~x) & 1) << 2)) & 15;
190}
191
192template<typename LogOp>
194 EmuTime::param time, VDPVRAM& vram, unsigned x, unsigned addr,
195 byte src, byte color, LogOp op)
196{
197 auto sh = byte(((~x) & 1) << 2);
198 op(time, vram, addr, src, byte(color << sh), ~byte(15 << sh));
199}
200
201inline byte Graphic4Mode::duplicate(byte color)
202{
203 assert((color & 0xF0) == 0);
204 return byte(color | (color << 4));
205}
206
210{
211 //using IncrByteAddr = IncrByteAddr5;
212 //using IncrPixelAddr = IncrPixelAddr5;
213 //using IncrMask = IncrMask5;
214 //using IncrShift = IncrShift5;
215 static constexpr byte COLOR_MASK = 0x03;
216 static constexpr byte PIXELS_PER_BYTE = 4;
217 static constexpr byte PIXELS_PER_BYTE_SHIFT = 2;
218 static constexpr unsigned PIXELS_PER_LINE = 512;
219 static unsigned addressOf(unsigned x, unsigned y, bool extVRAM);
220 static byte point(const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM);
221 template<typename LogOp>
222 static void pset(EmuTime::param time, VDPVRAM& vram,
223 unsigned x, unsigned addr, byte src, byte color, LogOp op);
224 static byte duplicate(byte color);
225};
226
228 unsigned x, unsigned y, bool extVRAM)
229{
230 if (!extVRAM) [[likely]] {
231 return ((y & 1023) << 7) | ((x & 511) >> 2);
232 } else {
233 return ((y & 511) << 7) | ((x & 511) >> 2) | 0x20000;
234 }
235}
236
238 const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM)
239{
240 return (vram.cmdReadWindow.readNP(addressOf(x, y, extVRAM))
241 >> (((~x) & 3) << 1)) & 3;
242}
243
244template<typename LogOp>
246 EmuTime::param time, VDPVRAM& vram, unsigned x, unsigned addr,
247 byte src, byte color, LogOp op)
248{
249 auto sh = byte(((~x) & 3) << 1);
250 op(time, vram, addr, src, byte(color << sh), ~byte(3 << sh));
251}
252
253inline byte Graphic5Mode::duplicate(byte color)
254{
255 assert((color & 0xFC) == 0);
256 color |= color << 2;
257 color |= color << 4;
258 return color;
259}
260
264{
265 //using IncrByteAddr = IncrByteAddr6;
266 //using IncrPixelAddr = IncrPixelAddr6;
267 //using IncrMask = IncrMask6;
268 //using IncrShift = IncrShift6;
269 static constexpr byte COLOR_MASK = 0x0F;
270 static constexpr byte PIXELS_PER_BYTE = 2;
271 static constexpr byte PIXELS_PER_BYTE_SHIFT = 1;
272 static constexpr unsigned PIXELS_PER_LINE = 512;
273 static unsigned addressOf(unsigned x, unsigned y, bool extVRAM);
274 static byte point(const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM);
275 template<typename LogOp>
276 static void pset(EmuTime::param time, VDPVRAM& vram,
277 unsigned x, unsigned addr, byte src, byte color, LogOp op);
278 static byte duplicate(byte color);
279};
280
282 unsigned x, unsigned y, bool extVRAM)
283{
284 if (!extVRAM) [[likely]] {
285 return ((x & 2) << 15) | ((y & 511) << 7) | ((x & 511) >> 2);
286 } else {
287 return 0x20000 | ((y & 511) << 7) | ((x & 511) >> 2);
288 }
289}
290
292 const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM)
293{
294 return (vram.cmdReadWindow.readNP(addressOf(x, y, extVRAM))
295 >> (((~x) & 1) << 2)) & 15;
296}
297
298template<typename LogOp>
300 EmuTime::param time, VDPVRAM& vram, unsigned x, unsigned addr,
301 byte src, byte color, LogOp op)
302{
303 auto sh = byte(((~x) & 1) << 2);
304 op(time, vram, addr, src, byte(color << sh), ~byte(15 << sh));
305}
306
307inline byte Graphic6Mode::duplicate(byte color)
308{
309 assert((color & 0xF0) == 0);
310 return byte(color | (color << 4));
311}
312
316{
317 //using IncrByteAddr = IncrByteAddr7;
318 //using IncrPixelAddr = IncrPixelAddr7;
319 //using IncrMask = IncrMask7;
320 //using IncrShift = IncrShift7;
321 static constexpr byte COLOR_MASK = 0xFF;
322 static constexpr byte PIXELS_PER_BYTE = 1;
323 static constexpr byte PIXELS_PER_BYTE_SHIFT = 0;
324 static constexpr unsigned PIXELS_PER_LINE = 256;
325 static unsigned addressOf(unsigned x, unsigned y, bool extVRAM);
326 static byte point(const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM);
327 template<typename LogOp>
328 static void pset(EmuTime::param time, VDPVRAM& vram,
329 unsigned x, unsigned addr, byte src, byte color, LogOp op);
330 static byte duplicate(byte color);
331};
332
334 unsigned x, unsigned y, bool extVRAM)
335{
336 if (!extVRAM) [[likely]] {
337 return ((x & 1) << 16) | ((y & 511) << 7) | ((x & 255) >> 1);
338 } else {
339 return 0x20000 | ((y & 511) << 7) | ((x & 255) >> 1);
340 }
341}
342
344 const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM)
345{
346 return vram.cmdReadWindow.readNP(addressOf(x, y, extVRAM));
347}
348
349template<typename LogOp>
351 EmuTime::param time, VDPVRAM& vram, unsigned /*x*/, unsigned addr,
352 byte src, byte color, LogOp op)
353{
354 op(time, vram, addr, src, color, 0);
355}
356
357inline byte Graphic7Mode::duplicate(byte color)
358{
359 return color;
360}
361
366{
367 //using IncrByteAddr = IncrByteAddrNonBitMap;
368 //using IncrPixelAddr = IncrPixelAddrNonBitMap;
369 //using IncrMask = IncrMaskNonBitMap;
370 //using IncrShift = IncrShiftNonBitMap;
371 static constexpr byte COLOR_MASK = 0xFF;
372 static constexpr byte PIXELS_PER_BYTE = 1;
373 static constexpr byte PIXELS_PER_BYTE_SHIFT = 0;
374 static constexpr unsigned PIXELS_PER_LINE = 256;
375 static unsigned addressOf(unsigned x, unsigned y, bool extVRAM);
376 static byte point(const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM);
377 template<typename LogOp>
378 static void pset(EmuTime::param time, VDPVRAM& vram,
379 unsigned x, unsigned addr, byte src, byte color, LogOp op);
380 static byte duplicate(byte color);
381};
382
384 unsigned x, unsigned y, bool extVRAM)
385{
386 if (!extVRAM) [[likely]] {
387 return ((y & 511) << 8) | (x & 255);
388 } else {
389 return ((y & 255) << 8) | (x & 255) | 0x20000;
390 }
391}
392
394 const VDPVRAM& vram, unsigned x, unsigned y, bool extVRAM)
395{
396 return vram.cmdReadWindow.readNP(addressOf(x, y, extVRAM));
397}
398
399template<typename LogOp>
401 EmuTime::param time, VDPVRAM& vram, unsigned /*x*/, unsigned addr,
402 byte src, byte color, LogOp op)
403{
404 op(time, vram, addr, src, color, 0);
405}
406
407inline byte NonBitmapMode::duplicate(byte color)
408{
409 return color;
410}
411
415{
416 IncrByteAddr4(unsigned x, unsigned y, int /*tx*/)
417 : addr(Graphic4Mode::addressOf(x, y, false))
418 {
419 }
420 [[nodiscard]] unsigned getAddr() const
421 {
422 return addr;
423 }
424 void step(int tx)
425 {
426 addr += (tx >> 1);
427 }
428
429private:
430 unsigned addr;
431};
432
434{
435 IncrByteAddr5(unsigned x, unsigned y, int /*tx*/)
436 : addr(Graphic5Mode::addressOf(x, y, false))
437 {
438 }
439 [[nodiscard]] unsigned getAddr() const
440 {
441 return addr;
442 }
443 void step(int tx)
444 {
445 addr += (tx >> 2);
446 }
447
448private:
449 unsigned addr;
450};
451
453{
454 IncrByteAddr7(unsigned x, unsigned y, int tx)
455 : addr(Graphic7Mode::addressOf(x, y, false))
456 , delta((tx > 0) ? 0x10000 : (0x10000 - 1))
457 , delta2((tx > 0) ? ( 0x10000 ^ (1 - 0x10000))
458 : (-0x10000 ^ (0x10000 - 1)))
459 {
460 if (x & 1) delta ^= delta2;
461 }
462 [[nodiscard]] unsigned getAddr() const
463 {
464 return addr;
465 }
466 void step(int /*tx*/)
467 {
468 addr += delta;
469 delta ^= delta2;
470 }
471
472private:
473 unsigned addr;
474 unsigned delta;
475 const unsigned delta2;
476};
477
479{
480 IncrByteAddr6(unsigned x, unsigned y, int tx)
481 : IncrByteAddr7(x >> 1, y, tx)
482 {
483 }
484};
485
489{
490 IncrPixelAddr4(unsigned x, unsigned y, int tx)
491 : addr(Graphic4Mode::addressOf(x, y, false))
492 , delta((tx == 1) ? (x & 1) : ((x & 1) - 1))
493 {
494 }
495 [[nodiscard]] unsigned getAddr() const { return addr; }
496 void step(int tx)
497 {
498 addr += delta;
499 delta ^= tx;
500 }
501private:
502 unsigned addr;
503 unsigned delta;
504};
505
507{
508 IncrPixelAddr5(unsigned x, unsigned y, int tx)
509 : addr(Graphic5Mode::addressOf(x, y, false))
510 // x | 0 | 1 | 2 | 3
511 //-----------------------
512 , c1(-(signed(x) & 1)) // | 0 | -1 | 0 | -1
513 , c2((x & 2) >> 1) // | 0 | 0 | 1 | 1
514 {
515 if (tx < 0) {
516 c1 = ~c1; // | -1 | 0 | -1 | 0
517 c2 -= 1; // | -1 | -1 | 0 | 0
518 }
519 }
520 [[nodiscard]] unsigned getAddr() const { return addr; }
521 void step(int tx)
522 {
523 addr += (c1 & c2);
524 c2 ^= (c1 & tx);
525 c1 = ~c1;
526 }
527private:
528 unsigned addr;
529 unsigned c1;
530 unsigned c2;
531};
532
534{
535 IncrPixelAddr6(unsigned x, unsigned y, int tx)
536 : addr(Graphic6Mode::addressOf(x, y, false))
537 , c1(-(signed(x) & 1))
538 , c3((tx == 1) ? unsigned(0x10000 ^ (1 - 0x10000)) // == -0x1FFFF
539 : unsigned(-0x10000 ^ (0x10000 - 1))) // == -1
540 {
541 if (tx == 1) {
542 c2 = (x & 2) ? (1 - 0x10000) : 0x10000;
543 } else {
544 c1 = ~c1;
545 c2 = (x & 2) ? -0x10000 : (0x10000 - 1);
546 }
547 }
548 [[nodiscard]] unsigned getAddr() const { return addr; }
549 void step(int /*tx*/)
550 {
551 addr += (c1 & c2);
552 c2 ^= (c1 & c3);
553 c1 = ~c1;
554 }
555private:
556 unsigned addr;
557 unsigned c1;
558 unsigned c2;
559 const unsigned c3;
560};
561
562
567{
568 IncrMask4(unsigned x, int /*tx*/)
569 : mask(byte(0x0F << ((x & 1) << 2)))
570 {
571 }
572 [[nodiscard]] byte getMask() const
573 {
574 return mask;
575 }
576 void step()
577 {
578 mask = ~mask;
579 }
580private:
581 byte mask;
582};
583
585{
586 IncrMask5(unsigned x, int tx)
587 : mask(~byte(0xC0 >> ((x & 3) << 1)))
588 , shift((tx > 0) ? 6 : 2)
589 {
590 }
591 [[nodiscard]] byte getMask() const
592 {
593 return mask;
594 }
595 void step()
596 {
597 mask = byte((mask << shift) | (mask >> (8 - shift)));
598 }
599private:
600 byte mask;
601 const byte shift;
602};
603
605{
606 IncrMask7(unsigned /*x*/, int /*tx*/) {}
607 [[nodiscard]] byte getMask() const
608 {
609 return 0;
610 }
611 void step() const {}
612};
613
614
615/* Shift between source and destination pixel for LMMM command.
616 */
618{
619 IncrShift4(unsigned sx, unsigned dx)
620 : shift(((dx - sx) & 1) * 4)
621 {
622 }
623 [[nodiscard]] byte doShift(byte color) const
624 {
625 return byte((color >> shift) | (color << shift));
626 }
627private:
628 const byte shift;
629};
630
632{
633 IncrShift5(unsigned sx, unsigned dx)
634 : shift(((dx - sx) & 3) * 2)
635 {
636 }
637 [[nodiscard]] byte doShift(byte color) const
638 {
639 return byte((color >> shift) | (color << (8 - shift)));
640 }
641private:
642 const byte shift;
643};
644
646{
647 IncrShift7(unsigned /*sx*/, unsigned /*dx*/) {}
648 [[nodiscard]] byte doShift(byte color) const
649 {
650 return color;
651 }
652};
653
654
655// Logical operations:
656
657struct DummyOp {
658 void operator()(EmuTime::param /*time*/, VDPVRAM& /*vram*/, unsigned /*addr*/,
659 byte /*src*/, byte /*color*/, byte /*mask*/) const
660 {
661 // Undefined logical operations do nothing.
662 }
663};
664
665struct ImpOp {
666 void operator()(EmuTime::param time, VDPVRAM& vram, unsigned addr,
667 byte src, byte color, byte mask) const
668 {
669 vram.cmdWrite(addr, (src & mask) | color, time);
670 }
671};
672
673struct AndOp {
674 void operator()(EmuTime::param time, VDPVRAM& vram, unsigned addr,
675 byte src, byte color, byte mask) const
676 {
677 vram.cmdWrite(addr, src & (color | mask), time);
678 }
679};
680
681struct OrOp {
682 void operator()(EmuTime::param time, VDPVRAM& vram, unsigned addr,
683 byte src, byte color, byte /*mask*/) const
684 {
685 vram.cmdWrite(addr, src | color, time);
686 }
687};
688
689struct XorOp {
690 void operator()(EmuTime::param time, VDPVRAM& vram, unsigned addr,
691 byte src, byte color, byte /*mask*/) const
692 {
693 vram.cmdWrite(addr, src ^ color, time);
694 }
695};
696
697struct NotOp {
698 void operator()(EmuTime::param time, VDPVRAM& vram, unsigned addr,
699 byte src, byte color, byte mask) const
700 {
701 vram.cmdWrite(addr, (src & mask) | ~(color | mask), time);
702 }
703};
704
705template<typename Op>
706struct TransparentOp : Op {
707 void operator()(EmuTime::param time, VDPVRAM& vram, unsigned addr,
708 byte src, byte color, byte mask) const
709 {
710 // TODO does this skip the write or re-write the original value
711 // might make a difference in case the CPU has written
712 // the same address between the command read and write
713 if (color) Op::operator()(time, vram, addr, src, color, mask);
714 }
715};
721
722
723// Commands
724
725void VDPCmdEngine::setStatusChangeTime(EmuTime::param t)
726{
727 statusChangeTime = t;
728 if ((t != EmuTime::infinity()) && executingProbe.anyObservers()) {
729 vdp.scheduleCmdSync(t);
730 }
731}
732
733void VDPCmdEngine::calcFinishTime(unsigned nx, unsigned ny, unsigned ticksPerPixel)
734{
735 if (!CMD) return;
736 if (vdp.getBrokenCmdTiming()) {
737 setStatusChangeTime(EmuTime::zero()); // will finish soon
738 return;
739 }
740
741 // Underestimation for when the command will be finished. This assumes
742 // we never have to wait for access slots and that there's no overhead
743 // per line.
744 auto t = VDP::VDPClock::duration(ticksPerPixel);
745 t *= ((nx * (ny - 1)) + (ANX - 1));
746 setStatusChangeTime(engineTime + t);
747}
748
751void VDPCmdEngine::startAbrt(EmuTime::param time)
752{
753 commandDone(time);
754}
755
758void VDPCmdEngine::startPoint(EmuTime::param time)
759{
760 vram.cmdReadWindow.setMask(0x3FFFF, ~0u << 18, time);
761 vram.cmdWriteWindow.disable(time);
762 nextAccessSlot(time);
763 setStatusChangeTime(EmuTime::zero()); // will finish soon
764}
765
766template<typename Mode>
767void VDPCmdEngine::executePoint(EmuTime::param limit)
768{
769 if (engineTime >= limit) [[unlikely]] return;
770
771 bool srcExt = (ARG & MXS) != 0;
772 if (bool doPoint = !srcExt || hasExtendedVRAM; doPoint) [[likely]] {
773 COL = Mode::point(vram, SX, SY, srcExt);
774 } else {
775 COL = 0xFF;
776 }
777 commandDone(engineTime);
778}
779
782void VDPCmdEngine::startPset(EmuTime::param time)
783{
784 vram.cmdReadWindow.disable(time);
785 vram.cmdWriteWindow.setMask(0x3FFFF, ~0u << 18, time);
786 nextAccessSlot(time);
787 setStatusChangeTime(EmuTime::zero()); // will finish soon
788 phase = 0;
789}
790
791template<typename Mode, typename LogOp>
792void VDPCmdEngine::executePset(EmuTime::param limit)
793{
794 bool dstExt = (ARG & MXD) != 0;
795 bool doPset = !dstExt || hasExtendedVRAM;
796 unsigned addr = Mode::addressOf(DX, DY, dstExt);
797
798 switch (phase) {
799 case 0:
800 if (engineTime >= limit) [[unlikely]] { phase = 0; break; }
801 if (doPset) [[likely]] {
802 tmpDst = vram.cmdWriteWindow.readNP(addr);
803 }
804 nextAccessSlot(Delta::D24); // TODO
805 [[fallthrough]];
806 case 1:
807 if (engineTime >= limit) [[unlikely]] { phase = 1; break; }
808 if (doPset) [[likely]] {
809 byte col = COL & Mode::COLOR_MASK;
810 Mode::pset(engineTime, vram, DX, addr, tmpDst, col, LogOp());
811 }
812 commandDone(engineTime);
813 break;
814 default:
816 }
817}
818
821void VDPCmdEngine::startSrch(EmuTime::param time)
822{
823 vram.cmdReadWindow.setMask(0x3FFFF, ~0u << 18, time);
824 vram.cmdWriteWindow.disable(time);
825 ASX = SX;
826 nextAccessSlot(time);
827 setStatusChangeTime(EmuTime::zero()); // we can find it any moment
828}
829
830template<typename Mode>
831void VDPCmdEngine::executeSrch(EmuTime::param limit)
832{
833 byte CL = COL & Mode::COLOR_MASK;
834 int TX = (ARG & DIX) ? -1 : 1;
835 bool AEQ = (ARG & EQ) != 0; // TODO: Do we look for "==" or "!="?
836
837 // TODO use MXS or MXD here?
838 // datasheet says MXD but MXS seems more logical
839 bool srcExt = (ARG & MXS) != 0;
840 bool doPoint = !srcExt || hasExtendedVRAM;
841 auto calculator = getSlotCalculator(limit);
842
843 while (!calculator.limitReached()) {
844 auto p = [&] () -> byte {
845 if (doPoint) [[likely]] {
846 return Mode::point(vram, ASX, SY, srcExt);
847 } else {
848 return 0xFF;
849 }
850 }();
851 if ((p == CL) ^ AEQ) {
852 status |= 0x10; // border detected
853 commandDone(calculator.getTime());
854 break;
855 }
856 ASX += TX;
857 if (ASX & Mode::PIXELS_PER_LINE) {
858 status &= 0xEF; // border not detected
859 commandDone(calculator.getTime());
860 break;
861 }
862 calculator.next(Delta::D88); // TODO
863 }
864 engineTime = calculator.getTime();
865}
866
869void VDPCmdEngine::startLine(EmuTime::param time)
870{
871 vram.cmdReadWindow.disable(time);
872 vram.cmdWriteWindow.setMask(0x3FFFF, ~0u << 18, time);
873 NY &= 1023;
874 ASX = ((NX - 1) >> 1);
875 ADX = DX;
876 ANX = 0;
877 nextAccessSlot(time);
878 setStatusChangeTime(EmuTime::zero()); // TODO can still be optimized
879 phase = 0;
880}
881
882template<typename Mode, typename LogOp>
883void VDPCmdEngine::executeLine(EmuTime::param limit)
884{
885 // See doc/line-speed.txt for some background info on the timing.
886 byte CL = COL & Mode::COLOR_MASK;
887 int TX = (ARG & DIX) ? -1 : 1;
888 int TY = (ARG & DIY) ? -1 : 1;
889 bool dstExt = (ARG & MXD) != 0;
890 bool doPset = !dstExt || hasExtendedVRAM;
891 unsigned addr = Mode::addressOf(ADX, DY, dstExt);
892 auto calculator = getSlotCalculator(limit);
893
894 switch (phase) {
895 case 0:
896loop: if (calculator.limitReached()) [[unlikely]] { phase = 0; break; }
897 if (doPset) [[likely]] {
898 tmpDst = vram.cmdWriteWindow.readNP(addr);
899 }
900 calculator.next(Delta::D24);
901 [[fallthrough]];
902 case 1: {
903 if (calculator.limitReached()) [[unlikely]] { phase = 1; break; }
904 if (doPset) [[likely]] {
905 Mode::pset(calculator.getTime(), vram, ADX, addr,
906 tmpDst, CL, LogOp());
907 }
908
909 Delta delta = Delta::D88;
910 if ((ARG & MAJ) == 0) {
911 // X-Axis is major direction.
912 ADX += TX;
913 // confirmed on real HW:
914 // - end-test happens before DY += TY
915 // - (ADX & PPL) test only happens after first pixel
916 // is drawn. And it does test with 'AND' (not with ==)
917 if (ANX++ == NX || (ADX & Mode::PIXELS_PER_LINE)) {
918 commandDone(calculator.getTime());
919 break;
920 }
921 if (ASX < NY) {
922 ASX += NX;
923 DY += TY;
924 delta = Delta::D120; // 88 + 32
925 }
926 ASX -= NY;
927 ASX &= 1023; // mask to 10 bits range
928 } else {
929 // Y-Axis is major direction.
930 // confirmed on real HW: DY += TY happens before end-test
931 DY += TY;
932 if (ASX < NY) {
933 ASX += NX;
934 ADX += TX;
935 delta = Delta::D120; // 88 + 32
936 }
937 ASX -= NY;
938 ASX &= 1023; // mask to 10 bits range
939 if (ANX++ == NX || (ADX & Mode::PIXELS_PER_LINE)) {
940 commandDone(calculator.getTime());
941 break;
942 }
943 }
944 addr = Mode::addressOf(ADX, DY, dstExt);
945 calculator.next(delta);
946 goto loop;
947 }
948 default:
950 }
951 engineTime = calculator.getTime();
952}
953
954
957template<typename Mode>
958void VDPCmdEngine::startLmmv(EmuTime::param time)
959{
960 vram.cmdReadWindow.disable(time);
961 vram.cmdWriteWindow.setMask(0x3FFFF, ~0u << 18, time);
962 NY &= 1023;
963 unsigned tmpNX = clipNX_1_pixel<Mode>(DX, NX, ARG);
964 unsigned tmpNY = clipNY_1(DY, NY, ARG);
965 ADX = DX;
966 ANX = tmpNX;
967 nextAccessSlot(time);
968 calcFinishTime(tmpNX, tmpNY, 72 + 24);
969 phase = 0;
970}
971
972template<typename Mode, typename LogOp>
973void VDPCmdEngine::executeLmmv(EmuTime::param limit)
974{
975 NY &= 1023;
976 unsigned tmpNX = clipNX_1_pixel<Mode>(DX, NX, ARG);
977 unsigned tmpNY = clipNY_1(DY, NY, ARG);
978 int TX = (ARG & DIX) ? -1 : 1;
979 int TY = (ARG & DIY) ? -1 : 1;
980 ANX = clipNX_1_pixel<Mode>(ADX, ANX, ARG);
981 byte CL = COL & Mode::COLOR_MASK;
982 bool dstExt = (ARG & MXD) != 0;
983 bool doPset = !dstExt || hasExtendedVRAM;
984 unsigned addr = Mode::addressOf(ADX, DY, dstExt);
985 auto calculator = getSlotCalculator(limit);
986
987 switch (phase) {
988 case 0:
989loop: if (calculator.limitReached()) [[unlikely]] { phase = 0; break; }
990 if (doPset) [[likely]] {
991 tmpDst = vram.cmdWriteWindow.readNP(addr);
992 }
993 calculator.next(Delta::D24);
994 [[fallthrough]];
995 case 1: {
996 if (calculator.limitReached()) [[unlikely]] { phase = 1; break; }
997 if (doPset) [[likely]] {
998 Mode::pset(calculator.getTime(), vram, ADX, addr,
999 tmpDst, CL, LogOp());
1000 }
1001 ADX += TX;
1002 Delta delta = Delta::D72;
1003 if (--ANX == 0) {
1004 delta = Delta::D136; // 72 + 64;
1005 DY += TY; --NY;
1006 ADX = DX; ANX = tmpNX;
1007 if (--tmpNY == 0) {
1008 commandDone(calculator.getTime());
1009 break;
1010 }
1011 }
1012 addr = Mode::addressOf(ADX, DY, dstExt);
1013 calculator.next(delta);
1014 goto loop;
1015 }
1016 default:
1018 }
1019 engineTime = calculator.getTime();
1020 this->calcFinishTime(tmpNX, tmpNY, 72 + 24);
1021
1022 /*
1023 if (dstExt) [[unlikely]] {
1024 bool doPset = !dstExt || hasExtendedVRAM;
1025 while (engineTime < limit) {
1026 if (doPset) [[likely]] {
1027 Mode::pset(engineTime, vram, ADX, DY,
1028 dstExt, CL, LogOp());
1029 }
1030 engineTime += delta;
1031 ADX += TX;
1032 if (--ANX == 0) {
1033 DY += TY; --NY;
1034 ADX = DX; ANX = tmpNX;
1035 if (--tmpNY == 0) {
1036 commandDone(engineTime);
1037 break;
1038 }
1039 }
1040 }
1041 } else {
1042 // fast-path, no extended VRAM
1043 CL = Mode::duplicate(CL);
1044 while (engineTime < limit) {
1045 typename Mode::IncrPixelAddr dstAddr(ADX, DY, TX);
1046 typename Mode::IncrMask dstMask(ADX, TX);
1047 EmuDuration dur = limit - engineTime;
1048 unsigned num = (delta != EmuDuration::zero())
1049 ? std::min(dur.divUp(delta), ANX)
1050 : ANX;
1051 for (auto i : xrange(num)) {
1052 byte mask = dstMask.getMask();
1053 psetFast(engineTime, vram, dstAddr.getAddr(),
1054 CL & ~mask, mask, LogOp());
1055 engineTime += delta;
1056 dstAddr.step(TX);
1057 dstMask.step();
1058 }
1059 ANX -= num;
1060 if (ANX == 0) {
1061 DY += TY;
1062 NY -= 1;
1063 ADX = DX;
1064 ANX = tmpNX;
1065 if (--tmpNY == 0) {
1066 commandDone(engineTime);
1067 break;
1068 }
1069 } else {
1070 ADX += num * TX;
1071 assert(engineTime >= limit);
1072 break;
1073 }
1074 }
1075 }
1076 */
1077}
1078
1081template<typename Mode>
1082void VDPCmdEngine::startLmmm(EmuTime::param time)
1083{
1084 vram.cmdReadWindow .setMask(0x3FFFF, ~0u << 18, time);
1085 vram.cmdWriteWindow.setMask(0x3FFFF, ~0u << 18, time);
1086 NY &= 1023;
1087 unsigned tmpNX = clipNX_2_pixel<Mode>(SX, DX, NX, ARG);
1088 unsigned tmpNY = clipNY_2(SY, DY, NY, ARG);
1089 ASX = SX;
1090 ADX = DX;
1091 ANX = tmpNX;
1092 nextAccessSlot(time);
1093 calcFinishTime(tmpNX, tmpNY, 64 + 32 + 24);
1094 phase = 0;
1095}
1096
1097template<typename Mode, typename LogOp>
1098void VDPCmdEngine::executeLmmm(EmuTime::param limit)
1099{
1100 NY &= 1023;
1101 unsigned tmpNX = clipNX_2_pixel<Mode>(SX, DX, NX, ARG);
1102 unsigned tmpNY = clipNY_2(SY, DY, NY, ARG);
1103 int TX = (ARG & DIX) ? -1 : 1;
1104 int TY = (ARG & DIY) ? -1 : 1;
1105 ANX = clipNX_2_pixel<Mode>(ASX, ADX, ANX, ARG);
1106 bool srcExt = (ARG & MXS) != 0;
1107 bool dstExt = (ARG & MXD) != 0;
1108 bool doPoint = !srcExt || hasExtendedVRAM;
1109 bool doPset = !dstExt || hasExtendedVRAM;
1110 unsigned dstAddr = Mode::addressOf(ADX, DY, dstExt);
1111 auto calculator = getSlotCalculator(limit);
1112
1113 switch (phase) {
1114 case 0:
1115loop: if (calculator.limitReached()) [[unlikely]] { phase = 0; break; }
1116 if (doPoint) [[likely]] {
1117 tmpSrc = Mode::point(vram, ASX, SY, srcExt);
1118 } else {
1119 tmpSrc = 0xFF;
1120 }
1121 calculator.next(Delta::D32);
1122 [[fallthrough]];
1123 case 1:
1124 if (calculator.limitReached()) [[unlikely]] { phase = 1; break; }
1125 if (doPset) [[likely]] {
1126 tmpDst = vram.cmdWriteWindow.readNP(dstAddr);
1127 }
1128 calculator.next(Delta::D24);
1129 [[fallthrough]];
1130 case 2: {
1131 if (calculator.limitReached()) [[unlikely]] { phase = 2; break; }
1132 if (doPset) [[likely]] {
1133 Mode::pset(calculator.getTime(), vram, ADX, dstAddr,
1134 tmpDst, tmpSrc, LogOp());
1135 }
1136 ASX += TX; ADX += TX;
1137 Delta delta = Delta::D64;
1138 if (--ANX == 0) {
1139 delta = Delta::D128; // 64 + 64
1140 SY += TY; DY += TY; --NY;
1141 ASX = SX; ADX = DX; ANX = tmpNX;
1142 if (--tmpNY == 0) {
1143 commandDone(calculator.getTime());
1144 break;
1145 }
1146 }
1147 dstAddr = Mode::addressOf(ADX, DY, dstExt);
1148 calculator.next(delta);
1149 goto loop;
1150 }
1151 default:
1153 }
1154 engineTime = calculator.getTime();
1155 this->calcFinishTime(tmpNX, tmpNY, 64 + 32 + 24);
1156
1157 /*if (srcExt || dstExt) [[unlikely]] {
1158 bool doPoint = !srcExt || hasExtendedVRAM;
1159 bool doPset = !dstExt || hasExtendedVRAM;
1160 while (engineTime < limit) {
1161 if (doPset) [[likely]] {
1162 auto p = [&] () -> byte {
1163 if (doPoint) [[likely]] {
1164 return Mode::point(vram, ASX, SY, srcExt);
1165 } else {
1166 return 0xFF;
1167 }
1168 }();
1169 Mode::pset(engineTime, vram, ADX, DY,
1170 dstExt, p, LogOp());
1171 }
1172 engineTime += delta;
1173 ASX += TX; ADX += TX;
1174 if (--ANX == 0) {
1175 SY += TY; DY += TY; --NY;
1176 ASX = SX; ADX = DX; ANX = tmpNX;
1177 if (--tmpNY == 0) {
1178 commandDone(engineTime);
1179 break;
1180 }
1181 }
1182 }
1183 } else {
1184 // fast-path, no extended VRAM
1185 while (engineTime < limit) {
1186 typename Mode::IncrPixelAddr srcAddr(ASX, SY, TX);
1187 typename Mode::IncrPixelAddr dstAddr(ADX, DY, TX);
1188 typename Mode::IncrMask dstMask(ADX, TX);
1189 typename Mode::IncrShift shift (ASX, ADX);
1190 EmuDuration dur = limit - engineTime;
1191 unsigned num = (delta != EmuDuration::zero())
1192 ? std::min(dur.divUp(delta), ANX)
1193 : ANX;
1194 for (auto i : xrange(num)) {
1195 byte p = vram.cmdReadWindow.readNP(srcAddr.getAddr());
1196 p = shift.doShift(p);
1197 byte mask = dstMask.getMask();
1198 psetFast(engineTime, vram, dstAddr.getAddr(),
1199 p & ~mask, mask, LogOp());
1200 engineTime += delta;
1201 srcAddr.step(TX);
1202 dstAddr.step(TX);
1203 dstMask.step();
1204 }
1205 ANX -= num;
1206 if (ANX == 0) {
1207 SY += TY;
1208 DY += TY;
1209 NY -= 1;
1210 ASX = SX;
1211 ADX = DX;
1212 ANX = tmpNX;
1213 if (--tmpNY == 0) {
1214 commandDone(engineTime);
1215 break;
1216 }
1217 } else {
1218 ASX += num * TX;
1219 ADX += num * TX;
1220 assert(engineTime >= limit);
1221 break;
1222 }
1223 }
1224 }
1225 */
1226}
1227
1230template<typename Mode>
1231void VDPCmdEngine::startLmcm(EmuTime::param time)
1232{
1233 vram.cmdReadWindow.setMask(0x3FFFF, ~0u << 18, time);
1234 vram.cmdWriteWindow.disable(time);
1235 NY &= 1023;
1236 unsigned tmpNX = clipNX_1_pixel<Mode>(SX, NX, ARG);
1237 ASX = SX;
1238 ANX = tmpNX;
1239 transfer = true;
1240 status |= 0x80;
1241 nextAccessSlot(time);
1242 setStatusChangeTime(EmuTime::zero());
1243}
1244
1245template<typename Mode>
1246void VDPCmdEngine::executeLmcm(EmuTime::param limit)
1247{
1248 if (!transfer) return;
1249 if (engineTime >= limit) [[unlikely]] return;
1250
1251 NY &= 1023;
1252 unsigned tmpNX = clipNX_1_pixel<Mode>(SX, NX, ARG);
1253 unsigned tmpNY = clipNY_1(SY, NY, ARG);
1254 int TX = (ARG & DIX) ? -1 : 1;
1255 int TY = (ARG & DIY) ? -1 : 1;
1256 ANX = clipNX_1_pixel<Mode>(ASX, ANX, ARG);
1257 bool srcExt = (ARG & MXS) != 0;
1258
1259 // TODO we should (most likely) perform the actual read earlier and
1260 // buffer it, and on a CPU-IO-read start the next read (just like how
1261 // regular reading from VRAM works).
1262 if (bool doPoint = !srcExt || hasExtendedVRAM; doPoint) [[likely]] {
1263 COL = Mode::point(vram, ASX, SY, srcExt);
1264 } else {
1265 COL = 0xFF;
1266 }
1267 transfer = false;
1268 ASX += TX; --ANX;
1269 if (ANX == 0) {
1270 SY += TY; --NY;
1271 ASX = SX; ANX = tmpNX;
1272 if (--tmpNY == 0) {
1273 commandDone(engineTime);
1274 }
1275 }
1276 nextAccessSlot(limit); // TODO
1277}
1278
1281template<typename Mode>
1282void VDPCmdEngine::startLmmc(EmuTime::param time)
1283{
1284 vram.cmdReadWindow.disable(time);
1285 vram.cmdWriteWindow.setMask(0x3FFFF, ~0u << 18, time);
1286 NY &= 1023;
1287 unsigned tmpNX = clipNX_1_pixel<Mode>(DX, NX, ARG);
1288 ADX = DX;
1289 ANX = tmpNX;
1290 setStatusChangeTime(EmuTime::zero());
1291 // do not set 'transfer = true', this fixes bug#1014
1292 // Baltak Rampage: characters in greetings part are one pixel offset
1293 status |= 0x80;
1294 nextAccessSlot(time);
1295}
1296
1297template<typename Mode, typename LogOp>
1298void VDPCmdEngine::executeLmmc(EmuTime::param limit)
1299{
1300 NY &= 1023;
1301 unsigned tmpNX = clipNX_1_pixel<Mode>(DX, NX, ARG);
1302 unsigned tmpNY = clipNY_1(DY, NY, ARG);
1303 int TX = (ARG & DIX) ? -1 : 1;
1304 int TY = (ARG & DIY) ? -1 : 1;
1305 ANX = clipNX_1_pixel<Mode>(ADX, ANX, ARG);
1306 bool dstExt = (ARG & MXD) != 0;
1307 bool doPset = !dstExt || hasExtendedVRAM;
1308
1309 if (transfer) {
1310 byte col = COL & Mode::COLOR_MASK;
1311 // TODO: timing is inaccurate, this executes the read and write
1312 // in the same access slot. Instead we should
1313 // - wait for a byte
1314 // - in next access slot read
1315 // - in next access slot write
1316 if (doPset) [[likely]] {
1317 unsigned addr = Mode::addressOf(ADX, DY, dstExt);
1318 tmpDst = vram.cmdWriteWindow.readNP(addr);
1319 Mode::pset(limit, vram, ADX, addr,
1320 tmpDst, col, LogOp());
1321 }
1322 // Execution is emulated as instantaneous, so don't bother
1323 // with the timing.
1324 // Note: Correct timing would require currentTime to be set
1325 // to the moment transfer becomes true.
1326 transfer = false;
1327
1328 ADX += TX; --ANX;
1329 if (ANX == 0) {
1330 DY += TY; --NY;
1331 ADX = DX; ANX = tmpNX;
1332 if (--tmpNY == 0) {
1333 commandDone(limit);
1334 }
1335 }
1336 }
1337 nextAccessSlot(limit); // inaccurate, but avoid assert
1338}
1339
1342template<typename Mode>
1343void VDPCmdEngine::startHmmv(EmuTime::param time)
1344{
1345 vram.cmdReadWindow.disable(time);
1346 vram.cmdWriteWindow.setMask(0x3FFFF, ~0u << 18, time);
1347 NY &= 1023;
1348 unsigned tmpNX = clipNX_1_byte<Mode>(DX, NX, ARG);
1349 unsigned tmpNY = clipNY_1(DY, NY, ARG);
1350 ADX = DX;
1351 ANX = tmpNX;
1352 nextAccessSlot(time);
1353 calcFinishTime(tmpNX, tmpNY, 48);
1354}
1355
1356template<typename Mode>
1357void VDPCmdEngine::executeHmmv(EmuTime::param limit)
1358{
1359 NY &= 1023;
1360 unsigned tmpNX = clipNX_1_byte<Mode>(DX, NX, ARG);
1361 unsigned tmpNY = clipNY_1(DY, NY, ARG);
1362 int TX = (ARG & DIX)
1363 ? -Mode::PIXELS_PER_BYTE : Mode::PIXELS_PER_BYTE;
1364 int TY = (ARG & DIY) ? -1 : 1;
1365 ANX = clipNX_1_byte<Mode>(
1366 ADX, ANX << Mode::PIXELS_PER_BYTE_SHIFT, ARG);
1367 bool dstExt = (ARG & MXD) != 0;
1368 bool doPset = !dstExt || hasExtendedVRAM;
1369 auto calculator = getSlotCalculator(limit);
1370
1371 while (!calculator.limitReached()) {
1372 if (doPset) [[likely]] {
1373 vram.cmdWrite(Mode::addressOf(ADX, DY, dstExt),
1374 COL, calculator.getTime());
1375 }
1376 ADX += TX;
1377 Delta delta = Delta::D48;
1378 if (--ANX == 0) {
1379 delta = Delta::D104; // 48 + 56;
1380 DY += TY; --NY;
1381 ADX = DX; ANX = tmpNX;
1382 if (--tmpNY == 0) {
1383 commandDone(calculator.getTime());
1384 break;
1385 }
1386 }
1387 calculator.next(delta);
1388 }
1389 engineTime = calculator.getTime();
1390 calcFinishTime(tmpNX, tmpNY, 48);
1391
1392 /*if (dstExt) [[unlikely]] {
1393 bool doPset = !dstExt || hasExtendedVRAM;
1394 while (engineTime < limit) {
1395 if (doPset) [[likely]] {
1396 vram.cmdWrite(Mode::addressOf(ADX, DY, dstExt),
1397 COL, engineTime);
1398 }
1399 engineTime += delta;
1400 ADX += TX;
1401 if (--ANX == 0) {
1402 DY += TY; --NY;
1403 ADX = DX; ANX = tmpNX;
1404 if (--tmpNY == 0) {
1405 commandDone(engineTime);
1406 break;
1407 }
1408 }
1409 }
1410 } else {
1411 // fast-path, no extended VRAM
1412 while (engineTime < limit) {
1413 typename Mode::IncrByteAddr dstAddr(ADX, DY, TX);
1414 EmuDuration dur = limit - engineTime;
1415 unsigned num = (delta != EmuDuration::zero())
1416 ? std::min(dur.divUp(delta), ANX)
1417 : ANX;
1418 for (auto i : xrange(num)) {
1419 vram.cmdWrite(dstAddr.getAddr(), COL,
1420 engineTime);
1421 engineTime += delta;
1422 dstAddr.step(TX);
1423 }
1424 ANX -= num;
1425 if (ANX == 0) {
1426 DY += TY;
1427 NY -= 1;
1428 ADX = DX;
1429 ANX = tmpNX;
1430 if (--tmpNY == 0) {
1431 commandDone(engineTime);
1432 break;
1433 }
1434 } else {
1435 ADX += num * TX;
1436 assert(engineTime >= limit);
1437 break;
1438 }
1439 }
1440 }
1441 */
1442}
1443
1446template<typename Mode>
1447void VDPCmdEngine::startHmmm(EmuTime::param time)
1448{
1449 vram.cmdReadWindow .setMask(0x3FFFF, ~0u << 18, time);
1450 vram.cmdWriteWindow.setMask(0x3FFFF, ~0u << 18, time);
1451 NY &= 1023;
1452 unsigned tmpNX = clipNX_2_byte<Mode>(SX, DX, NX, ARG);
1453 unsigned tmpNY = clipNY_2(SY, DY, NY, ARG);
1454 ASX = SX;
1455 ADX = DX;
1456 ANX = tmpNX;
1457 nextAccessSlot(time);
1458 calcFinishTime(tmpNX, tmpNY, 24 + 64);
1459 phase = 0;
1460}
1461
1462template<typename Mode>
1463void VDPCmdEngine::executeHmmm(EmuTime::param limit)
1464{
1465 NY &= 1023;
1466 unsigned tmpNX = clipNX_2_byte<Mode>(SX, DX, NX, ARG);
1467 unsigned tmpNY = clipNY_2(SY, DY, NY, ARG);
1468 int TX = (ARG & DIX)
1469 ? -Mode::PIXELS_PER_BYTE : Mode::PIXELS_PER_BYTE;
1470 int TY = (ARG & DIY) ? -1 : 1;
1471 ANX = clipNX_2_byte<Mode>(
1472 ASX, ADX, ANX << Mode::PIXELS_PER_BYTE_SHIFT, ARG);
1473 bool srcExt = (ARG & MXS) != 0;
1474 bool dstExt = (ARG & MXD) != 0;
1475 bool doPoint = !srcExt || hasExtendedVRAM;
1476 bool doPset = !dstExt || hasExtendedVRAM;
1477 auto calculator = getSlotCalculator(limit);
1478
1479 switch (phase) {
1480 case 0:
1481loop: if (calculator.limitReached()) [[unlikely]] { phase = 0; break; }
1482 if (doPoint) [[likely]] {
1483 tmpSrc = vram.cmdReadWindow.readNP(Mode::addressOf(ASX, SY, srcExt));
1484 } else {
1485 tmpSrc = 0xFF;
1486 }
1487 calculator.next(Delta::D24);
1488 [[fallthrough]];
1489 case 1: {
1490 if (calculator.limitReached()) [[unlikely]] { phase = 1; break; }
1491 if (doPset) [[likely]] {
1492 vram.cmdWrite(Mode::addressOf(ADX, DY, dstExt),
1493 tmpSrc, calculator.getTime());
1494 }
1495 ASX += TX; ADX += TX;
1496 Delta delta = Delta::D64;
1497 if (--ANX == 0) {
1498 delta = Delta::D128; // 64 + 64
1499 SY += TY; DY += TY; --NY;
1500 ASX = SX; ADX = DX; ANX = tmpNX;
1501 if (--tmpNY == 0) {
1502 commandDone(calculator.getTime());
1503 break;
1504 }
1505 }
1506 calculator.next(delta);
1507 goto loop;
1508 }
1509 default:
1511 }
1512 engineTime = calculator.getTime();
1513 calcFinishTime(tmpNX, tmpNY, 24 + 64);
1514
1515 /*if (srcExt || dstExt) [[unlikely]] {
1516 bool doPoint = !srcExt || hasExtendedVRAM;
1517 bool doPset = !dstExt || hasExtendedVRAM;
1518 while (engineTime < limit) {
1519 if (doPset) [[likely]] {
1520 auto p = [&] () -> byte {
1521 if (doPoint) [[likely]] {
1522 return vram.cmdReadWindow.readNP(Mode::addressOf(ASX, SY, srcExt));
1523 } else {
1524 return 0xFF;
1525 }
1526 }();
1527 vram.cmdWrite(Mode::addressOf(ADX, DY, dstExt),
1528 p, engineTime);
1529 }
1530 engineTime += delta;
1531 ASX += TX; ADX += TX;
1532 if (--ANX == 0) {
1533 SY += TY; DY += TY; --NY;
1534 ASX = SX; ADX = DX; ANX = tmpNX;
1535 if (--tmpNY == 0) {
1536 commandDone(engineTime);
1537 break;
1538 }
1539 }
1540 }
1541 } else {
1542 // fast-path, no extended VRAM
1543 while (engineTime < limit) {
1544 typename Mode::IncrByteAddr srcAddr(ASX, SY, TX);
1545 typename Mode::IncrByteAddr dstAddr(ADX, DY, TX);
1546 EmuDuration dur = limit - engineTime;
1547 unsigned num = (delta != EmuDuration::zero())
1548 ? std::min(dur.divUp(delta), ANX)
1549 : ANX;
1550 for (auto i : xrange(num)) {
1551 byte p = vram.cmdReadWindow.readNP(srcAddr.getAddr());
1552 vram.cmdWrite(dstAddr.getAddr(), p, engineTime);
1553 engineTime += delta;
1554 srcAddr.step(TX);
1555 dstAddr.step(TX);
1556 }
1557 ANX -= num;
1558 if (ANX == 0) {
1559 SY += TY;
1560 DY += TY;
1561 NY -= 1;
1562 ASX = SX;
1563 ADX = DX;
1564 ANX = tmpNX;
1565 if (--tmpNY == 0) {
1566 commandDone(engineTime);
1567 break;
1568 }
1569 } else {
1570 ASX += num * TX;
1571 ADX += num * TX;
1572 assert(engineTime >= limit);
1573 break;
1574 }
1575 }
1576 }
1577 */
1578}
1579
1582template<typename Mode>
1583void VDPCmdEngine::startYmmm(EmuTime::param time)
1584{
1585 vram.cmdReadWindow .setMask(0x3FFFF, ~0u << 18, time);
1586 vram.cmdWriteWindow.setMask(0x3FFFF, ~0u << 18, time);
1587 NY &= 1023;
1588 unsigned tmpNX = clipNX_1_byte<Mode>(DX, 512, ARG);
1589 // large enough so that it gets clipped
1590 unsigned tmpNY = clipNY_2(SY, DY, NY, ARG);
1591 ADX = DX;
1592 ANX = tmpNX;
1593 nextAccessSlot(time);
1594 calcFinishTime(tmpNX, tmpNY, 24 + 40);
1595 phase = 0;
1596}
1597
1598template<typename Mode>
1599void VDPCmdEngine::executeYmmm(EmuTime::param limit)
1600{
1601 NY &= 1023;
1602 unsigned tmpNX = clipNX_1_byte<Mode>(DX, 512, ARG);
1603 // large enough so that it gets clipped
1604 unsigned tmpNY = clipNY_2(SY, DY, NY, ARG);
1605 int TX = (ARG & DIX)
1606 ? -Mode::PIXELS_PER_BYTE : Mode::PIXELS_PER_BYTE;
1607 int TY = (ARG & DIY) ? -1 : 1;
1608 ANX = clipNX_1_byte<Mode>(ADX, 512, ARG);
1609
1610 // TODO does this use MXD for both read and write?
1611 // it says so in the datasheet, but it seems illogical
1612 // OTOH YMMM also uses DX for both read and write
1613 bool dstExt = (ARG & MXD) != 0;
1614 bool doPset = !dstExt || hasExtendedVRAM;
1615 auto calculator = getSlotCalculator(limit);
1616
1617 switch (phase) {
1618 case 0:
1619loop: if (calculator.limitReached()) [[unlikely]] { phase = 0; break; }
1620 if (doPset) [[likely]] {
1621 tmpSrc = vram.cmdReadWindow.readNP(
1622 Mode::addressOf(ADX, SY, dstExt));
1623 }
1624 calculator.next(Delta::D24);
1625 [[fallthrough]];
1626 case 1:
1627 if (calculator.limitReached()) [[unlikely]] { phase = 1; break; }
1628 if (doPset) [[likely]] {
1629 vram.cmdWrite(Mode::addressOf(ADX, DY, dstExt),
1630 tmpSrc, calculator.getTime());
1631 }
1632 ADX += TX;
1633 if (--ANX == 0) {
1634 // note: going to the next line does not take extra time
1635 SY += TY; DY += TY; --NY;
1636 ADX = DX; ANX = tmpNX;
1637 if (--tmpNY == 0) {
1638 commandDone(calculator.getTime());
1639 break;
1640 }
1641 }
1642 calculator.next(Delta::D40);
1643 goto loop;
1644 default:
1646 }
1647 engineTime = calculator.getTime();
1648 calcFinishTime(tmpNX, tmpNY, 24 + 40);
1649
1650 /*
1651 if (dstExt) [[unlikely]] {
1652 bool doPset = !dstExt || hasExtendedVRAM;
1653 while (engineTime < limit) {
1654 if (doPset) [[likely]] {
1655 byte p = vram.cmdReadWindow.readNP(
1656 Mode::addressOf(ADX, SY, dstExt));
1657 vram.cmdWrite(Mode::addressOf(ADX, DY, dstExt),
1658 p, engineTime);
1659 }
1660 engineTime += delta;
1661 ADX += TX;
1662 if (--ANX == 0) {
1663 SY += TY; DY += TY; --NY;
1664 ADX = DX; ANX = tmpNX;
1665 if (--tmpNY == 0) {
1666 commandDone(engineTime);
1667 break;
1668 }
1669 }
1670 }
1671 } else {
1672 // fast-path, no extended VRAM
1673 while (engineTime < limit) {
1674 typename Mode::IncrByteAddr srcAddr(ADX, SY, TX);
1675 typename Mode::IncrByteAddr dstAddr(ADX, DY, TX);
1676 EmuDuration dur = limit - engineTime;
1677 unsigned num = (delta != EmuDuration::zero())
1678 ? std::min(dur.divUp(delta), ANX)
1679 : ANX;
1680 for (auto i : xrange(num)) {
1681 byte p = vram.cmdReadWindow.readNP(srcAddr.getAddr());
1682 vram.cmdWrite(dstAddr.getAddr(), p, engineTime);
1683 engineTime += delta;
1684 srcAddr.step(TX);
1685 dstAddr.step(TX);
1686 }
1687 ANX -= num;
1688 if (ANX == 0) {
1689 SY += TY;
1690 DY += TY;
1691 NY -= 1;
1692 ADX = DX;
1693 ANX = tmpNX;
1694 if (--tmpNY == 0) {
1695 commandDone(engineTime);
1696 break;
1697 }
1698 } else {
1699 ADX += num * TX;
1700 assert(engineTime >= limit);
1701 break;
1702 }
1703 }
1704 }
1705 */
1706}
1707
1710template<typename Mode>
1711void VDPCmdEngine::startHmmc(EmuTime::param time)
1712{
1713 vram.cmdReadWindow.disable(time);
1714 vram.cmdWriteWindow.setMask(0x3FFFF, ~0u << 18, time);
1715 NY &= 1023;
1716 unsigned tmpNX = clipNX_1_byte<Mode>(DX, NX, ARG);
1717 ADX = DX;
1718 ANX = tmpNX;
1719 setStatusChangeTime(EmuTime::zero());
1720 // do not set 'transfer = true', see startLmmc()
1721 status |= 0x80;
1722 nextAccessSlot(time);
1723}
1724
1725template<typename Mode>
1726void VDPCmdEngine::executeHmmc(EmuTime::param limit)
1727{
1728 NY &= 1023;
1729 unsigned tmpNX = clipNX_1_byte<Mode>(DX, NX, ARG);
1730 unsigned tmpNY = clipNY_1(DY, NY, ARG);
1731 int TX = (ARG & DIX)
1732 ? -Mode::PIXELS_PER_BYTE : Mode::PIXELS_PER_BYTE;
1733 int TY = (ARG & DIY) ? -1 : 1;
1734 ANX = clipNX_1_byte<Mode>(
1735 ADX, ANX << Mode::PIXELS_PER_BYTE_SHIFT, ARG);
1736 bool dstExt = (ARG & MXD) != 0;
1737 bool doPset = !dstExt || hasExtendedVRAM;
1738
1739 if (transfer) {
1740 // TODO: timing is inaccurate. We should
1741 // - wait for a byte
1742 // - on the next access slot write that byte
1743 if (doPset) [[likely]] {
1744 vram.cmdWrite(Mode::addressOf(ADX, DY, dstExt),
1745 COL, limit);
1746 }
1747 transfer = false;
1748
1749 ADX += TX; --ANX;
1750 if (ANX == 0) {
1751 DY += TY; --NY;
1752 ADX = DX; ANX = tmpNX;
1753 if (--tmpNY == 0) {
1754 commandDone(limit);
1755 }
1756 }
1757 }
1758 nextAccessSlot(limit); // inaccurate, but avoid assert
1759}
1760
1761
1763 : vdp(vdp_), vram(vdp.getVRAM())
1764 , cmdTraceSetting(
1765 commandController, vdp_.getName() == "VDP" ? "vdpcmdtrace" :
1766 vdp_.getName() + " vdpcmdtrace", "VDP command tracing on/off",
1767 false)
1768 , cmdInProgressCallback(
1769 commandController, vdp_.getName() == "VDP" ?
1770 "vdpcmdinprogress_callback" : vdp_.getName() +
1771 " vdpcmdinprogress_callback",
1772 "Tcl proc to call when a write to the VDP command engine is "
1773 "detected while the previous command is still in progress.",
1774 "",
1775 Setting::Save::YES)
1776 , executingProbe(
1777 vdp_.getMotherBoard().getDebugger(),
1778 strCat(vdp.getName(), '.', "commandExecuting"),
1779 "Is the V99x8 VDP is currently executing a command",
1780 false)
1781 , hasExtendedVRAM(vram.getSize() == (192 * 1024))
1782{
1783}
1784
1785void VDPCmdEngine::reset(EmuTime::param time)
1786{
1787 for (int i = 14; i >= 0; --i) { // start with ABORT
1788 setCmdReg(byte(i), 0, time);
1789 }
1790 status = 0;
1791 scrMode = -1;
1792
1793 updateDisplayMode(vdp.getDisplayMode(), vdp.getCmdBit(), time);
1794}
1795
1796void VDPCmdEngine::setCmdReg(byte index, byte value, EmuTime::param time)
1797{
1798 sync(time);
1799 if (CMD && (index != 12)) {
1800 cmdInProgressCallback.execute(index, value);
1801 }
1802 switch (index) {
1803 case 0x00: // source X low
1804 SX = (SX & 0x100) | value;
1805 break;
1806 case 0x01: // source X high
1807 SX = (SX & 0x0FF) | ((value & 0x01) << 8);
1808 break;
1809 case 0x02: // source Y low
1810 SY = (SY & 0x300) | value;
1811 break;
1812 case 0x03: // source Y high
1813 SY = (SY & 0x0FF) | ((value & 0x03) << 8);
1814 break;
1815
1816 case 0x04: // destination X low
1817 DX = (DX & 0x100) | value;
1818 break;
1819 case 0x05: // destination X high
1820 DX = (DX & 0x0FF) | ((value & 0x01) << 8);
1821 break;
1822 case 0x06: // destination Y low
1823 DY = (DY & 0x300) | value;
1824 break;
1825 case 0x07: // destination Y high
1826 DY = (DY & 0x0FF) | ((value & 0x03) << 8);
1827 break;
1828
1829 // TODO is DX 9 or 10 bits, at least current implementation needs
1830 // 10 bits (otherwise texts in UR are screwed)
1831 case 0x08: // number X low
1832 NX = (NX & 0x300) | value;
1833 break;
1834 case 0x09: // number X high
1835 NX = (NX & 0x0FF) | ((value & 0x03) << 8);
1836 break;
1837 case 0x0A: // number Y low
1838 NY = (NY & 0x300) | value;
1839 break;
1840 case 0x0B: // number Y high
1841 NY = (NY & 0x0FF) | ((value & 0x03) << 8);
1842 break;
1843
1844 case 0x0C: // color
1845 COL = value;
1846 // Note: Real VDP always resets TR, but for such a short time
1847 // that the MSX won't notice it.
1848 // TODO: What happens on non-transfer commands?
1849 if (!CMD) status &= 0x7F;
1850 transfer = true;
1851 break;
1852 case 0x0D: // argument
1853 ARG = value;
1854 break;
1855 case 0x0E: // command
1856 CMD = value;
1857 executeCommand(time);
1858 break;
1859 default:
1861 }
1862}
1863
1864byte VDPCmdEngine::peekCmdReg(byte index) const
1865{
1866 switch (index) {
1867 case 0x00: return narrow_cast<byte>(SX & 0xFF);
1868 case 0x01: return narrow_cast<byte>(SX >> 8);
1869 case 0x02: return narrow_cast<byte>(SY & 0xFF);
1870 case 0x03: return narrow_cast<byte>(SY >> 8);
1871
1872 case 0x04: return narrow_cast<byte>(DX & 0xFF);
1873 case 0x05: return narrow_cast<byte>(DX >> 8);
1874 case 0x06: return narrow_cast<byte>(DY & 0xFF);
1875 case 0x07: return narrow_cast<byte>(DY >> 8);
1876
1877 case 0x08: return narrow_cast<byte>(NX & 0xFF);
1878 case 0x09: return narrow_cast<byte>(NX >> 8);
1879 case 0x0A: return narrow_cast<byte>(NY & 0xFF);
1880 case 0x0B: return narrow_cast<byte>(NY >> 8);
1881
1882 case 0x0C: return COL;
1883 case 0x0D: return ARG;
1884 case 0x0E: return CMD;
1885 default: UNREACHABLE;
1886 }
1887}
1888
1889void VDPCmdEngine::updateDisplayMode(DisplayMode mode, bool cmdBit, EmuTime::param time)
1890{
1891 int newScrMode = [&] {
1892 switch (mode.getBase()) {
1894 return 0;
1896 return 1;
1898 return 2;
1900 return 3;
1901 default:
1902 if (cmdBit) {
1903 return 4; // like GRAPHIC7, but non-planar
1904 // TODO timing might be different
1905 } else {
1906 return -1; // no commands
1907 }
1908 }
1909 }();
1910
1911 if (newScrMode != scrMode) {
1912 sync(time);
1913 if (CMD) {
1914 // VDP mode switch while command in progress
1915 if (newScrMode == -1) {
1916 // TODO: For now abort cmd in progress,
1917 // later find out what really happens.
1918 // At least CE remains high for a while,
1919 // but it is not yet clear what happens in VRAM.
1920 commandDone(time);
1921 }
1922 }
1923 scrMode = newScrMode;
1924 }
1925}
1926
1927void VDPCmdEngine::executeCommand(EmuTime::param time)
1928{
1929 // V9938 ops only work in SCREEN 5-8.
1930 // V9958 ops work in non SCREEN 5-8 when CMD bit is set
1931 if (scrMode < 0) {
1932 commandDone(time);
1933 return;
1934 }
1935
1936 // store a copy of the start registers
1937 lastSX = SX; lastSY = SY;
1938 lastDX = DX; lastDY = DY;
1939 lastNX = NX; lastNY = NY;
1940 lastCOL = COL; lastARG = ARG; lastCMD = CMD;
1941
1942 if (cmdTraceSetting.getBoolean()) {
1943 reportVdpCommand();
1944 }
1945
1946 // Start command.
1947 status |= 0x01;
1948 executingProbe = true;
1949
1950 switch ((scrMode << 4) | (CMD >> 4)) {
1951 case 0x00: case 0x10: case 0x20: case 0x30: case 0x40:
1952 case 0x01: case 0x11: case 0x21: case 0x31: case 0x41:
1953 case 0x02: case 0x12: case 0x22: case 0x32: case 0x42:
1954 case 0x03: case 0x13: case 0x23: case 0x33: case 0x43:
1955 startAbrt(time); break;
1956
1957 case 0x04: case 0x14: case 0x24: case 0x34: case 0x44:
1958 startPoint(time); break;
1959 case 0x05: case 0x15: case 0x25: case 0x35: case 0x45:
1960 startPset(time); break;
1961 case 0x06: case 0x16: case 0x26: case 0x36: case 0x46:
1962 startSrch(time); break;
1963 case 0x07: case 0x17: case 0x27: case 0x37: case 0x47:
1964 startLine(time); break;
1965
1966 case 0x08: startLmmv<Graphic4Mode >(time); break;
1967 case 0x18: startLmmv<Graphic5Mode >(time); break;
1968 case 0x28: startLmmv<Graphic6Mode >(time); break;
1969 case 0x38: startLmmv<Graphic7Mode >(time); break;
1970 case 0x48: startLmmv<NonBitmapMode>(time); break;
1971
1972 case 0x09: startLmmm<Graphic4Mode >(time); break;
1973 case 0x19: startLmmm<Graphic5Mode >(time); break;
1974 case 0x29: startLmmm<Graphic6Mode >(time); break;
1975 case 0x39: startLmmm<Graphic7Mode >(time); break;
1976 case 0x49: startLmmm<NonBitmapMode>(time); break;
1977
1978 case 0x0A: startLmcm<Graphic4Mode >(time); break;
1979 case 0x1A: startLmcm<Graphic5Mode >(time); break;
1980 case 0x2A: startLmcm<Graphic6Mode >(time); break;
1981 case 0x3A: startLmcm<Graphic7Mode >(time); break;
1982 case 0x4A: startLmcm<NonBitmapMode>(time); break;
1983
1984 case 0x0B: startLmmc<Graphic4Mode >(time); break;
1985 case 0x1B: startLmmc<Graphic5Mode >(time); break;
1986 case 0x2B: startLmmc<Graphic6Mode >(time); break;
1987 case 0x3B: startLmmc<Graphic7Mode >(time); break;
1988 case 0x4B: startLmmc<NonBitmapMode>(time); break;
1989
1990 case 0x0C: startHmmv<Graphic4Mode >(time); break;
1991 case 0x1C: startHmmv<Graphic5Mode >(time); break;
1992 case 0x2C: startHmmv<Graphic6Mode >(time); break;
1993 case 0x3C: startHmmv<Graphic7Mode >(time); break;
1994 case 0x4C: startHmmv<NonBitmapMode>(time); break;
1995
1996 case 0x0D: startHmmm<Graphic4Mode >(time); break;
1997 case 0x1D: startHmmm<Graphic5Mode >(time); break;
1998 case 0x2D: startHmmm<Graphic6Mode >(time); break;
1999 case 0x3D: startHmmm<Graphic7Mode >(time); break;
2000 case 0x4D: startHmmm<NonBitmapMode>(time); break;
2001
2002 case 0x0E: startYmmm<Graphic4Mode >(time); break;
2003 case 0x1E: startYmmm<Graphic5Mode >(time); break;
2004 case 0x2E: startYmmm<Graphic6Mode >(time); break;
2005 case 0x3E: startYmmm<Graphic7Mode >(time); break;
2006 case 0x4E: startYmmm<NonBitmapMode>(time); break;
2007
2008 case 0x0F: startHmmc<Graphic4Mode >(time); break;
2009 case 0x1F: startHmmc<Graphic5Mode >(time); break;
2010 case 0x2F: startHmmc<Graphic6Mode >(time); break;
2011 case 0x3F: startHmmc<Graphic7Mode >(time); break;
2012 case 0x4F: startHmmc<NonBitmapMode>(time); break;
2013
2014 default: UNREACHABLE;
2015 }
2016}
2017
2018void VDPCmdEngine::sync2(EmuTime::param time)
2019{
2020 switch ((scrMode << 8) | CMD) {
2021 case 0x000: case 0x100: case 0x200: case 0x300: case 0x400:
2022 case 0x001: case 0x101: case 0x201: case 0x301: case 0x401:
2023 case 0x002: case 0x102: case 0x202: case 0x302: case 0x402:
2024 case 0x003: case 0x103: case 0x203: case 0x303: case 0x403:
2025 case 0x004: case 0x104: case 0x204: case 0x304: case 0x404:
2026 case 0x005: case 0x105: case 0x205: case 0x305: case 0x405:
2027 case 0x006: case 0x106: case 0x206: case 0x306: case 0x406:
2028 case 0x007: case 0x107: case 0x207: case 0x307: case 0x407:
2029 case 0x008: case 0x108: case 0x208: case 0x308: case 0x408:
2030 case 0x009: case 0x109: case 0x209: case 0x309: case 0x409:
2031 case 0x00A: case 0x10A: case 0x20A: case 0x30A: case 0x40A:
2032 case 0x00B: case 0x10B: case 0x20B: case 0x30B: case 0x40B:
2033 case 0x00C: case 0x10C: case 0x20C: case 0x30C: case 0x40C:
2034 case 0x00D: case 0x10D: case 0x20D: case 0x30D: case 0x40D:
2035 case 0x00E: case 0x10E: case 0x20E: case 0x30E: case 0x40E:
2036 case 0x00F: case 0x10F: case 0x20F: case 0x30F: case 0x40F:
2037 case 0x010: case 0x110: case 0x210: case 0x310: case 0x410:
2038 case 0x011: case 0x111: case 0x211: case 0x311: case 0x411:
2039 case 0x012: case 0x112: case 0x212: case 0x312: case 0x412:
2040 case 0x013: case 0x113: case 0x213: case 0x313: case 0x413:
2041 case 0x014: case 0x114: case 0x214: case 0x314: case 0x414:
2042 case 0x015: case 0x115: case 0x215: case 0x315: case 0x415:
2043 case 0x016: case 0x116: case 0x216: case 0x316: case 0x416:
2044 case 0x017: case 0x117: case 0x217: case 0x317: case 0x417:
2045 case 0x018: case 0x118: case 0x218: case 0x318: case 0x418:
2046 case 0x019: case 0x119: case 0x219: case 0x319: case 0x419:
2047 case 0x01A: case 0x11A: case 0x21A: case 0x31A: case 0x41A:
2048 case 0x01B: case 0x11B: case 0x21B: case 0x31B: case 0x41B:
2049 case 0x01C: case 0x11C: case 0x21C: case 0x31C: case 0x41C:
2050 case 0x01D: case 0x11D: case 0x21D: case 0x31D: case 0x41D:
2051 case 0x01E: case 0x11E: case 0x21E: case 0x31E: case 0x41E:
2052 case 0x01F: case 0x11F: case 0x21F: case 0x31F: case 0x41F:
2053 case 0x020: case 0x120: case 0x220: case 0x320: case 0x420:
2054 case 0x021: case 0x121: case 0x221: case 0x321: case 0x421:
2055 case 0x022: case 0x122: case 0x222: case 0x322: case 0x422:
2056 case 0x023: case 0x123: case 0x223: case 0x323: case 0x423:
2057 case 0x024: case 0x124: case 0x224: case 0x324: case 0x424:
2058 case 0x025: case 0x125: case 0x225: case 0x325: case 0x425:
2059 case 0x026: case 0x126: case 0x226: case 0x326: case 0x426:
2060 case 0x027: case 0x127: case 0x227: case 0x327: case 0x427:
2061 case 0x028: case 0x128: case 0x228: case 0x328: case 0x428:
2062 case 0x029: case 0x129: case 0x229: case 0x329: case 0x429:
2063 case 0x02A: case 0x12A: case 0x22A: case 0x32A: case 0x42A:
2064 case 0x02B: case 0x12B: case 0x22B: case 0x32B: case 0x42B:
2065 case 0x02C: case 0x12C: case 0x22C: case 0x32C: case 0x42C:
2066 case 0x02D: case 0x12D: case 0x22D: case 0x32D: case 0x42D:
2067 case 0x02E: case 0x12E: case 0x22E: case 0x32E: case 0x42E:
2068 case 0x02F: case 0x12F: case 0x22F: case 0x32F: case 0x42F:
2069 case 0x030: case 0x130: case 0x230: case 0x330: case 0x430:
2070 case 0x031: case 0x131: case 0x231: case 0x331: case 0x431:
2071 case 0x032: case 0x132: case 0x232: case 0x332: case 0x432:
2072 case 0x033: case 0x133: case 0x233: case 0x333: case 0x433:
2073 case 0x034: case 0x134: case 0x234: case 0x334: case 0x434:
2074 case 0x035: case 0x135: case 0x235: case 0x335: case 0x435:
2075 case 0x036: case 0x136: case 0x236: case 0x336: case 0x436:
2076 case 0x037: case 0x137: case 0x237: case 0x337: case 0x437:
2077 case 0x038: case 0x138: case 0x238: case 0x338: case 0x438:
2078 case 0x039: case 0x139: case 0x239: case 0x339: case 0x439:
2079 case 0x03A: case 0x13A: case 0x23A: case 0x33A: case 0x43A:
2080 case 0x03B: case 0x13B: case 0x23B: case 0x33B: case 0x43B:
2081 case 0x03C: case 0x13C: case 0x23C: case 0x33C: case 0x43C:
2082 case 0x03D: case 0x13D: case 0x23D: case 0x33D: case 0x43D:
2083 case 0x03E: case 0x13E: case 0x23E: case 0x33E: case 0x43E:
2084 case 0x03F: case 0x13F: case 0x23F: case 0x33F: case 0x43F:
2086
2087 case 0x040: case 0x041: case 0x042: case 0x043:
2088 case 0x044: case 0x045: case 0x046: case 0x047:
2089 case 0x048: case 0x049: case 0x04A: case 0x04B:
2090 case 0x04C: case 0x04D: case 0x04E: case 0x04F:
2091 executePoint<Graphic4Mode>(time); break;
2092 case 0x140: case 0x141: case 0x142: case 0x143:
2093 case 0x144: case 0x145: case 0x146: case 0x147:
2094 case 0x148: case 0x149: case 0x14A: case 0x14B:
2095 case 0x14C: case 0x14D: case 0x14E: case 0x14F:
2096 executePoint<Graphic5Mode>(time); break;
2097 case 0x240: case 0x241: case 0x242: case 0x243:
2098 case 0x244: case 0x245: case 0x246: case 0x247:
2099 case 0x248: case 0x249: case 0x24A: case 0x24B:
2100 case 0x24C: case 0x24D: case 0x24E: case 0x24F:
2101 executePoint<Graphic6Mode>(time); break;
2102 case 0x340: case 0x341: case 0x342: case 0x343:
2103 case 0x344: case 0x345: case 0x346: case 0x347:
2104 case 0x348: case 0x349: case 0x34A: case 0x34B:
2105 case 0x34C: case 0x34D: case 0x34E: case 0x34F:
2106 executePoint<Graphic7Mode>(time); break;
2107 case 0x440: case 0x441: case 0x442: case 0x443:
2108 case 0x444: case 0x445: case 0x446: case 0x447:
2109 case 0x448: case 0x449: case 0x44A: case 0x44B:
2110 case 0x44C: case 0x44D: case 0x44E: case 0x44F:
2111 executePoint<NonBitmapMode>(time); break;
2112
2113 case 0x050: executePset<Graphic4Mode, ImpOp>(time); break;
2114 case 0x051: executePset<Graphic4Mode, AndOp>(time); break;
2115 case 0x052: executePset<Graphic4Mode, OrOp >(time); break;
2116 case 0x053: executePset<Graphic4Mode, XorOp>(time); break;
2117 case 0x054: executePset<Graphic4Mode, NotOp>(time); break;
2118 case 0x058: executePset<Graphic4Mode, TImpOp>(time); break;
2119 case 0x059: executePset<Graphic4Mode, TAndOp>(time); break;
2120 case 0x05A: executePset<Graphic4Mode, TOrOp >(time); break;
2121 case 0x05B: executePset<Graphic4Mode, TXorOp>(time); break;
2122 case 0x05C: executePset<Graphic4Mode, TNotOp>(time); break;
2123 case 0x055: case 0x056: case 0x057: case 0x05D: case 0x05E: case 0x05F:
2124 executePset<Graphic4Mode, DummyOp>(time); break;
2125 case 0x150: executePset<Graphic5Mode, ImpOp>(time); break;
2126 case 0x151: executePset<Graphic5Mode, AndOp>(time); break;
2127 case 0x152: executePset<Graphic5Mode, OrOp >(time); break;
2128 case 0x153: executePset<Graphic5Mode, XorOp>(time); break;
2129 case 0x154: executePset<Graphic5Mode, NotOp>(time); break;
2130 case 0x158: executePset<Graphic5Mode, TImpOp>(time); break;
2131 case 0x159: executePset<Graphic5Mode, TAndOp>(time); break;
2132 case 0x15A: executePset<Graphic5Mode, TOrOp >(time); break;
2133 case 0x15B: executePset<Graphic5Mode, TXorOp>(time); break;
2134 case 0x15C: executePset<Graphic5Mode, TNotOp>(time); break;
2135 case 0x155: case 0x156: case 0x157: case 0x15D: case 0x15E: case 0x15F:
2136 executePset<Graphic5Mode, DummyOp>(time); break;
2137 case 0x250: executePset<Graphic6Mode, ImpOp>(time); break;
2138 case 0x251: executePset<Graphic6Mode, AndOp>(time); break;
2139 case 0x252: executePset<Graphic6Mode, OrOp >(time); break;
2140 case 0x253: executePset<Graphic6Mode, XorOp>(time); break;
2141 case 0x254: executePset<Graphic6Mode, NotOp>(time); break;
2142 case 0x258: executePset<Graphic6Mode, TImpOp>(time); break;
2143 case 0x259: executePset<Graphic6Mode, TAndOp>(time); break;
2144 case 0x25A: executePset<Graphic6Mode, TOrOp >(time); break;
2145 case 0x25B: executePset<Graphic6Mode, TXorOp>(time); break;
2146 case 0x25C: executePset<Graphic6Mode, TNotOp>(time); break;
2147 case 0x255: case 0x256: case 0x257: case 0x25D: case 0x25E: case 0x25F:
2148 executePset<Graphic6Mode, DummyOp>(time); break;
2149 case 0x350: executePset<Graphic7Mode, ImpOp>(time); break;
2150 case 0x351: executePset<Graphic7Mode, AndOp>(time); break;
2151 case 0x352: executePset<Graphic7Mode, OrOp >(time); break;
2152 case 0x353: executePset<Graphic7Mode, XorOp>(time); break;
2153 case 0x354: executePset<Graphic7Mode, NotOp>(time); break;
2154 case 0x358: executePset<Graphic7Mode, TImpOp>(time); break;
2155 case 0x359: executePset<Graphic7Mode, TAndOp>(time); break;
2156 case 0x35A: executePset<Graphic7Mode, TOrOp >(time); break;
2157 case 0x35B: executePset<Graphic7Mode, TXorOp>(time); break;
2158 case 0x35C: executePset<Graphic7Mode, TNotOp>(time); break;
2159 case 0x355: case 0x356: case 0x357: case 0x35D: case 0x35E: case 0x35F:
2160 executePset<Graphic7Mode, DummyOp>(time); break;
2161 case 0x450: executePset<NonBitmapMode, ImpOp>(time); break;
2162 case 0x451: executePset<NonBitmapMode, AndOp>(time); break;
2163 case 0x452: executePset<NonBitmapMode, OrOp >(time); break;
2164 case 0x453: executePset<NonBitmapMode, XorOp>(time); break;
2165 case 0x454: executePset<NonBitmapMode, NotOp>(time); break;
2166 case 0x458: executePset<NonBitmapMode, TImpOp>(time); break;
2167 case 0x459: executePset<NonBitmapMode, TAndOp>(time); break;
2168 case 0x45A: executePset<NonBitmapMode, TOrOp >(time); break;
2169 case 0x45B: executePset<NonBitmapMode, TXorOp>(time); break;
2170 case 0x45C: executePset<NonBitmapMode, TNotOp>(time); break;
2171 case 0x455: case 0x456: case 0x457: case 0x45D: case 0x45E: case 0x45F:
2172 executePset<NonBitmapMode, DummyOp>(time); break;
2173
2174 case 0x060: case 0x061: case 0x062: case 0x063:
2175 case 0x064: case 0x065: case 0x066: case 0x067:
2176 case 0x068: case 0x069: case 0x06A: case 0x06B:
2177 case 0x06C: case 0x06D: case 0x06E: case 0x06F:
2178 executeSrch<Graphic4Mode>(time); break;
2179 case 0x160: case 0x161: case 0x162: case 0x163:
2180 case 0x164: case 0x165: case 0x166: case 0x167:
2181 case 0x168: case 0x169: case 0x16A: case 0x16B:
2182 case 0x16C: case 0x16D: case 0x16E: case 0x16F:
2183 executeSrch<Graphic5Mode>(time); break;
2184 case 0x260: case 0x261: case 0x262: case 0x263:
2185 case 0x264: case 0x265: case 0x266: case 0x267:
2186 case 0x268: case 0x269: case 0x26A: case 0x26B:
2187 case 0x26C: case 0x26D: case 0x26E: case 0x26F:
2188 executeSrch<Graphic6Mode>(time); break;
2189 case 0x360: case 0x361: case 0x362: case 0x363:
2190 case 0x364: case 0x365: case 0x366: case 0x367:
2191 case 0x368: case 0x369: case 0x36A: case 0x36B:
2192 case 0x36C: case 0x36D: case 0x36E: case 0x36F:
2193 executeSrch<Graphic7Mode>(time); break;
2194 case 0x460: case 0x461: case 0x462: case 0x463:
2195 case 0x464: case 0x465: case 0x466: case 0x467:
2196 case 0x468: case 0x469: case 0x46A: case 0x46B:
2197 case 0x46C: case 0x46D: case 0x46E: case 0x46F:
2198 executeSrch<NonBitmapMode>(time); break;
2199
2200 case 0x070: executeLine<Graphic4Mode, ImpOp>(time); break;
2201 case 0x071: executeLine<Graphic4Mode, AndOp>(time); break;
2202 case 0x072: executeLine<Graphic4Mode, OrOp >(time); break;
2203 case 0x073: executeLine<Graphic4Mode, XorOp>(time); break;
2204 case 0x074: executeLine<Graphic4Mode, NotOp>(time); break;
2205 case 0x078: executeLine<Graphic4Mode, TImpOp>(time); break;
2206 case 0x079: executeLine<Graphic4Mode, TAndOp>(time); break;
2207 case 0x07A: executeLine<Graphic4Mode, TOrOp >(time); break;
2208 case 0x07B: executeLine<Graphic4Mode, TXorOp>(time); break;
2209 case 0x07C: executeLine<Graphic4Mode, TNotOp>(time); break;
2210 case 0x075: case 0x076: case 0x077: case 0x07D: case 0x07E: case 0x07F:
2211 executeLine<Graphic4Mode, DummyOp>(time); break;
2212 case 0x170: executeLine<Graphic5Mode, ImpOp>(time); break;
2213 case 0x171: executeLine<Graphic5Mode, AndOp>(time); break;
2214 case 0x172: executeLine<Graphic5Mode, OrOp >(time); break;
2215 case 0x173: executeLine<Graphic5Mode, XorOp>(time); break;
2216 case 0x174: executeLine<Graphic5Mode, NotOp>(time); break;
2217 case 0x178: executeLine<Graphic5Mode, TImpOp>(time); break;
2218 case 0x179: executeLine<Graphic5Mode, TAndOp>(time); break;
2219 case 0x17A: executeLine<Graphic5Mode, TOrOp >(time); break;
2220 case 0x17B: executeLine<Graphic5Mode, TXorOp>(time); break;
2221 case 0x17C: executeLine<Graphic5Mode, TNotOp>(time); break;
2222 case 0x175: case 0x176: case 0x177: case 0x17D: case 0x17E: case 0x17F:
2223 executeLine<Graphic5Mode, DummyOp>(time); break;
2224 case 0x270: executeLine<Graphic6Mode, ImpOp>(time); break;
2225 case 0x271: executeLine<Graphic6Mode, AndOp>(time); break;
2226 case 0x272: executeLine<Graphic6Mode, OrOp >(time); break;
2227 case 0x273: executeLine<Graphic6Mode, XorOp>(time); break;
2228 case 0x274: executeLine<Graphic6Mode, NotOp>(time); break;
2229 case 0x278: executeLine<Graphic6Mode, TImpOp>(time); break;
2230 case 0x279: executeLine<Graphic6Mode, TAndOp>(time); break;
2231 case 0x27A: executeLine<Graphic6Mode, TOrOp >(time); break;
2232 case 0x27B: executeLine<Graphic6Mode, TXorOp>(time); break;
2233 case 0x27C: executeLine<Graphic6Mode, TNotOp>(time); break;
2234 case 0x275: case 0x276: case 0x277: case 0x27D: case 0x27E: case 0x27F:
2235 executeLine<Graphic6Mode, DummyOp>(time); break;
2236 case 0x370: executeLine<Graphic7Mode, ImpOp>(time); break;
2237 case 0x371: executeLine<Graphic7Mode, AndOp>(time); break;
2238 case 0x372: executeLine<Graphic7Mode, OrOp >(time); break;
2239 case 0x373: executeLine<Graphic7Mode, XorOp>(time); break;
2240 case 0x374: executeLine<Graphic7Mode, NotOp>(time); break;
2241 case 0x378: executeLine<Graphic7Mode, TImpOp>(time); break;
2242 case 0x379: executeLine<Graphic7Mode, TAndOp>(time); break;
2243 case 0x37A: executeLine<Graphic7Mode, TOrOp >(time); break;
2244 case 0x37B: executeLine<Graphic7Mode, TXorOp>(time); break;
2245 case 0x37C: executeLine<Graphic7Mode, TNotOp>(time); break;
2246 case 0x375: case 0x376: case 0x377: case 0x37D: case 0x37E: case 0x37F:
2247 executeLine<Graphic7Mode, DummyOp>(time); break;
2248 case 0x470: executeLine<NonBitmapMode, ImpOp>(time); break;
2249 case 0x471: executeLine<NonBitmapMode, AndOp>(time); break;
2250 case 0x472: executeLine<NonBitmapMode, OrOp >(time); break;
2251 case 0x473: executeLine<NonBitmapMode, XorOp>(time); break;
2252 case 0x474: executeLine<NonBitmapMode, NotOp>(time); break;
2253 case 0x478: executeLine<NonBitmapMode, TImpOp>(time); break;
2254 case 0x479: executeLine<NonBitmapMode, TAndOp>(time); break;
2255 case 0x47A: executeLine<NonBitmapMode, TOrOp >(time); break;
2256 case 0x47B: executeLine<NonBitmapMode, TXorOp>(time); break;
2257 case 0x47C: executeLine<NonBitmapMode, TNotOp>(time); break;
2258 case 0x475: case 0x476: case 0x477: case 0x47D: case 0x47E: case 0x47F:
2259 executeLine<NonBitmapMode, DummyOp>(time); break;
2260
2261 case 0x080: executeLmmv<Graphic4Mode, ImpOp>(time); break;
2262 case 0x081: executeLmmv<Graphic4Mode, AndOp>(time); break;
2263 case 0x082: executeLmmv<Graphic4Mode, OrOp >(time); break;
2264 case 0x083: executeLmmv<Graphic4Mode, XorOp>(time); break;
2265 case 0x084: executeLmmv<Graphic4Mode, NotOp>(time); break;
2266 case 0x088: executeLmmv<Graphic4Mode, TImpOp>(time); break;
2267 case 0x089: executeLmmv<Graphic4Mode, TAndOp>(time); break;
2268 case 0x08A: executeLmmv<Graphic4Mode, TOrOp >(time); break;
2269 case 0x08B: executeLmmv<Graphic4Mode, TXorOp>(time); break;
2270 case 0x08C: executeLmmv<Graphic4Mode, TNotOp>(time); break;
2271 case 0x085: case 0x086: case 0x087: case 0x08D: case 0x08E: case 0x08F:
2272 executeLmmv<Graphic4Mode, DummyOp>(time); break;
2273 case 0x180: executeLmmv<Graphic5Mode, ImpOp>(time); break;
2274 case 0x181: executeLmmv<Graphic5Mode, AndOp>(time); break;
2275 case 0x182: executeLmmv<Graphic5Mode, OrOp >(time); break;
2276 case 0x183: executeLmmv<Graphic5Mode, XorOp>(time); break;
2277 case 0x184: executeLmmv<Graphic5Mode, NotOp>(time); break;
2278 case 0x188: executeLmmv<Graphic5Mode, TImpOp>(time); break;
2279 case 0x189: executeLmmv<Graphic5Mode, TAndOp>(time); break;
2280 case 0x18A: executeLmmv<Graphic5Mode, TOrOp >(time); break;
2281 case 0x18B: executeLmmv<Graphic5Mode, TXorOp>(time); break;
2282 case 0x18C: executeLmmv<Graphic5Mode, TNotOp>(time); break;
2283 case 0x185: case 0x186: case 0x187: case 0x18D: case 0x18E: case 0x18F:
2284 executeLmmv<Graphic5Mode, DummyOp>(time); break;
2285 case 0x280: executeLmmv<Graphic6Mode, ImpOp>(time); break;
2286 case 0x281: executeLmmv<Graphic6Mode, AndOp>(time); break;
2287 case 0x282: executeLmmv<Graphic6Mode, OrOp >(time); break;
2288 case 0x283: executeLmmv<Graphic6Mode, XorOp>(time); break;
2289 case 0x284: executeLmmv<Graphic6Mode, NotOp>(time); break;
2290 case 0x288: executeLmmv<Graphic6Mode, TImpOp>(time); break;
2291 case 0x289: executeLmmv<Graphic6Mode, TAndOp>(time); break;
2292 case 0x28A: executeLmmv<Graphic6Mode, TOrOp >(time); break;
2293 case 0x28B: executeLmmv<Graphic6Mode, TXorOp>(time); break;
2294 case 0x28C: executeLmmv<Graphic6Mode, TNotOp>(time); break;
2295 case 0x285: case 0x286: case 0x287: case 0x28D: case 0x28E: case 0x28F:
2296 executeLmmv<Graphic6Mode, DummyOp>(time); break;
2297 case 0x380: executeLmmv<Graphic7Mode, ImpOp>(time); break;
2298 case 0x381: executeLmmv<Graphic7Mode, AndOp>(time); break;
2299 case 0x382: executeLmmv<Graphic7Mode, OrOp >(time); break;
2300 case 0x383: executeLmmv<Graphic7Mode, XorOp>(time); break;
2301 case 0x384: executeLmmv<Graphic7Mode, NotOp>(time); break;
2302 case 0x388: executeLmmv<Graphic7Mode, TImpOp>(time); break;
2303 case 0x389: executeLmmv<Graphic7Mode, TAndOp>(time); break;
2304 case 0x38A: executeLmmv<Graphic7Mode, TOrOp >(time); break;
2305 case 0x38B: executeLmmv<Graphic7Mode, TXorOp>(time); break;
2306 case 0x38C: executeLmmv<Graphic7Mode, TNotOp>(time); break;
2307 case 0x385: case 0x386: case 0x387: case 0x38D: case 0x38E: case 0x38F:
2308 executeLmmv<Graphic7Mode, DummyOp>(time); break;
2309 case 0x480: executeLmmv<NonBitmapMode, ImpOp>(time); break;
2310 case 0x481: executeLmmv<NonBitmapMode, AndOp>(time); break;
2311 case 0x482: executeLmmv<NonBitmapMode, OrOp >(time); break;
2312 case 0x483: executeLmmv<NonBitmapMode, XorOp>(time); break;
2313 case 0x484: executeLmmv<NonBitmapMode, NotOp>(time); break;
2314 case 0x488: executeLmmv<NonBitmapMode, TImpOp>(time); break;
2315 case 0x489: executeLmmv<NonBitmapMode, TAndOp>(time); break;
2316 case 0x48A: executeLmmv<NonBitmapMode, TOrOp >(time); break;
2317 case 0x48B: executeLmmv<NonBitmapMode, TXorOp>(time); break;
2318 case 0x48C: executeLmmv<NonBitmapMode, TNotOp>(time); break;
2319 case 0x485: case 0x486: case 0x487: case 0x48D: case 0x48E: case 0x48F:
2320 executeLmmv<NonBitmapMode, DummyOp>(time); break;
2321
2322 case 0x090: executeLmmm<Graphic4Mode, ImpOp>(time); break;
2323 case 0x091: executeLmmm<Graphic4Mode, AndOp>(time); break;
2324 case 0x092: executeLmmm<Graphic4Mode, OrOp >(time); break;
2325 case 0x093: executeLmmm<Graphic4Mode, XorOp>(time); break;
2326 case 0x094: executeLmmm<Graphic4Mode, NotOp>(time); break;
2327 case 0x098: executeLmmm<Graphic4Mode, TImpOp>(time); break;
2328 case 0x099: executeLmmm<Graphic4Mode, TAndOp>(time); break;
2329 case 0x09A: executeLmmm<Graphic4Mode, TOrOp >(time); break;
2330 case 0x09B: executeLmmm<Graphic4Mode, TXorOp>(time); break;
2331 case 0x09C: executeLmmm<Graphic4Mode, TNotOp>(time); break;
2332 case 0x095: case 0x096: case 0x097: case 0x09D: case 0x09E: case 0x09F:
2333 executeLmmm<Graphic4Mode, DummyOp>(time); break;
2334 case 0x190: executeLmmm<Graphic5Mode, ImpOp>(time); break;
2335 case 0x191: executeLmmm<Graphic5Mode, AndOp>(time); break;
2336 case 0x192: executeLmmm<Graphic5Mode, OrOp >(time); break;
2337 case 0x193: executeLmmm<Graphic5Mode, XorOp>(time); break;
2338 case 0x194: executeLmmm<Graphic5Mode, NotOp>(time); break;
2339 case 0x198: executeLmmm<Graphic5Mode, TImpOp>(time); break;
2340 case 0x199: executeLmmm<Graphic5Mode, TAndOp>(time); break;
2341 case 0x19A: executeLmmm<Graphic5Mode, TOrOp >(time); break;
2342 case 0x19B: executeLmmm<Graphic5Mode, TXorOp>(time); break;
2343 case 0x19C: executeLmmm<Graphic5Mode, TNotOp>(time); break;
2344 case 0x195: case 0x196: case 0x197: case 0x19D: case 0x19E: case 0x19F:
2345 executeLmmm<Graphic5Mode, DummyOp>(time); break;
2346 case 0x290: executeLmmm<Graphic6Mode, ImpOp>(time); break;
2347 case 0x291: executeLmmm<Graphic6Mode, AndOp>(time); break;
2348 case 0x292: executeLmmm<Graphic6Mode, OrOp >(time); break;
2349 case 0x293: executeLmmm<Graphic6Mode, XorOp>(time); break;
2350 case 0x294: executeLmmm<Graphic6Mode, NotOp>(time); break;
2351 case 0x298: executeLmmm<Graphic6Mode, TImpOp>(time); break;
2352 case 0x299: executeLmmm<Graphic6Mode, TAndOp>(time); break;
2353 case 0x29A: executeLmmm<Graphic6Mode, TOrOp >(time); break;
2354 case 0x29B: executeLmmm<Graphic6Mode, TXorOp>(time); break;
2355 case 0x29C: executeLmmm<Graphic6Mode, TNotOp>(time); break;
2356 case 0x295: case 0x296: case 0x297: case 0x29D: case 0x29E: case 0x29F:
2357 executeLmmm<Graphic6Mode, DummyOp>(time); break;
2358 case 0x390: executeLmmm<Graphic7Mode, ImpOp>(time); break;
2359 case 0x391: executeLmmm<Graphic7Mode, AndOp>(time); break;
2360 case 0x392: executeLmmm<Graphic7Mode, OrOp >(time); break;
2361 case 0x393: executeLmmm<Graphic7Mode, XorOp>(time); break;
2362 case 0x394: executeLmmm<Graphic7Mode, NotOp>(time); break;
2363 case 0x398: executeLmmm<Graphic7Mode, TImpOp>(time); break;
2364 case 0x399: executeLmmm<Graphic7Mode, TAndOp>(time); break;
2365 case 0x39A: executeLmmm<Graphic7Mode, TOrOp >(time); break;
2366 case 0x39B: executeLmmm<Graphic7Mode, TXorOp>(time); break;
2367 case 0x39C: executeLmmm<Graphic7Mode, TNotOp>(time); break;
2368 case 0x395: case 0x396: case 0x397: case 0x39D: case 0x39E: case 0x39F:
2369 executeLmmm<Graphic7Mode, DummyOp>(time); break;
2370 case 0x490: executeLmmm<NonBitmapMode, ImpOp>(time); break;
2371 case 0x491: executeLmmm<NonBitmapMode, AndOp>(time); break;
2372 case 0x492: executeLmmm<NonBitmapMode, OrOp >(time); break;
2373 case 0x493: executeLmmm<NonBitmapMode, XorOp>(time); break;
2374 case 0x494: executeLmmm<NonBitmapMode, NotOp>(time); break;
2375 case 0x498: executeLmmm<NonBitmapMode, TImpOp>(time); break;
2376 case 0x499: executeLmmm<NonBitmapMode, TAndOp>(time); break;
2377 case 0x49A: executeLmmm<NonBitmapMode, TOrOp >(time); break;
2378 case 0x49B: executeLmmm<NonBitmapMode, TXorOp>(time); break;
2379 case 0x49C: executeLmmm<NonBitmapMode, TNotOp>(time); break;
2380 case 0x495: case 0x496: case 0x497: case 0x49D: case 0x49E: case 0x49F:
2381 executeLmmm<NonBitmapMode, DummyOp>(time); break;
2382
2383 case 0x0A0: case 0x0A1: case 0x0A2: case 0x0A3:
2384 case 0x0A4: case 0x0A5: case 0x0A6: case 0x0A7:
2385 case 0x0A8: case 0x0A9: case 0x0AA: case 0x0AB:
2386 case 0x0AC: case 0x0AD: case 0x0AE: case 0x0AF:
2387 executeLmcm<Graphic4Mode>(time); break;
2388 case 0x1A0: case 0x1A1: case 0x1A2: case 0x1A3:
2389 case 0x1A4: case 0x1A5: case 0x1A6: case 0x1A7:
2390 case 0x1A8: case 0x1A9: case 0x1AA: case 0x1AB:
2391 case 0x1AC: case 0x1AD: case 0x1AE: case 0x1AF:
2392 executeLmcm<Graphic5Mode>(time); break;
2393 case 0x2A0: case 0x2A1: case 0x2A2: case 0x2A3:
2394 case 0x2A4: case 0x2A5: case 0x2A6: case 0x2A7:
2395 case 0x2A8: case 0x2A9: case 0x2AA: case 0x2AB:
2396 case 0x2AC: case 0x2AD: case 0x2AE: case 0x2AF:
2397 executeLmcm<Graphic6Mode>(time); break;
2398 case 0x3A0: case 0x3A1: case 0x3A2: case 0x3A3:
2399 case 0x3A4: case 0x3A5: case 0x3A6: case 0x3A7:
2400 case 0x3A8: case 0x3A9: case 0x3AA: case 0x3AB:
2401 case 0x3AC: case 0x3AD: case 0x3AE: case 0x3AF:
2402 executeLmcm<Graphic7Mode>(time); break;
2403 case 0x4A0: case 0x4A1: case 0x4A2: case 0x4A3:
2404 case 0x4A4: case 0x4A5: case 0x4A6: case 0x4A7:
2405 case 0x4A8: case 0x4A9: case 0x4AA: case 0x4AB:
2406 case 0x4AC: case 0x4AD: case 0x4AE: case 0x4AF:
2407 executeLmcm<NonBitmapMode>(time); break;
2408
2409 case 0x0B0: executeLmmc<Graphic4Mode, ImpOp>(time); break;
2410 case 0x0B1: executeLmmc<Graphic4Mode, AndOp>(time); break;
2411 case 0x0B2: executeLmmc<Graphic4Mode, OrOp >(time); break;
2412 case 0x0B3: executeLmmc<Graphic4Mode, XorOp>(time); break;
2413 case 0x0B4: executeLmmc<Graphic4Mode, NotOp>(time); break;
2414 case 0x0B8: executeLmmc<Graphic4Mode, TImpOp>(time); break;
2415 case 0x0B9: executeLmmc<Graphic4Mode, TAndOp>(time); break;
2416 case 0x0BA: executeLmmc<Graphic4Mode, TOrOp >(time); break;
2417 case 0x0BB: executeLmmc<Graphic4Mode, TXorOp>(time); break;
2418 case 0x0BC: executeLmmc<Graphic4Mode, TNotOp>(time); break;
2419 case 0x0B5: case 0x0B6: case 0x0B7: case 0x0BD: case 0x0BE: case 0x0BF:
2420 executeLmmc<Graphic4Mode, DummyOp>(time); break;
2421 case 0x1B0: executeLmmc<Graphic5Mode, ImpOp>(time); break;
2422 case 0x1B1: executeLmmc<Graphic5Mode, AndOp>(time); break;
2423 case 0x1B2: executeLmmc<Graphic5Mode, OrOp >(time); break;
2424 case 0x1B3: executeLmmc<Graphic5Mode, XorOp>(time); break;
2425 case 0x1B4: executeLmmc<Graphic5Mode, NotOp>(time); break;
2426 case 0x1B8: executeLmmc<Graphic5Mode, TImpOp>(time); break;
2427 case 0x1B9: executeLmmc<Graphic5Mode, TAndOp>(time); break;
2428 case 0x1BA: executeLmmc<Graphic5Mode, TOrOp >(time); break;
2429 case 0x1BB: executeLmmc<Graphic5Mode, TXorOp>(time); break;
2430 case 0x1BC: executeLmmc<Graphic5Mode, TNotOp>(time); break;
2431 case 0x1B5: case 0x1B6: case 0x1B7: case 0x1BD: case 0x1BE: case 0x1BF:
2432 executeLmmc<Graphic5Mode, DummyOp>(time); break;
2433 case 0x2B0: executeLmmc<Graphic6Mode, ImpOp>(time); break;
2434 case 0x2B1: executeLmmc<Graphic6Mode, AndOp>(time); break;
2435 case 0x2B2: executeLmmc<Graphic6Mode, OrOp >(time); break;
2436 case 0x2B3: executeLmmc<Graphic6Mode, XorOp>(time); break;
2437 case 0x2B4: executeLmmc<Graphic6Mode, NotOp>(time); break;
2438 case 0x2B8: executeLmmc<Graphic6Mode, TImpOp>(time); break;
2439 case 0x2B9: executeLmmc<Graphic6Mode, TAndOp>(time); break;
2440 case 0x2BA: executeLmmc<Graphic6Mode, TOrOp >(time); break;
2441 case 0x2BB: executeLmmc<Graphic6Mode, TXorOp>(time); break;
2442 case 0x2BC: executeLmmc<Graphic6Mode, TNotOp>(time); break;
2443 case 0x2B5: case 0x2B6: case 0x2B7: case 0x2BD: case 0x2BE: case 0x2BF:
2444 executeLmmc<Graphic6Mode, DummyOp>(time); break;
2445 case 0x3B0: executeLmmc<Graphic7Mode, ImpOp>(time); break;
2446 case 0x3B1: executeLmmc<Graphic7Mode, AndOp>(time); break;
2447 case 0x3B2: executeLmmc<Graphic7Mode, OrOp >(time); break;
2448 case 0x3B3: executeLmmc<Graphic7Mode, XorOp>(time); break;
2449 case 0x3B4: executeLmmc<Graphic7Mode, NotOp>(time); break;
2450 case 0x3B8: executeLmmc<Graphic7Mode, TImpOp>(time); break;
2451 case 0x3B9: executeLmmc<Graphic7Mode, TAndOp>(time); break;
2452 case 0x3BA: executeLmmc<Graphic7Mode, TOrOp >(time); break;
2453 case 0x3BB: executeLmmc<Graphic7Mode, TXorOp>(time); break;
2454 case 0x3BC: executeLmmc<Graphic7Mode, TNotOp>(time); break;
2455 case 0x3B5: case 0x3B6: case 0x3B7: case 0x3BD: case 0x3BE: case 0x3BF:
2456 executeLmmc<Graphic7Mode, DummyOp>(time); break;
2457 case 0x4B0: executeLmmc<NonBitmapMode, ImpOp>(time); break;
2458 case 0x4B1: executeLmmc<NonBitmapMode, AndOp>(time); break;
2459 case 0x4B2: executeLmmc<NonBitmapMode, OrOp >(time); break;
2460 case 0x4B3: executeLmmc<NonBitmapMode, XorOp>(time); break;
2461 case 0x4B4: executeLmmc<NonBitmapMode, NotOp>(time); break;
2462 case 0x4B8: executeLmmc<NonBitmapMode, TImpOp>(time); break;
2463 case 0x4B9: executeLmmc<NonBitmapMode, TAndOp>(time); break;
2464 case 0x4BA: executeLmmc<NonBitmapMode, TOrOp >(time); break;
2465 case 0x4BB: executeLmmc<NonBitmapMode, TXorOp>(time); break;
2466 case 0x4BC: executeLmmc<NonBitmapMode, TNotOp>(time); break;
2467 case 0x4B5: case 0x4B6: case 0x4B7: case 0x4BD: case 0x4BE: case 0x4BF:
2468 executeLmmc<NonBitmapMode, DummyOp>(time); break;
2469
2470 case 0x0C0: case 0x0C1: case 0x0C2: case 0x0C3:
2471 case 0x0C4: case 0x0C5: case 0x0C6: case 0x0C7:
2472 case 0x0C8: case 0x0C9: case 0x0CA: case 0x0CB:
2473 case 0x0CC: case 0x0CD: case 0x0CE: case 0x0CF:
2474 executeHmmv<Graphic4Mode>(time); break;
2475 case 0x1C0: case 0x1C1: case 0x1C2: case 0x1C3:
2476 case 0x1C4: case 0x1C5: case 0x1C6: case 0x1C7:
2477 case 0x1C8: case 0x1C9: case 0x1CA: case 0x1CB:
2478 case 0x1CC: case 0x1CD: case 0x1CE: case 0x1CF:
2479 executeHmmv<Graphic5Mode>(time); break;
2480 case 0x2C0: case 0x2C1: case 0x2C2: case 0x2C3:
2481 case 0x2C4: case 0x2C5: case 0x2C6: case 0x2C7:
2482 case 0x2C8: case 0x2C9: case 0x2CA: case 0x2CB:
2483 case 0x2CC: case 0x2CD: case 0x2CE: case 0x2CF:
2484 executeHmmv<Graphic6Mode>(time); break;
2485 case 0x3C0: case 0x3C1: case 0x3C2: case 0x3C3:
2486 case 0x3C4: case 0x3C5: case 0x3C6: case 0x3C7:
2487 case 0x3C8: case 0x3C9: case 0x3CA: case 0x3CB:
2488 case 0x3CC: case 0x3CD: case 0x3CE: case 0x3CF:
2489 executeHmmv<Graphic7Mode>(time); break;
2490 case 0x4C0: case 0x4C1: case 0x4C2: case 0x4C3:
2491 case 0x4C4: case 0x4C5: case 0x4C6: case 0x4C7:
2492 case 0x4C8: case 0x4C9: case 0x4CA: case 0x4CB:
2493 case 0x4CC: case 0x4CD: case 0x4CE: case 0x4CF:
2494 executeHmmv<NonBitmapMode>(time); break;
2495
2496 case 0x0D0: case 0x0D1: case 0x0D2: case 0x0D3:
2497 case 0x0D4: case 0x0D5: case 0x0D6: case 0x0D7:
2498 case 0x0D8: case 0x0D9: case 0x0DA: case 0x0DB:
2499 case 0x0DC: case 0x0DD: case 0x0DE: case 0x0DF:
2500 executeHmmm<Graphic4Mode>(time); break;
2501 case 0x1D0: case 0x1D1: case 0x1D2: case 0x1D3:
2502 case 0x1D4: case 0x1D5: case 0x1D6: case 0x1D7:
2503 case 0x1D8: case 0x1D9: case 0x1DA: case 0x1DB:
2504 case 0x1DC: case 0x1DD: case 0x1DE: case 0x1DF:
2505 executeHmmm<Graphic5Mode>(time); break;
2506 case 0x2D0: case 0x2D1: case 0x2D2: case 0x2D3:
2507 case 0x2D4: case 0x2D5: case 0x2D6: case 0x2D7:
2508 case 0x2D8: case 0x2D9: case 0x2DA: case 0x2DB:
2509 case 0x2DC: case 0x2DD: case 0x2DE: case 0x2DF:
2510 executeHmmm<Graphic6Mode>(time); break;
2511 case 0x3D0: case 0x3D1: case 0x3D2: case 0x3D3:
2512 case 0x3D4: case 0x3D5: case 0x3D6: case 0x3D7:
2513 case 0x3D8: case 0x3D9: case 0x3DA: case 0x3DB:
2514 case 0x3DC: case 0x3DD: case 0x3DE: case 0x3DF:
2515 executeHmmm<Graphic7Mode>(time); break;
2516 case 0x4D0: case 0x4D1: case 0x4D2: case 0x4D3:
2517 case 0x4D4: case 0x4D5: case 0x4D6: case 0x4D7:
2518 case 0x4D8: case 0x4D9: case 0x4DA: case 0x4DB:
2519 case 0x4DC: case 0x4DD: case 0x4DE: case 0x4DF:
2520 executeHmmm<NonBitmapMode>(time); break;
2521
2522 case 0x0E0: case 0x0E1: case 0x0E2: case 0x0E3:
2523 case 0x0E4: case 0x0E5: case 0x0E6: case 0x0E7:
2524 case 0x0E8: case 0x0E9: case 0x0EA: case 0x0EB:
2525 case 0x0EC: case 0x0ED: case 0x0EE: case 0x0EF:
2526 executeYmmm<Graphic4Mode>(time); break;
2527 case 0x1E0: case 0x1E1: case 0x1E2: case 0x1E3:
2528 case 0x1E4: case 0x1E5: case 0x1E6: case 0x1E7:
2529 case 0x1E8: case 0x1E9: case 0x1EA: case 0x1EB:
2530 case 0x1EC: case 0x1ED: case 0x1EE: case 0x1EF:
2531 executeYmmm<Graphic5Mode>(time); break;
2532 case 0x2E0: case 0x2E1: case 0x2E2: case 0x2E3:
2533 case 0x2E4: case 0x2E5: case 0x2E6: case 0x2E7:
2534 case 0x2E8: case 0x2E9: case 0x2EA: case 0x2EB:
2535 case 0x2EC: case 0x2ED: case 0x2EE: case 0x2EF:
2536 executeYmmm<Graphic6Mode>(time); break;
2537 case 0x3E0: case 0x3E1: case 0x3E2: case 0x3E3:
2538 case 0x3E4: case 0x3E5: case 0x3E6: case 0x3E7:
2539 case 0x3E8: case 0x3E9: case 0x3EA: case 0x3EB:
2540 case 0x3EC: case 0x3ED: case 0x3EE: case 0x3EF:
2541 executeYmmm<Graphic7Mode>(time); break;
2542 case 0x4E0: case 0x4E1: case 0x4E2: case 0x4E3:
2543 case 0x4E4: case 0x4E5: case 0x4E6: case 0x4E7:
2544 case 0x4E8: case 0x4E9: case 0x4EA: case 0x4EB:
2545 case 0x4EC: case 0x4ED: case 0x4EE: case 0x4EF:
2546 executeYmmm<NonBitmapMode>(time); break;
2547
2548 case 0x0F0: case 0x0F1: case 0x0F2: case 0x0F3:
2549 case 0x0F4: case 0x0F5: case 0x0F6: case 0x0F7:
2550 case 0x0F8: case 0x0F9: case 0x0FA: case 0x0FB:
2551 case 0x0FC: case 0x0FD: case 0x0FE: case 0x0FF:
2552 executeHmmc<Graphic4Mode>(time); break;
2553 case 0x1F0: case 0x1F1: case 0x1F2: case 0x1F3:
2554 case 0x1F4: case 0x1F5: case 0x1F6: case 0x1F7:
2555 case 0x1F8: case 0x1F9: case 0x1FA: case 0x1FB:
2556 case 0x1FC: case 0x1FD: case 0x1FE: case 0x1FF:
2557 executeHmmc<Graphic5Mode>(time); break;
2558 case 0x2F0: case 0x2F1: case 0x2F2: case 0x2F3:
2559 case 0x2F4: case 0x2F5: case 0x2F6: case 0x2F7:
2560 case 0x2F8: case 0x2F9: case 0x2FA: case 0x2FB:
2561 case 0x2FC: case 0x2FD: case 0x2FE: case 0x2FF:
2562 executeHmmc<Graphic6Mode>(time); break;
2563 case 0x3F0: case 0x3F1: case 0x3F2: case 0x3F3:
2564 case 0x3F4: case 0x3F5: case 0x3F6: case 0x3F7:
2565 case 0x3F8: case 0x3F9: case 0x3FA: case 0x3FB:
2566 case 0x3FC: case 0x3FD: case 0x3FE: case 0x3FF:
2567 executeHmmc<Graphic7Mode>(time); break;
2568 case 0x4F0: case 0x4F1: case 0x4F2: case 0x4F3:
2569 case 0x4F4: case 0x4F5: case 0x4F6: case 0x4F7:
2570 case 0x4F8: case 0x4F9: case 0x4FA: case 0x4FB:
2571 case 0x4FC: case 0x4FD: case 0x4FE: case 0x4FF:
2572 executeHmmc<NonBitmapMode>(time); break;
2573
2574 default:
2576 }
2577}
2578
2579void VDPCmdEngine::reportVdpCommand() const
2580{
2581 static constexpr std::array<std::string_view, 16> COMMANDS = {
2582 " ABRT"," ????"," ????"," ????","POINT"," PSET"," SRCH"," LINE",
2583 " LMMV"," LMMM"," LMCM"," LMMC"," HMMV"," HMMM"," YMMM"," HMMC"
2584 };
2585 static constexpr std::array<std::string_view, 16> OPS = {
2586 "IMP ","AND ","OR ","XOR ","NOT ","NOP ","NOP ","NOP ",
2587 "TIMP","TAND","TOR ","TXOR","TNOT","NOP ","NOP ","NOP "
2588 };
2589
2590 std::cerr << "VDPCmd " << COMMANDS[CMD >> 4] << '-' << OPS[CMD & 15]
2591 << '(' << int(SX) << ',' << int(SY) << ")->("
2592 << int(DX) << ',' << int(DY) << ")," << int(COL)
2593 << " [" << int((ARG & DIX) ? -int(NX) : int(NX))
2594 << ',' << int((ARG & DIY) ? -int(NY) : int(NY)) << "]\n";
2595}
2596
2597void VDPCmdEngine::commandDone(EmuTime::param time)
2598{
2599 // Note: TR is not reset yet; it is reset when S#2 is read next.
2600 status &= 0xFE; // reset CE
2601 executingProbe = false;
2602 CMD = 0;
2603 setStatusChangeTime(EmuTime::infinity());
2604 vram.cmdReadWindow.disable(time);
2605 vram.cmdWriteWindow.disable(time);
2606}
2607
2608
2609// version 1: initial version
2610// version 2: replaced member 'Clock<> clock' with 'EmuTime time'
2611// version 3: added 'phase', 'tmpSrc', 'tmpDst'
2612// version 4: added 'lastXXX'. This is only used for debugging, so you could ask
2613// why this needs to be serialized. Maybe for savestate/replay-files
2614// it's not super useful, but it is important for reverse during a
2615// debug session.
2616template<typename Archive>
2617void VDPCmdEngine::serialize(Archive& ar, unsigned version)
2618{
2619 // In older (development) versions CMD was split in a CMD and a LOG
2620 // member, though it was combined for the savestate. Only the CMD part
2621 // was guaranteed to be zero when no command was executing. So when
2622 // loading an older savestate this can still be the case.
2623
2624 if (ar.versionAtLeast(version, 2)) {
2625 ar.serialize("time", engineTime);
2626 } else {
2627 // in version 1, the 'engineTime' member had type 'Clock<>'
2628 assert(Archive::IS_LOADER);
2629 VDP::VDPClock clock(EmuTime::dummy());
2630 ar.serialize("clock", clock);
2631 engineTime = clock.getTime();
2632 }
2633 ar.serialize("statusChangeTime", statusChangeTime,
2634 "scrMode", scrMode,
2635 "status", status,
2636 "transfer", transfer,
2637 "SX", SX,
2638 "SY", SY,
2639 "DX", DX,
2640 "DY", DY,
2641 "NX", NX,
2642 "NY", NY,
2643 "ASX", ASX,
2644 "ADX", ADX,
2645 "ANX", ANX,
2646 "COL", COL,
2647 "ARG", ARG,
2648 "CMD", CMD);
2649
2650 if (ar.versionAtLeast(version, 3)) {
2651 ar.serialize("phase", phase,
2652 "tmpSrc", tmpSrc,
2653 "tmpDst", tmpDst);
2654 } else {
2655 assert(Archive::IS_LOADER);
2656 phase = tmpSrc = tmpDst = 0;
2657 }
2658
2659 if constexpr (Archive::IS_LOADER) {
2660 if (CMD & 0xF0) {
2661 assert(scrMode >= 0);
2662 } else {
2663 CMD = 0; // see note above
2664 }
2665 }
2666
2667 if (ar.versionAtLeast(version, 4)) {
2668 ar.serialize("lastSX", lastSX,
2669 "lastSY", lastSY,
2670 "lastDX", lastDX,
2671 "lastDY", lastDY,
2672 "lastNX", lastNX,
2673 "lastNY", lastNY,
2674 "lastCOL", lastCOL,
2675 "lastARG", lastARG,
2676 "lastCMD", lastCMD);
2677 } else {
2678 assert(Archive::IS_LOADER);
2679 lastSX = SX;
2680 lastSY = SY;
2681 lastDX = DX;
2682 lastDY = DY;
2683 lastNX = NX;
2684 lastNY = NY;
2685 lastCOL = COL;
2686 lastARG = ARG;
2687 lastCMD = CMD;
2688 }
2689}
2691
2692} // namespace openmsx
TclObject t
bool getBoolean() const noexcept
static constexpr EmuDuration duration(unsigned ticks)
Calculates the duration of the given number of ticks at this clock's frequency.
Definition Clock.hh:35
constexpr EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
Definition Clock.hh:46
Represents a VDP display mode.
constexpr byte getBase() const
Get the base display mode as an integer: M5..M1 combined.
static constexpr uint8_t GRAPHIC4
static constexpr uint8_t GRAPHIC5
static constexpr uint8_t GRAPHIC7
static constexpr uint8_t GRAPHIC6
bool anyObservers() const
Definition Subject.hh:27
TclObject execute() const
VDP command engine by Alex Wulms.
static constexpr byte MXD
void updateDisplayMode(DisplayMode mode, bool cmdBit, EmuTime::param time)
Informs the command engine of a VDP display mode change.
void serialize(Archive &ar, unsigned version)
Interface for logical operations.
static constexpr byte MAJ
static constexpr byte DIX
static constexpr byte DIY
static constexpr byte MXS
void reset(EmuTime::param time)
Reinitialize Renderer state.
VDPCmdEngine(VDP &vdp, CommandController &commandController)
void sync2(EmuTime::param time)
static constexpr byte EQ
byte peekCmdReg(byte index) const
Read the content of a command register.
void sync(EmuTime::param time)
Synchronizes the command engine with the VDP.
void setCmdReg(byte index, byte value, EmuTime::param time)
Writes to a command register.
Manages VRAM contents and synchronizes the various users of the VRAM.
Definition VDPVRAM.hh:399
void cmdWrite(unsigned address, byte value, EmuTime::param time)
Write a byte from the command engine.
Definition VDPVRAM.hh:427
VRAMWindow cmdReadWindow
Definition VDPVRAM.hh:687
Unified implementation of MSX Video Display Processors (VDPs).
Definition VDP.hh:67
bool getBrokenCmdTiming() const
Value of the cmdTiming setting, true means commands have infinite speed.
Definition VDP.hh:663
void scheduleCmdSync(EmuTime t)
Only used when there are commandExecuting-probe listeners.
Definition VDP.hh:700
bool getCmdBit() const
Are commands possible in non Graphic modes? (V9958 only)
Definition VDP.hh:547
DisplayMode getDisplayMode() const
Get the display mode the VDP is in.
Definition VDP.hh:156
byte readNP(unsigned index) const
Reads a byte from VRAM in its current state.
Definition VDPVRAM.hh:265
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition gl_vec.hh:431
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
Definition gl_vec.hh:449
This file implemented 3 utility functions:
Definition Autofire.cc:11
uint8_t byte
8 bit unsigned integer
Definition openmsx.hh:26
STL namespace.
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
std::string strCat()
Definition strCat.hh:703
void operator()(EmuTime::param time, VDPVRAM &vram, unsigned addr, byte src, byte color, byte mask) const
void operator()(EmuTime::param, VDPVRAM &, unsigned, byte, byte, byte) const
Represents V9938 Graphic 4 mode (SCREEN5).
static void pset(EmuTime::param time, VDPVRAM &vram, unsigned x, unsigned addr, byte src, byte color, LogOp op)
static byte point(const VDPVRAM &vram, unsigned x, unsigned y, bool extVRAM)
static constexpr byte PIXELS_PER_BYTE
static constexpr byte COLOR_MASK
static constexpr byte PIXELS_PER_BYTE_SHIFT
static unsigned addressOf(unsigned x, unsigned y, bool extVRAM)
static constexpr unsigned PIXELS_PER_LINE
static byte duplicate(byte color)
Represents V9938 Graphic 5 mode (SCREEN6).
static void pset(EmuTime::param time, VDPVRAM &vram, unsigned x, unsigned addr, byte src, byte color, LogOp op)
static constexpr unsigned PIXELS_PER_LINE
static byte duplicate(byte color)
static constexpr byte PIXELS_PER_BYTE_SHIFT
static constexpr byte COLOR_MASK
static byte point(const VDPVRAM &vram, unsigned x, unsigned y, bool extVRAM)
static unsigned addressOf(unsigned x, unsigned y, bool extVRAM)
static constexpr byte PIXELS_PER_BYTE
Represents V9938 Graphic 6 mode (SCREEN7).
static constexpr byte PIXELS_PER_BYTE
static byte duplicate(byte color)
static byte point(const VDPVRAM &vram, unsigned x, unsigned y, bool extVRAM)
static void pset(EmuTime::param time, VDPVRAM &vram, unsigned x, unsigned addr, byte src, byte color, LogOp op)
static unsigned addressOf(unsigned x, unsigned y, bool extVRAM)
static constexpr byte PIXELS_PER_BYTE_SHIFT
static constexpr byte COLOR_MASK
static constexpr unsigned PIXELS_PER_LINE
Represents V9938 Graphic 7 mode (SCREEN8).
static constexpr byte PIXELS_PER_BYTE
static constexpr byte PIXELS_PER_BYTE_SHIFT
static byte duplicate(byte color)
static byte point(const VDPVRAM &vram, unsigned x, unsigned y, bool extVRAM)
static void pset(EmuTime::param time, VDPVRAM &vram, unsigned x, unsigned addr, byte src, byte color, LogOp op)
static unsigned addressOf(unsigned x, unsigned y, bool extVRAM)
static constexpr byte COLOR_MASK
static constexpr unsigned PIXELS_PER_LINE
void operator()(EmuTime::param time, VDPVRAM &vram, unsigned addr, byte src, byte color, byte mask) const
Incremental address calculation (byte based, no extended VRAM)
unsigned getAddr() const
IncrByteAddr4(unsigned x, unsigned y, int)
unsigned getAddr() const
IncrByteAddr5(unsigned x, unsigned y, int)
IncrByteAddr6(unsigned x, unsigned y, int tx)
IncrByteAddr7(unsigned x, unsigned y, int tx)
unsigned getAddr() const
Incremental mask calculation.
byte getMask() const
IncrMask4(unsigned x, int)
byte getMask() const
IncrMask5(unsigned x, int tx)
byte getMask() const
IncrMask7(unsigned, int)
Incremental address calculation (pixel-based)
unsigned getAddr() const
IncrPixelAddr4(unsigned x, unsigned y, int tx)
unsigned getAddr() const
IncrPixelAddr5(unsigned x, unsigned y, int tx)
unsigned getAddr() const
IncrPixelAddr6(unsigned x, unsigned y, int tx)
byte doShift(byte color) const
IncrShift4(unsigned sx, unsigned dx)
byte doShift(byte color) const
IncrShift5(unsigned sx, unsigned dx)
IncrShift7(unsigned, unsigned)
byte doShift(byte color) const
Represents V9958 non-bitmap command mode.
static unsigned addressOf(unsigned x, unsigned y, bool extVRAM)
static constexpr byte COLOR_MASK
static byte point(const VDPVRAM &vram, unsigned x, unsigned y, bool extVRAM)
static constexpr byte PIXELS_PER_BYTE_SHIFT
static byte duplicate(byte color)
static void pset(EmuTime::param time, VDPVRAM &vram, unsigned x, unsigned addr, byte src, byte color, LogOp op)
static constexpr byte PIXELS_PER_BYTE
static constexpr unsigned PIXELS_PER_LINE
void operator()(EmuTime::param time, VDPVRAM &vram, unsigned addr, byte src, byte color, byte mask) const
void operator()(EmuTime::param time, VDPVRAM &vram, unsigned addr, byte src, byte color, byte) const
void operator()(EmuTime::param time, VDPVRAM &vram, unsigned addr, byte src, byte color, byte mask) const
void operator()(EmuTime::param time, VDPVRAM &vram, unsigned addr, byte src, byte color, byte) const
#define UNREACHABLE