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