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