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