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