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