openMSX
V9990CmdEngine.cc
Go to the documentation of this file.
1 #include "V9990CmdEngine.hh"
2 #include "V9990.hh"
3 #include "V9990VRAM.hh"
4 #include "V9990DisplayTiming.hh"
5 #include "MSXMotherBoard.hh"
6 #include "RenderSettings.hh"
7 #include "BooleanSetting.hh"
8 #include "EnumSetting.hh"
9 #include "MemBuffer.hh"
10 #include "Clock.hh"
11 #include "serialize.hh"
12 #include "likely.hh"
13 #include "unreachable.hh"
14 #include "xrange.hh"
15 #include <cassert>
16 #include <iostream>
17 
18 namespace openmsx {
19 
20 constexpr unsigned maxLength = 171; // The maximum value from the xxx_TIMING tables below
21 static constexpr EmuDuration d(unsigned x)
22 {
23  assert(x <= maxLength);
25 }
27 
28 // 1st index B0/2/4, B1/3/7, P1, P2
29 // 2nd index sprites-ON, sprites-OFF, display-OFF
30 // 3th index 2bpp, 4bpp, 8bpp, 16bpp
31 // (for P1/P2 fill in the same value 4 times)
32 constexpr EDStorage LMMV_TIMING[4][3][4] = {
33  {{ d( 8), d(11), d(15), d(30)}, {d( 7), d(10), d(13), d(26)}, {d( 7), d(10), d(13), d(25)}},
34  {{ d( 5), d( 7), d( 9), d(18)}, {d( 5), d( 6), d( 8), d(17)}, {d( 5), d( 6), d( 8), d(17)}},
35  {{ d(56), d(56), d(56), d(56)}, {d(25), d(25), d(25), d(25)}, {d( 9), d( 9), d( 9), d( 9)}},
36  {{ d(28), d(28), d(28), d(28)}, {d(15), d(15), d(15), d(15)}, {d( 6), d( 6), d( 6), d( 6)}}
37 };
38 constexpr EDStorage LMMM_TIMING[4][3][4] = {
39  {{d (10),d (16),d( 32),d( 66)}, {d( 8), d(14), d(28), d(57)}, {d( 8), d(13), d(27), d(54)}},
40  {{d ( 6),d( 10),d( 20),d( 39)}, {d( 5), d( 9), d(18), d(35)}, {d( 5), d( 9), d(17), d(35)}},
41  {{d(115),d(115),d(115),d(115)}, {d(52), d(52), d(52), d(52)}, {d(18), d(18), d(18), d(18)}},
42  {{d( 57),d( 57),d( 57),d( 57)}, {d(25), d(25), d(25), d(25)}, {d( 9), d( 9), d( 9), d( 9)}}
43 };
44 constexpr EDStorage BMXL_TIMING[4][3][4] = { // NOTE: values are BYTE based here!
45  {{d( 38),d( 33),d( 32),d( 33)}, {d(33), d(28), d(28), d(28)}, {d(33), d(27), d(27), d(27)}}, // identical to LMMM (b)
46  {{d( 24),d( 20),d( 20),d (19)}, {d(22), d(18), d(18), d(18)}, {d(21), d(17), d(17), d(17)}}, // identical to LMMM (b)
47  {{d(171),d(171),d(171),d(171)}, {d(82), d(82), d(82), d(82)}, {d(29), d(29), d(29), d(29)}},
48  {{d(114),d(114),d(114),d(114)}, {d(50), d(50), d(50), d(50)}, {d(18), d(18), d(18), d(18)}}
49 };
50 constexpr EDStorage BMLX_TIMING[4][3][4] = {
51  {{ d(10), d(16), d(32), d(66)}, {d( 8), d(14), d(28), d(57)}, {d( 8), d(13), d(27), d(54)}}, // identical to LMMM
52  {{ d( 6), d(10), d(20), d(39)}, {d( 5), d( 9), d(18), d(35)}, {d( 5), d( 9), d(17), d(35)}}, // identical to LMMM
53  {{ d(84), d(84), d(84), d(84)}, {d(44), d(44), d(44), d(44)}, {d(17), d(17), d(17), d(17)}},
54  {{ d(57), d(57), d(57), d(57)}, {d(25), d(25), d(25), d(25)}, {d( 9), d( 9), d( 9), d( 9)}}
55 };
56 constexpr EDStorage BMLL_TIMING[4][3][4] = {
57  {{d( 33),d( 33),d( 33),d( 33)}, {d(28), d(28), d(28), d(28)}, {d(27), d(27), d(27), d(27)}},
58  {{d( 20),d( 20),d( 20),d( 20)}, {d(18), d(18), d(18), d(18)}, {d(18), d(18), d(18), d(18)}},
59  {{d(118),d(118),d(118),d(118)}, {d(52), d(52), d(52), d(52)}, {d(18), d(18), d(18), d(18)}},
60  {{d(118),d(118),d(118),d(118)}, {d(52), d(52), d(52), d(52)}, {d(18), d(18), d(18), d(18)}}
61 };
62 constexpr EDStorage CMMM_TIMING[4][3][4] = {
63  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}}, // TODO
64  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}}, // TODO
65  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}}, // TODO
66  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}} // TODO
67 };
68 constexpr EDStorage LINE_TIMING[4][3][4] = {
69  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}}, // TODO
70  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}}, // TODO
71  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}}, // TODO
72  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}} // TODO
73 };
74 constexpr EDStorage SRCH_TIMING[4][3][4] = {
75  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}}, // TODO
76  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}}, // TODO
77  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}}, // TODO
78  {{ d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}, {d(24), d(24), d(24), d(24)}} // TODO
79 };
80 
81 [[nodiscard]] static EmuDuration getTiming(const V9990CmdEngine& cmdEngine, const EDStorage table[4][3][4])
82 {
83  if (unlikely(cmdEngine.getBrokenTiming())) return EmuDuration();
84 
85  const auto& vdp = cmdEngine.getVDP();
86  auto mode = vdp.getDisplayMode();
87  unsigned idx1 = (mode == P1) ? 2 :
88  (mode == P2) ? 3 :
89  (vdp.isOverScan()) ? 0 : 1;
90  unsigned idx2 = vdp.isDisplayEnabled() ? (vdp.spritesEnabled() ? 0 : 1)
91  : 2;
92  unsigned idx3 = vdp.getColorDepth();
93  return table[idx1][idx2][idx3];
94 }
95 
96 
97 
98 // Lazily initialized LUT to speed up logical operations:
99 // - 1st index is the mode: 2,4,8 bpp or 'not-transparent'
100 // - 2nd index is the logical operation: one of the 16 possible binary functions
101 // * Each entry contains a 256x256 byte array, that array is indexed using
102 // destination and source byte (in that order).
103 // * A fully populated logOpLUT would take 4MB, however the vast majority of
104 // this table is (almost) never used. So we save quite some memory (and
105 // startup time) by lazily initializing this table.
106 static MemBuffer<byte> logOpLUT[4][16];
107 
108 // to speedup calculating logOpLUT
109 static constexpr auto bitLUT = [] {
110  std::array<std::array<std::array<std::array<byte, 2>, 2>, 16>, 8> result = {};
111  for (auto op : xrange(16)) {
112  unsigned tmp = op;
113  for (auto src : xrange(2)) {
114  for (auto dst : xrange(2)) {
115  unsigned b = tmp & 1;
116  for (auto bit : xrange(8)) {
117  result[bit][op][src][dst] = b << bit;
118  }
119  tmp >>= 1;
120  }
121  }
122  }
123  return result;
124 }();
125 
127 
128 [[nodiscard]] static constexpr byte func01(unsigned op, unsigned src, unsigned dst)
129 {
130  if ((src & 0x03) == 0) return dst & 0x03;
131  byte res = 0;
132  res |= bitLUT[0][op][(src & 0x01) >> 0][(dst & 0x01) >> 0];
133  res |= bitLUT[1][op][(src & 0x02) >> 1][(dst & 0x02) >> 1];
134  return res;
135 }
136 [[nodiscard]] static constexpr byte func23(unsigned op, unsigned src, unsigned dst)
137 {
138  if ((src & 0x0C) == 0) return dst & 0x0C;
139  byte res = 0;
140  res |= bitLUT[2][op][(src & 0x04) >> 2][(dst & 0x04) >> 2];
141  res |= bitLUT[3][op][(src & 0x08) >> 3][(dst & 0x08) >> 3];
142  return res;
143 }
144 [[nodiscard]] static constexpr byte func45(unsigned op, unsigned src, unsigned dst)
145 {
146  if ((src & 0x30) == 0) return dst & 0x30;
147  byte res = 0;
148  res |= bitLUT[4][op][(src & 0x10) >> 4][(dst & 0x10) >> 4];
149  res |= bitLUT[5][op][(src & 0x20) >> 5][(dst & 0x20) >> 5];
150  return res;
151 }
152 [[nodiscard]] static constexpr byte func67(unsigned op, unsigned src, unsigned dst)
153 {
154  if ((src & 0xC0) == 0) return dst & 0xC0;
155  byte res = 0;
156  res |= bitLUT[6][op][(src & 0x40) >> 6][(dst & 0x40) >> 6];
157  res |= bitLUT[7][op][(src & 0x80) >> 7][(dst & 0x80) >> 7];
158  return res;
159 }
160 
161 [[nodiscard]] static constexpr byte func03(unsigned op, unsigned src, unsigned dst)
162 {
163  if ((src & 0x0F) == 0) return dst & 0x0F;
164  byte res = 0;
165  res |= bitLUT[0][op][(src & 0x01) >> 0][(dst & 0x01) >> 0];
166  res |= bitLUT[1][op][(src & 0x02) >> 1][(dst & 0x02) >> 1];
167  res |= bitLUT[2][op][(src & 0x04) >> 2][(dst & 0x04) >> 2];
168  res |= bitLUT[3][op][(src & 0x08) >> 3][(dst & 0x08) >> 3];
169  return res;
170 }
171 [[nodiscard]] static constexpr byte func47(unsigned op, unsigned src, unsigned dst)
172 {
173  if ((src & 0xF0) == 0) return dst & 0xF0;
174  byte res = 0;
175  res |= bitLUT[4][op][(src & 0x10) >> 4][(dst & 0x10) >> 4];
176  res |= bitLUT[5][op][(src & 0x20) >> 5][(dst & 0x20) >> 5];
177  res |= bitLUT[6][op][(src & 0x40) >> 6][(dst & 0x40) >> 6];
178  res |= bitLUT[7][op][(src & 0x80) >> 7][(dst & 0x80) >> 7];
179  return res;
180 }
181 
182 [[nodiscard]] static constexpr byte func07(unsigned op, unsigned src, unsigned dst)
183 {
184  // if (src == 0) return dst; // handled in fillTable8
185  byte res = 0;
186  res |= bitLUT[0][op][(src & 0x01) >> 0][(dst & 0x01) >> 0];
187  res |= bitLUT[1][op][(src & 0x02) >> 1][(dst & 0x02) >> 1];
188  res |= bitLUT[2][op][(src & 0x04) >> 2][(dst & 0x04) >> 2];
189  res |= bitLUT[3][op][(src & 0x08) >> 3][(dst & 0x08) >> 3];
190  res |= bitLUT[4][op][(src & 0x10) >> 4][(dst & 0x10) >> 4];
191  res |= bitLUT[5][op][(src & 0x20) >> 5][(dst & 0x20) >> 5];
192  res |= bitLUT[6][op][(src & 0x40) >> 6][(dst & 0x40) >> 6];
193  res |= bitLUT[7][op][(src & 0x80) >> 7][(dst & 0x80) >> 7];
194  return res;
195 }
196 
197 static constexpr void fillTableNoT(unsigned op, byte* table)
198 {
199  for (auto dst : xrange(256)) {
200  for (auto src : xrange(256)) {
201  table[dst * 256 + src] = func07(op, src, dst);
202  }
203  }
204 }
205 
206 static constexpr void fillTable2(unsigned op, byte* table)
207 {
208  for (auto dst : xrange(256)) {
209  for (auto src : xrange(256)) {
210  byte res = 0;
211  res |= func01(op, src, dst);
212  res |= func23(op, src, dst);
213  res |= func45(op, src, dst);
214  res |= func67(op, src, dst);
215  table[dst * 256 + src] = res;
216  }
217  }
218 }
219 
220 static constexpr void fillTable4(unsigned op, byte* table)
221 {
222  for (auto dst : xrange(256)) {
223  for (auto src : xrange(256)) {
224  byte res = 0;
225  res |= func03(op, src, dst);
226  res |= func47(op, src, dst);
227  table[dst * 256 + src] = res;
228  }
229  }
230 }
231 
232 static constexpr void fillTable8(unsigned op, byte* table)
233 {
234  for (auto dst : xrange(256)) {
235  { // src == 0
236  table[dst * 256 + 0 ] = dst;
237  }
238  for (auto src : xrange(1, 256)) { // src != 0
239  table[dst * 256 + src] = func07(op, src, dst);
240  }
241  }
242 }
243 
244 [[nodiscard]] static const byte* getLogOpImpl(unsigned mode, unsigned op)
245 {
246  op &= 0x0f;
247  if (!logOpLUT[mode][op].data()) {
248  logOpLUT[mode][op].resize(256 * 256);
249  switch (mode) {
250  case LOG_NO_T:
251  fillTableNoT(op, logOpLUT[mode][op].data());
252  break;
253  case LOG_BPP2:
254  fillTable2 (op, logOpLUT[mode][op].data());
255  break;
256  case LOG_BPP4:
257  fillTable4 (op, logOpLUT[mode][op].data());
258  break;
259  case LOG_BPP8:
260  fillTable8 (op, logOpLUT[mode][op].data());
261  break;
262  default:
263  UNREACHABLE;
264  }
265  }
266  return logOpLUT[mode][op].data();
267 }
268 
269 
270 constexpr byte DIY = 0x08;
271 constexpr byte DIX = 0x04;
272 constexpr byte NEQ = 0x02;
273 constexpr byte MAJ = 0x01;
274 
275 // P1 --------------------------------------------------------------
276 inline unsigned V9990CmdEngine::V9990P1::getPitch(unsigned width)
277 {
278  return width / 2;
279 }
280 
281 inline unsigned V9990CmdEngine::V9990P1::addressOf(
282  unsigned x, unsigned y, unsigned pitch)
283 {
284  //return V9990VRAM::transformP1(((x / 2) & (pitch - 1)) + y * pitch) & 0x7FFFF;
285  // TODO figure out exactly how the coordinate system maps to vram in P1
286  unsigned addr = V9990VRAM::transformP1(((x / 2) & (pitch - 1)) + y * pitch);
287  return (addr & 0x3FFFF) | ((x & 0x200) << 9);
288 }
289 
290 inline byte V9990CmdEngine::V9990P1::point(
291  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch)
292 {
293  return vram.readVRAMDirect(addressOf(x, y, pitch));
294 }
295 
296 inline byte V9990CmdEngine::V9990P1::shift(
297  byte value, unsigned fromX, unsigned toX)
298 {
299  int shift = 4 * ((toX & 1) - (fromX & 1));
300  return (shift > 0) ? (value >> shift) : (value << -shift);
301 }
302 
303 inline byte V9990CmdEngine::V9990P1::shiftMask(unsigned x)
304 {
305  return (x & 1) ? 0x0F : 0xF0;
306 }
307 
308 inline const byte* V9990CmdEngine::V9990P1::getLogOpLUT(byte op)
309 {
310  return getLogOpImpl((op & 0x10) ? LOG_BPP4 : LOG_NO_T, op);
311 }
312 
313 inline byte V9990CmdEngine::V9990P1::logOp(
314  const byte* lut, byte src, byte dst)
315 {
316  return lut[256 * dst + src];
317 }
318 
319 inline void V9990CmdEngine::V9990P1::pset(
320  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
321  byte srcColor, word mask, const byte* lut, byte /*op*/)
322 {
323  unsigned addr = addressOf(x, y, pitch);
324  byte dstColor = vram.readVRAMDirect(addr);
325  byte newColor = logOp(lut, srcColor, dstColor);
326  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
327  byte mask2 = mask1 & shiftMask(x);
328  byte result = (dstColor & ~mask2) | (newColor & mask2);
329  vram.writeVRAMDirect(addr, result);
330 }
331 inline void V9990CmdEngine::V9990P1::psetColor(
332  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
333  word color, word mask, const byte* lut, byte /*op*/)
334 {
335  unsigned addr = addressOf(x, y, pitch);
336  byte srcColor = (addr & 0x40000) ? (color >> 8) : (color & 0xFF);
337  byte dstColor = vram.readVRAMDirect(addr);
338  byte newColor = logOp(lut, srcColor, dstColor);
339  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
340  byte mask2 = mask1 & (0xF0 >> (4 * (x & 1)));
341  byte result = (dstColor & ~mask2) | (newColor & mask2);
342  vram.writeVRAMDirect(addr, result);
343 }
344 
345 // P2 --------------------------------------------------------------
346 inline unsigned V9990CmdEngine::V9990P2::getPitch(unsigned width)
347 {
348  return width / 2;
349 }
350 
351 inline unsigned V9990CmdEngine::V9990P2::addressOf(
352  unsigned x, unsigned y, unsigned pitch)
353 {
354  // TODO check
355  return V9990VRAM::transformP2(((x / 2) & (pitch - 1)) + y * pitch) & 0x7FFFF;
356 }
357 
358 inline byte V9990CmdEngine::V9990P2::point(
359  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch)
360 {
361  return vram.readVRAMDirect(addressOf(x, y, pitch));
362 }
363 
364 inline byte V9990CmdEngine::V9990P2::shift(
365  byte value, unsigned fromX, unsigned toX)
366 {
367  int shift = 4 * ((toX & 1) - (fromX & 1));
368  return (shift > 0) ? (value >> shift) : (value << -shift);
369 }
370 
371 inline byte V9990CmdEngine::V9990P2::shiftMask(unsigned x)
372 {
373  return (x & 1) ? 0x0F : 0xF0;
374 }
375 
376 inline const byte* V9990CmdEngine::V9990P2::getLogOpLUT(byte op)
377 {
378  return getLogOpImpl((op & 0x10) ? LOG_BPP4 : LOG_NO_T, op);
379 }
380 
381 inline byte V9990CmdEngine::V9990P2::logOp(
382  const byte* lut, byte src, byte dst)
383 {
384  return lut[256 * dst + src];
385 }
386 
387 inline void V9990CmdEngine::V9990P2::pset(
388  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
389  byte srcColor, word mask, const byte* lut, byte /*op*/)
390 {
391  unsigned addr = addressOf(x, y, pitch);
392  byte dstColor = vram.readVRAMDirect(addr);
393  byte newColor = logOp(lut, srcColor, dstColor);
394  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
395  byte mask2 = mask1 & shiftMask(x);
396  byte result = (dstColor & ~mask2) | (newColor & mask2);
397  vram.writeVRAMDirect(addr, result);
398 }
399 
400 inline void V9990CmdEngine::V9990P2::psetColor(
401  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
402  word color, word mask, const byte* lut, byte /*op*/)
403 {
404  unsigned addr = addressOf(x, y, pitch);
405  byte srcColor = (addr & 0x40000) ? (color >> 8) : (color & 0xFF);
406  byte dstColor = vram.readVRAMDirect(addr);
407  byte newColor = logOp(lut, srcColor, dstColor);
408  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
409  byte mask2 = mask1 & (0xF0 >> (4 * (x & 1)));
410  byte result = (dstColor & ~mask2) | (newColor & mask2);
411  vram.writeVRAMDirect(addr, result);
412 }
413 
414 // 2 bpp --------------------------------------------------------------
415 inline unsigned V9990CmdEngine::V9990Bpp2::getPitch(unsigned width)
416 {
417  return width / 4;
418 }
419 
420 inline unsigned V9990CmdEngine::V9990Bpp2::addressOf(
421  unsigned x, unsigned y, unsigned pitch)
422 {
423  return V9990VRAM::transformBx(((x / 4) & (pitch - 1)) + y * pitch) & 0x7FFFF;
424 }
425 
426 inline byte V9990CmdEngine::V9990Bpp2::point(
427  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch)
428 {
429  return vram.readVRAMDirect(addressOf(x, y, pitch));
430 }
431 
432 inline byte V9990CmdEngine::V9990Bpp2::shift(
433  byte value, unsigned fromX, unsigned toX)
434 {
435  int shift = 2 * ((toX & 3) - (fromX & 3));
436  return (shift > 0) ? (value >> shift) : (value << -shift);
437 }
438 
439 inline byte V9990CmdEngine::V9990Bpp2::shiftMask(unsigned x)
440 {
441  return 0xC0 >> (2 * (x & 3));
442 }
443 
444 inline const byte* V9990CmdEngine::V9990Bpp2::getLogOpLUT(byte op)
445 {
446  return getLogOpImpl((op & 0x10) ? LOG_BPP2 : LOG_NO_T, op);
447 }
448 
449 inline byte V9990CmdEngine::V9990Bpp2::logOp(
450  const byte* lut, byte src, byte dst)
451 {
452  return lut[256 * dst + src];
453 }
454 
455 inline void V9990CmdEngine::V9990Bpp2::pset(
456  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
457  byte srcColor, word mask, const byte* lut, byte /*op*/)
458 {
459  unsigned addr = addressOf(x, y, pitch);
460  byte dstColor = vram.readVRAMDirect(addr);
461  byte newColor = logOp(lut, srcColor, dstColor);
462  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
463  byte mask2 = mask1 & shiftMask(x);
464  byte result = (dstColor & ~mask2) | (newColor & mask2);
465  vram.writeVRAMDirect(addr, result);
466 }
467 
468 inline void V9990CmdEngine::V9990Bpp2::psetColor(
469  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
470  word color, word mask, const byte* lut, byte /*op*/)
471 {
472  unsigned addr = addressOf(x, y, pitch);
473  byte srcColor = (addr & 0x40000) ? (color >> 8) : (color & 0xFF);
474  byte dstColor = vram.readVRAMDirect(addr);
475  byte newColor = logOp(lut, srcColor, dstColor);
476  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
477  byte mask2 = mask1 & (0xC0 >> (2 * (x & 3)));
478  byte result = (dstColor & ~mask2) | (newColor & mask2);
479  vram.writeVRAMDirect(addr, result);
480 }
481 
482 // 4 bpp --------------------------------------------------------------
483 inline unsigned V9990CmdEngine::V9990Bpp4::getPitch(unsigned width)
484 {
485  return width / 2;
486 }
487 
488 inline unsigned V9990CmdEngine::V9990Bpp4::addressOf(
489  unsigned x, unsigned y, unsigned pitch)
490 {
491  return V9990VRAM::transformBx(((x / 2) & (pitch - 1)) + y * pitch) & 0x7FFFF;
492 }
493 
494 inline byte V9990CmdEngine::V9990Bpp4::point(
495  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch)
496 {
497  return vram.readVRAMDirect(addressOf(x, y, pitch));
498 }
499 
500 inline byte V9990CmdEngine::V9990Bpp4::shift(
501  byte value, unsigned fromX, unsigned toX)
502 {
503  int shift = 4 * ((toX & 1) - (fromX & 1));
504  return (shift > 0) ? (value >> shift) : (value << -shift);
505 }
506 
507 inline byte V9990CmdEngine::V9990Bpp4::shiftMask(unsigned x)
508 {
509  return (x & 1) ? 0x0F : 0xF0;
510 }
511 
512 inline const byte* V9990CmdEngine::V9990Bpp4::getLogOpLUT(byte op)
513 {
514  return getLogOpImpl((op & 0x10) ? LOG_BPP4 : LOG_NO_T, op);
515 }
516 
517 inline byte V9990CmdEngine::V9990Bpp4::logOp(
518  const byte* lut, byte src, byte dst)
519 {
520  return lut[256 * dst + src];
521 }
522 
523 inline void V9990CmdEngine::V9990Bpp4::pset(
524  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
525  byte srcColor, word mask, const byte* lut, byte /*op*/)
526 {
527  unsigned addr = addressOf(x, y, pitch);
528  byte dstColor = vram.readVRAMDirect(addr);
529  byte newColor = logOp(lut, srcColor, dstColor);
530  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
531  byte mask2 = mask1 & shiftMask(x);
532  byte result = (dstColor & ~mask2) | (newColor & mask2);
533  vram.writeVRAMDirect(addr, result);
534 }
535 
536 inline void V9990CmdEngine::V9990Bpp4::psetColor(
537  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
538  word color, word mask, const byte* lut, byte /*op*/)
539 {
540  unsigned addr = addressOf(x, y, pitch);
541  byte srcColor = (addr & 0x40000) ? (color >> 8) : (color & 0xFF);
542  byte dstColor = vram.readVRAMDirect(addr);
543  byte newColor = logOp(lut, srcColor, dstColor);
544  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
545  byte mask2 = mask1 & (0xF0 >> (4 * (x & 1)));
546  byte result = (dstColor & ~mask2) | (newColor & mask2);
547  vram.writeVRAMDirect(addr, result);
548 }
549 
550 // 8 bpp --------------------------------------------------------------
551 inline unsigned V9990CmdEngine::V9990Bpp8::getPitch(unsigned width)
552 {
553  return width;
554 }
555 
556 inline unsigned V9990CmdEngine::V9990Bpp8::addressOf(
557  unsigned x, unsigned y, unsigned pitch)
558 {
559  return V9990VRAM::transformBx((x & (pitch - 1)) + y * pitch) & 0x7FFFF;
560 }
561 
562 inline byte V9990CmdEngine::V9990Bpp8::point(
563  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch)
564 {
565  return vram.readVRAMDirect(addressOf(x, y, pitch));
566 }
567 
568 inline byte V9990CmdEngine::V9990Bpp8::shift(
569  byte value, unsigned /*fromX*/, unsigned /*toX*/)
570 {
571  return value;
572 }
573 
574 inline byte V9990CmdEngine::V9990Bpp8::shiftMask(unsigned /*x*/)
575 {
576  return 0xFF;
577 }
578 
579 inline const byte* V9990CmdEngine::V9990Bpp8::getLogOpLUT(byte op)
580 {
581  return getLogOpImpl((op & 0x10) ? LOG_BPP8 : LOG_NO_T, op);
582 }
583 
584 inline byte V9990CmdEngine::V9990Bpp8::logOp(
585  const byte* lut, byte src, byte dst)
586 {
587  return lut[256 * dst + src];
588 }
589 
590 inline void V9990CmdEngine::V9990Bpp8::pset(
591  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
592  byte srcColor, word mask, const byte* lut, byte /*op*/)
593 {
594  unsigned addr = addressOf(x, y, pitch);
595  byte dstColor = vram.readVRAMDirect(addr);
596  byte newColor = logOp(lut, srcColor, dstColor);
597  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
598  byte result = (dstColor & ~mask1) | (newColor & mask1);
599  vram.writeVRAMDirect(addr, result);
600 }
601 
602 inline void V9990CmdEngine::V9990Bpp8::psetColor(
603  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
604  word color, word mask, const byte* lut, byte /*op*/)
605 {
606  unsigned addr = addressOf(x, y, pitch);
607  byte srcColor = (addr & 0x40000) ? (color >> 8) : (color & 0xFF);
608  byte dstColor = vram.readVRAMDirect(addr);
609  byte newColor = logOp(lut, srcColor, dstColor);
610  byte mask1 = (addr & 0x40000) ? (mask >> 8) : (mask & 0xFF);
611  byte result = (dstColor & ~mask1) | (newColor & mask1);
612  vram.writeVRAMDirect(addr, result);
613 }
614 
615 // 16 bpp -------------------------------------------------------------
616 inline unsigned V9990CmdEngine::V9990Bpp16::getPitch(unsigned width)
617 {
618  //return width * 2;
619  return width;
620 }
621 
622 inline unsigned V9990CmdEngine::V9990Bpp16::addressOf(
623  unsigned x, unsigned y, unsigned pitch)
624 {
625  //return V9990VRAM::transformBx(((x * 2) & (pitch - 1)) + y * pitch) & 0x7FFFF;
626  return ((x & (pitch - 1)) + y * pitch) & 0x3FFFF;
627 }
628 
629 inline word V9990CmdEngine::V9990Bpp16::point(
630  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch)
631 {
632  unsigned addr = addressOf(x, y, pitch);
633  return vram.readVRAMDirect(addr + 0x00000) +
634  vram.readVRAMDirect(addr + 0x40000) * 256;
635 }
636 
637 inline word V9990CmdEngine::V9990Bpp16::shift(
638  word value, unsigned /*fromX*/, unsigned /*toX*/)
639 {
640  return value;
641 }
642 
643 inline word V9990CmdEngine::V9990Bpp16::shiftMask(unsigned /*x*/)
644 {
645  return 0xFFFF;
646 }
647 
648 inline const byte* V9990CmdEngine::V9990Bpp16::getLogOpLUT(byte op)
649 {
650  return getLogOpImpl(LOG_NO_T, op);
651 }
652 
653 inline word V9990CmdEngine::V9990Bpp16::logOp(
654  const byte* lut, word src, word dst, bool transp)
655 {
656  if (transp && (src == 0)) return dst;
657  return (lut[((dst & 0x00FF) << 8) + ((src & 0x00FF) >> 0)] << 0) +
658  (lut[((dst & 0xFF00) << 0) + ((src & 0xFF00) >> 8)] << 8);
659 }
660 
661 inline void V9990CmdEngine::V9990Bpp16::pset(
662  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
663  word srcColor, word mask, const byte* lut, byte op)
664 {
665  unsigned addr = addressOf(x, y, pitch);
666  word dstColor = vram.readVRAMDirect(addr + 0x00000) +
667  vram.readVRAMDirect(addr + 0x40000) * 256;
668  word newColor = logOp(lut, srcColor, dstColor, (op & 0x10) != 0);
669  word result = (dstColor & ~mask) | (newColor & mask);
670  vram.writeVRAMDirect(addr + 0x00000, result & 0xFF);
671  vram.writeVRAMDirect(addr + 0x40000, result >> 8);
672 }
673 
674 inline void V9990CmdEngine::V9990Bpp16::psetColor(
675  V9990VRAM& vram, unsigned x, unsigned y, unsigned pitch,
676  word srcColor, word mask, const byte* lut, byte op)
677 {
678  unsigned addr = addressOf(x, y, pitch);
679  word dstColor = vram.readVRAMDirect(addr + 0x00000) +
680  vram.readVRAMDirect(addr + 0x40000) * 256;
681  word newColor = logOp(lut, srcColor, dstColor, (op & 0x10) != 0);
682  word result = (dstColor & ~mask) | (newColor & mask);
683  vram.writeVRAMDirect(addr + 0x00000, result & 0xFF);
684  vram.writeVRAMDirect(addr + 0x40000, result >> 8);
685 }
686 
687 // ====================================================================
690 V9990CmdEngine::V9990CmdEngine(V9990& vdp_, EmuTime::param time_,
691  RenderSettings& settings_)
692  : settings(settings_), vdp(vdp_), vram(vdp.getVRAM()), engineTime(time_)
693 {
694  cmdTraceSetting = vdp.getMotherBoard().getSharedStuff<BooleanSetting>(
695  "v9990cmdtrace",
696  vdp.getCommandController(), "v9990cmdtrace",
697  "V9990 command tracing on/off", false);
698 
699  auto& cmdTimingSetting = settings.getCmdTimingSetting();
700  update(cmdTimingSetting);
701  cmdTimingSetting.attach(*this);
702 
703  reset(time_);
704 
705  // avoid UMR on savestate
706  srcAddress = dstAddress = nbBytes = 0;
707  ASX = ADX = ANX = ANY = 0;
708  SX = SY = DX = DY = NX = NY = 0;
709  WM = fgCol = bgCol = 0;
710  ARG = LOG = 0;
711  data = bitsLeft = partial = 0;
712 }
713 
715 {
716  settings.getCmdTimingSetting().detach(*this);
717 }
718 
719 void V9990CmdEngine::reset(EmuTime::param /*time*/)
720 {
721  CMD = 0;
722  status = 0;
723  borderX = 0;
724  endAfterRead = false;
725 }
726 
727 void V9990CmdEngine::setCmdReg(byte reg, byte value, EmuTime::param time)
728 {
729  sync(time);
730  switch(reg - 32) {
731  case 0: // SX low
732  SX = (SX & 0x0700) | value;
733  break;
734  case 1: // SX high
735  SX = (SX & 0x00FF) | ((value & 0x07) << 8);
736  break;
737  case 2: // SY low
738  SY = (SY & 0x0F00) | value;
739  break;
740  case 3: // SY high
741  SY = (SY & 0x00FF) | ((value & 0x0F) << 8);
742  break;
743  case 4: // DX low
744  DX = (DX & 0x0700) | value;
745  break;
746  case 5: // DX high
747  DX = (DX & 0x00FF) | ((value & 0x07) << 8);
748  break;
749  case 6: // DY low
750  DY = (DY & 0x0F00) | value;
751  break;
752  case 7: // DY high
753  DY = (DY & 0x00FF) | ((value & 0x0F) << 8);
754  break;
755  case 8: // NX low
756  NX = (NX & 0x0F00) | value;
757  break;
758  case 9: // NX high
759  NX = (NX & 0x00FF) | ((value & 0x0F) << 8);
760  break;
761  case 10: // NY low
762  NY = (NY & 0x0F00) | value;
763  break;
764  case 11: // NY high
765  NY = (NY & 0x00FF) | ((value & 0x0F) << 8);
766  break;
767  case 12: // ARG
768  ARG = value & 0x0F;
769  break;
770  case 13: // LOGOP
771  LOG = value & 0x1F;
772  break;
773  case 14: // write mask low
774  WM = (WM & 0xFF00) | value;
775  break;
776  case 15: // write mask high
777  WM = (WM & 0x00FF) | (value << 8);
778  break;
779  case 16: // Font color - FG low
780  fgCol = (fgCol & 0xFF00) | value;
781  break;
782  case 17: // Font color - FG high
783  fgCol = (fgCol & 0x00FF) | (value << 8);
784  break;
785  case 18: // Font color - BG low
786  bgCol = (bgCol & 0xFF00) | value;
787  break;
788  case 19: // Font color - BG high
789  bgCol = (bgCol & 0x00FF) | (value << 8);
790  break;
791  case 20: { // CMD
792  CMD = value;
793  if (cmdTraceSetting->getBoolean()) {
794  reportV9990Command();
795  }
796  status |= CE;
797 
798  // TODO do this when mode changes instead of at the start of a command.
799  setCommandMode();
800 
801  //currentCommand->start(time);
802  switch (cmdMode | (CMD >> 4)) {
803  case 0x00: case 0x10: case 0x20: case 0x30: case 0x40: case 0x50:
804  startSTOP(time); break;
805 
806  case 0x01: case 0x11: case 0x21: case 0x31: case 0x41:
807  startLMMC (time); break;
808  case 0x51:
809  startLMMC16(time); break;
810 
811  case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
812  startLMMV(time); break;
813 
814  case 0x03: case 0x13: case 0x23: case 0x33: case 0x43:
815  startLMCM (time); break;
816  case 0x53:
817  startLMCM16(time); break;
818 
819  case 0x04: case 0x14: case 0x24: case 0x34: case 0x44: case 0x54:
820  startLMMM(time); break;
821 
822  case 0x05: case 0x15: case 0x25: case 0x35: case 0x45: case 0x55:
823  startCMMC(time); break;
824 
825  case 0x06: case 0x16: case 0x26: case 0x36: case 0x46: case 0x56:
826  startCMMK(time); break;
827 
828  case 0x07: case 0x17: case 0x27: case 0x37: case 0x47: case 0x57:
829  startCMMM(time); break;
830 
831  case 0x08: case 0x18: case 0x28: case 0x38: case 0x48: case 0x58:
832  startBMXL(time); break;
833 
834  case 0x09: case 0x19: case 0x29: case 0x39: case 0x49: case 0x59:
835  startBMLX(time); break;
836 
837  case 0x0A: case 0x1A: case 0x2A: case 0x3A: case 0x4A:
838  startBMLL (time); break;
839  case 0x5A:
840  startBMLL16(time); break;
841 
842  case 0x0B: case 0x1B: case 0x2B: case 0x3B: case 0x4B: case 0x5B:
843  startLINE(time); break;
844 
845  case 0x0C: case 0x1C: case 0x2C: case 0x3C: case 0x4C: case 0x5C:
846  startSRCH(time); break;
847 
848  case 0x0D: startPOINT<V9990P1 >(time); break;
849  case 0x1D: startPOINT<V9990P2 >(time); break;
850  case 0x2D: startPOINT<V9990Bpp2 >(time); break;
851  case 0x3D: startPOINT<V9990Bpp4 >(time); break;
852  case 0x4D: startPOINT<V9990Bpp8 >(time); break;
853  case 0x5D: startPOINT<V9990Bpp16>(time); break;
854 
855  case 0x0E: startPSET<V9990P1 >(time); break;
856  case 0x1E: startPSET<V9990P2 >(time); break;
857  case 0x2E: startPSET<V9990Bpp2 >(time); break;
858  case 0x3E: startPSET<V9990Bpp4 >(time); break;
859  case 0x4E: startPSET<V9990Bpp8 >(time); break;
860  case 0x5E: startPSET<V9990Bpp16>(time); break;
861 
862  case 0x0F: case 0x1F: case 0x2F: case 0x3F: case 0x4F: case 0x5F:
863  startADVN(time); break;
864 
865  default: UNREACHABLE;
866  }
867 
868  // Finish command now if instantaneous command timing is active.
869  if (brokenTiming) {
870  sync(time);
871  }
872  break;
873  }
874  }
875 }
876 
877 void V9990CmdEngine::setCommandMode()
878 {
879  auto dispMode = vdp.getDisplayMode();
880  if (dispMode == P1) {
881  cmdMode = 0 << 4; // P1;
882  } else if (dispMode == P2) {
883  cmdMode = 1 << 4; // P2;
884  } else { // Bx
885  switch (vdp.getColorMode()) {
886  default:
887  UNREACHABLE;
888  case BP2:
889  cmdMode = 2 << 4; // BPP2;
890  break;
891  case PP:
892  case BP4:
893  cmdMode = 3 << 4; // BPP4;
894  break;
895  case BYUV:
896  case BYUVP:
897  case BYJK:
898  case BYJKP:
899  case BD8:
900  case BP6:
901  cmdMode = 4 << 4; // BPP8;
902  break;
903  case BD16:
904  cmdMode = 5 << 4; // BPP16;
905  break;
906  }
907  }
908 }
909 
910 void V9990CmdEngine::reportV9990Command() const
911 {
912  const char* const COMMANDS[16] = {
913  "STOP", "LMMC", "LMMV", "LMCM",
914  "LMMM", "CMMC", "CMMK", "CMMM",
915  "BMXL", "BMLX", "BMLL", "LINE",
916  "SRCH", "POINT","PSET", "ADVN"
917  };
918  std::cerr << "V9990Cmd " << COMMANDS[CMD >> 4]
919  << " SX=" << std::dec << SX
920  << " SY=" << std::dec << SY
921  << " DX=" << std::dec << DX
922  << " DY=" << std::dec << DY
923  << " NX=" << std::dec << NX
924  << " NY=" << std::dec << NY
925  << " ARG=" << std::hex << int(ARG)
926  << " LOG=" << std::hex << int(LOG)
927  << " WM=" << std::hex << WM
928  << " FC=" << std::hex << fgCol
929  << " BC=" << std::hex << bgCol
930  << " CMD=" << std::hex << int(CMD)
931  << '\n';
932 }
933 
934 void V9990CmdEngine::update(const Setting& setting) noexcept
935 {
936  brokenTiming = static_cast<const EnumSetting<bool>&>(setting).getEnum();
937 }
938 
939 // STOP
940 void V9990CmdEngine::startSTOP(EmuTime::param time)
941 {
942  cmdReady(time);
943 }
944 
945 void V9990CmdEngine::executeSTOP(EmuTime::param /*limit*/)
946 {
947  UNREACHABLE;
948 }
949 
950 // LMMC
951 void V9990CmdEngine::startLMMC(EmuTime::param /*time*/)
952 {
953  ANX = getWrappedNX();
954  ANY = getWrappedNY();
955  status |= TR;
956 }
957 void V9990CmdEngine::startLMMC16(EmuTime::param time)
958 {
959  bitsLeft = 1;
960  startLMMC(time);
961 }
962 
963 template<>
964 void V9990CmdEngine::executeLMMC<V9990CmdEngine::V9990Bpp16>(EmuTime::param limit)
965 {
966  if (!(status & TR)) {
967  status |= TR;
968  if (bitsLeft) {
969  bitsLeft = 0;
970  partial = data;
971  } else {
972  bitsLeft = 1;
973  word value = (data << 8) | partial;
974  unsigned pitch = V9990Bpp16::getPitch(vdp.getImageWidth());
975  const byte* lut = V9990Bpp16::getLogOpLUT(LOG);
976  V9990Bpp16::pset(vram, DX, DY, pitch, value, WM, lut, LOG);
977  int dx = (ARG & DIX) ? -1 : 1;
978  DX += dx;
979  if (!--(ANX)) {
980  int dy = (ARG & DIY) ? -1 : 1;
981  DX -= (NX * dx);
982  DY += dy;
983  if (!--(ANY)) {
984  cmdReady(limit);
985  } else {
986  ANX = getWrappedNX();
987  }
988  }
989  }
990  }
991 }
992 
993 template<typename Mode>
994 void V9990CmdEngine::executeLMMC(EmuTime::param limit)
995 {
996  if (!(status & TR)) {
997  status |= TR;
998  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
999  const byte* lut = Mode::getLogOpLUT(LOG);
1000  for (int i = 0; (ANY > 0) && (i < Mode::PIXELS_PER_BYTE); ++i) {
1001  byte d = Mode::shift(data, i, DX);
1002  Mode::pset(vram, DX, DY, pitch, d, WM, lut, LOG);
1003 
1004  int dx = (ARG & DIX) ? -1 : 1;
1005  DX += dx;
1006  if (!--(ANX)) {
1007  int dy = (ARG & DIY) ? -1 : 1;
1008  DX -= (NX * dx);
1009  DY += dy;
1010  if (!--(ANY)) {
1011  cmdReady(limit);
1012  } else {
1013  ANX = NX;
1014  }
1015  }
1016  }
1017  }
1018 }
1019 
1020 // LMMV
1021 void V9990CmdEngine::startLMMV(EmuTime::param time)
1022 {
1023  engineTime = time;
1024  ANX = getWrappedNX();
1025  ANY = getWrappedNY();
1026 }
1027 
1028 template<typename Mode>
1029 void V9990CmdEngine::executeLMMV(EmuTime::param limit)
1030 {
1031  // TODO can be optimized a lot
1032 
1033  auto delta = getTiming(*this, LMMV_TIMING);
1034  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
1035  int dx = (ARG & DIX) ? -1 : 1;
1036  int dy = (ARG & DIY) ? -1 : 1;
1037  const byte* lut = Mode::getLogOpLUT(LOG);
1038  while (engineTime < limit) {
1039  engineTime += delta;
1040  Mode::psetColor(vram, DX, DY, pitch, fgCol, WM, lut, LOG);
1041 
1042  DX += dx;
1043  if (!--(ANX)) {
1044  DX -= (NX * dx);
1045  DY += dy;
1046  if (!--(ANY)) {
1047  cmdReady(engineTime);
1048  return;
1049  } else {
1050  ANX = getWrappedNX();
1051  }
1052  }
1053  }
1054 }
1055 
1056 // LMCM
1057 void V9990CmdEngine::startLMCM(EmuTime::param /*time*/)
1058 {
1059  ANX = getWrappedNX();
1060  ANY = getWrappedNY();
1061  status &= ~TR;
1062  endAfterRead = false;
1063 }
1064 void V9990CmdEngine::startLMCM16(EmuTime::param time)
1065 {
1066  bitsLeft = 0;
1067  startLMCM(time);
1068 }
1069 
1070 template<typename Mode>
1071 void V9990CmdEngine::executeLMCM(EmuTime::param /*limit*/)
1072 {
1073  if (!(status & TR)) {
1074  status |= TR;
1075  if ((Mode::BITS_PER_PIXEL == 16) && bitsLeft) {
1076  bitsLeft = 0;
1077  data = partial;
1078  return;
1079  }
1080  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
1081  typename Mode::Type d = 0;
1082  for (int i = 0; (ANY > 0) && (i < Mode::PIXELS_PER_BYTE); ++i) {
1083  auto src = Mode::point(vram, SX, SY, pitch);
1084  d |= Mode::shift(src, SX, i) & Mode::shiftMask(i);
1085 
1086  int dx = (ARG & DIX) ? -1 : 1;
1087  SX += dx;
1088  if (!--(ANX)) {
1089  int dy = (ARG & DIY) ? -1 : 1;
1090  SX -= (NX * dx);
1091  SY += dy;
1092  if (!--(ANY)) {
1093  endAfterRead = true;
1094  } else {
1095  ANX = getWrappedNX();
1096  }
1097  }
1098  }
1099  if (Mode::BITS_PER_PIXEL == 16) {
1100  unsigned tmp = d; // workaround for VC++ warning C4333
1101  // (in case Mode::Type == byte and
1102  // Mode::BITS_PER_PIXEL == 8)
1103  data = tmp & 0xff;
1104  partial = tmp >> 8;
1105  bitsLeft = 1;
1106  } else {
1107  data = byte(d);
1108  }
1109  }
1110 }
1111 
1112 // LMMM
1113 void V9990CmdEngine::startLMMM(EmuTime::param time)
1114 {
1115  engineTime = time;
1116  ANX = getWrappedNX();
1117  ANY = getWrappedNY();
1118 }
1119 
1120 template<typename Mode>
1121 void V9990CmdEngine::executeLMMM(EmuTime::param limit)
1122 {
1123  // TODO can be optimized a lot
1124 
1125  auto delta = getTiming(*this, LMMM_TIMING);
1126  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
1127  int dx = (ARG & DIX) ? -1 : 1;
1128  int dy = (ARG & DIY) ? -1 : 1;
1129  const byte* lut = Mode::getLogOpLUT(LOG);
1130  while (engineTime < limit) {
1131  engineTime += delta;
1132  auto src = Mode::point(vram, SX, SY, pitch);
1133  src = Mode::shift(src, SX, DX);
1134  Mode::pset(vram, DX, DY, pitch, src, WM, lut, LOG);
1135 
1136  DX += dx;
1137  SX += dx;
1138  if (!--(ANX)) {
1139  DX -= (NX * dx);
1140  SX -= (NX * dx);
1141  DY += dy;
1142  SY += dy;
1143  if (!--(ANY)) {
1144  cmdReady(engineTime);
1145  return;
1146  } else {
1147  ANX = getWrappedNX();
1148  }
1149  }
1150  }
1151 }
1152 
1153 // CMMC
1154 void V9990CmdEngine::startCMMC(EmuTime::param /*time*/)
1155 {
1156  ANX = getWrappedNX();
1157  ANY = getWrappedNY();
1158  status |= TR;
1159 }
1160 
1161 template<typename Mode>
1162 void V9990CmdEngine::executeCMMC(EmuTime::param limit)
1163 {
1164  if (!(status & TR)) {
1165  status |= TR;
1166 
1167  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
1168  int dx = (ARG & DIX) ? -1 : 1;
1169  int dy = (ARG & DIY) ? -1 : 1;
1170  const byte* lut = Mode::getLogOpLUT(LOG);
1171  for (auto i : xrange(8)) {
1172  (void)i;
1173  bool bit = (data & 0x80) != 0;
1174  data <<= 1;
1175 
1176  word src = bit ? fgCol : bgCol;
1177  Mode::psetColor(vram, DX, DY, pitch, src, WM, lut, LOG);
1178 
1179  DX += dx;
1180  if (!--(ANX)) {
1181  DX -= (NX * dx);
1182  DY += dy;
1183  if (!--(ANY)) {
1184  cmdReady(limit);
1185  return;
1186  } else {
1187  ANX = getWrappedNX();
1188  }
1189  }
1190  }
1191  }
1192 }
1193 
1194 // CMMK
1195 void V9990CmdEngine::startCMMK(EmuTime::param time)
1196 {
1197  std::cout << "V9990: CMMK not yet implemented\n";
1198  cmdReady(time); // TODO dummy implementation
1199 }
1200 
1201 void V9990CmdEngine::executeCMMK(EmuTime::param /*limit*/)
1202 {
1203  UNREACHABLE;
1204 }
1205 
1206 // CMMM
1207 void V9990CmdEngine::startCMMM(EmuTime::param time)
1208 {
1209  engineTime = time;
1210  srcAddress = (SX & 0xFF) + ((SY & 0x7FF) << 8);
1211  ANX = getWrappedNX();
1212  ANY = getWrappedNY();
1213  bitsLeft = 0;
1214 }
1215 
1216 template<typename Mode>
1217 void V9990CmdEngine::executeCMMM(EmuTime::param limit)
1218 {
1219  // TODO can be optimized a lot
1220 
1221  auto delta = getTiming(*this, CMMM_TIMING);
1222  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
1223  int dx = (ARG & DIX) ? -1 : 1;
1224  int dy = (ARG & DIY) ? -1 : 1;
1225  const byte* lut = Mode::getLogOpLUT(LOG);
1226  while (engineTime < limit) {
1227  engineTime += delta;
1228  if (!bitsLeft) {
1229  data = vram.readVRAMBx(srcAddress++);
1230  bitsLeft = 8;
1231  }
1232  --bitsLeft;
1233  bool bit = (data & 0x80) != 0;
1234  data <<= 1;
1235 
1236  word color = bit ? fgCol : bgCol;
1237  Mode::psetColor(vram, DX, DY, pitch, color, WM, lut, LOG);
1238 
1239  DX += dx;
1240  if (!--(ANX)) {
1241  DX -= (NX * dx);
1242  DY += dy;
1243  if (!--(ANY)) {
1244  cmdReady(engineTime);
1245  return;
1246  } else {
1247  ANX = getWrappedNX();
1248  }
1249  }
1250  }
1251 }
1252 
1253 // BMXL
1254 void V9990CmdEngine::startBMXL(EmuTime::param time)
1255 {
1256  engineTime = time;
1257  srcAddress = (SX & 0xFF) + ((SY & 0x7FF) << 8);
1258  ANX = getWrappedNX();
1259  ANY = getWrappedNY();
1260 }
1261 
1262 template<>
1263 void V9990CmdEngine::executeBMXL<V9990CmdEngine::V9990Bpp16>(EmuTime::param limit)
1264 {
1265  // timing value is times 2, because it does 2 bytes per iteration:
1266  auto delta = getTiming(*this, BMXL_TIMING) * 2;
1267  unsigned pitch = V9990Bpp16::getPitch(vdp.getImageWidth());
1268  int dx = (ARG & DIX) ? -1 : 1;
1269  int dy = (ARG & DIY) ? -1 : 1;
1270  const byte* lut = V9990Bpp16::getLogOpLUT(LOG);
1271 
1272  while (engineTime < limit) {
1273  engineTime += delta;
1274  word src = vram.readVRAMBx(srcAddress + 0) +
1275  vram.readVRAMBx(srcAddress + 1) * 256;
1276  srcAddress += 2;
1277  V9990Bpp16::pset(vram, DX, DY, pitch, src, WM, lut, LOG);
1278  DX += dx;
1279  if (!--(ANX)) {
1280  DX -= (NX * dx);
1281  DY += dy;
1282  if (!--(ANY)) {
1283  cmdReady(engineTime);
1284  return;
1285  } else {
1286  ANX = getWrappedNX();
1287  }
1288  }
1289  }
1290 }
1291 
1292 template<typename Mode>
1293 void V9990CmdEngine::executeBMXL(EmuTime::param limit)
1294 {
1295  auto delta = getTiming(*this, BMXL_TIMING);
1296  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
1297  int dx = (ARG & DIX) ? -1 : 1;
1298  int dy = (ARG & DIY) ? -1 : 1;
1299  const byte* lut = Mode::getLogOpLUT(LOG);
1300 
1301  while (engineTime < limit) {
1302  engineTime += delta;
1303  byte d = vram.readVRAMBx(srcAddress++);
1304  for (int i = 0; (ANY > 0) && (i < Mode::PIXELS_PER_BYTE); ++i) {
1305  Mode::pset(vram, DX, DY, pitch, d, WM, lut, LOG);
1306  DX += dx;
1307  if (!--(ANX)) {
1308  DX -= (NX * dx);
1309  DY += dy;
1310  if (!--(ANY)) {
1311  cmdReady(engineTime);
1312  return;
1313  } else {
1314  ANX = getWrappedNX();
1315  }
1316  }
1317  }
1318  }
1319 }
1320 
1321 // BMLX
1322 void V9990CmdEngine::startBMLX(EmuTime::param time)
1323 {
1324  engineTime = time;
1325  dstAddress = (DX & 0xFF) + ((DY & 0x7FF) << 8);
1326  ANX = getWrappedNX();
1327  ANY = getWrappedNY();
1328 }
1329 
1330 template<>
1331 void V9990CmdEngine::executeBMLX<V9990CmdEngine::V9990Bpp16>(EmuTime::param limit)
1332 {
1333  // TODO test corner cases, timing
1334  auto delta = getTiming(*this, BMLX_TIMING);
1335  unsigned pitch = V9990Bpp16::getPitch(vdp.getImageWidth());
1336  int dx = (ARG & DIX) ? -1 : 1;
1337  int dy = (ARG & DIY) ? -1 : 1;
1338 
1339  while (engineTime < limit) {
1340  engineTime += delta;
1341  auto src = V9990Bpp16::point(vram, SX, SY, pitch);
1342  vram.writeVRAMBx(dstAddress++, src & 0xFF);
1343  vram.writeVRAMBx(dstAddress++, src >> 8);
1344  SX += dx;
1345  if (!--(ANX)) {
1346  SX -= (NX * dx);
1347  SY += dy;
1348  if (!--(ANY)) {
1349  cmdReady(engineTime);
1350  return;
1351  } else {
1352  ANX = getWrappedNX();
1353  }
1354  }
1355  }
1356 }
1357 template<typename Mode>
1358 void V9990CmdEngine::executeBMLX(EmuTime::param limit)
1359 {
1360  // TODO test corner cases, timing
1361  auto delta = getTiming(*this, BMLX_TIMING);
1362  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
1363  int dx = (ARG & DIX) ? -1 : 1;
1364  int dy = (ARG & DIY) ? -1 : 1;
1365 
1366  while (engineTime < limit) {
1367  engineTime += delta;
1368  byte d = 0;
1369  for (auto i : xrange(Mode::PIXELS_PER_BYTE)) {
1370  auto src = Mode::point(vram, SX, SY, pitch);
1371  d |= Mode::shift(src, SX, i) & Mode::shiftMask(i);
1372  SX += dx;
1373  if (!--(ANX)) {
1374  SX -= (NX * dx);
1375  SY += dy;
1376  if (!--(ANY)) {
1377  vram.writeVRAMBx(dstAddress++, d);
1378  cmdReady(engineTime);
1379  return;
1380  } else {
1381  ANX = getWrappedNX();
1382  }
1383  }
1384  }
1385  vram.writeVRAMBx(dstAddress++, d);
1386  }
1387 }
1388 
1389 // BMLL
1390 void V9990CmdEngine::startBMLL(EmuTime::param time)
1391 {
1392  engineTime = time;
1393  srcAddress = (SX & 0xFF) + ((SY & 0x7FF) << 8);
1394  dstAddress = (DX & 0xFF) + ((DY & 0x7FF) << 8);
1395  nbBytes = (NX & 0xFF) + ((NY & 0x7FF) << 8);
1396  if (nbBytes == 0) {
1397  nbBytes = 0x80000;
1398  }
1399 }
1400 void V9990CmdEngine::startBMLL16(EmuTime::param time)
1401 {
1402  startBMLL(time);
1403  // TODO is this correct???
1404  // drop last bit
1405  srcAddress >>= 1;
1406  dstAddress >>= 1;
1407  nbBytes >>= 1;
1408 }
1409 
1410 template<>
1411 void V9990CmdEngine::executeBMLL<V9990CmdEngine::V9990Bpp16>(EmuTime::param limit)
1412 {
1413  // TODO DIX DIY?
1414  // timing value is times 2, because it does 2 bytes per iteration:
1415  auto delta = getTiming(*this, BMLL_TIMING) * 2;
1416  const byte* lut = V9990Bpp16::getLogOpLUT(LOG);
1417  bool transp = (LOG & 0x10) != 0;
1418  while (engineTime < limit) {
1419  engineTime += delta;
1420  // VRAM always mapped as in Bx modes
1421  word srcColor = vram.readVRAMDirect(srcAddress + 0x00000) +
1422  vram.readVRAMDirect(srcAddress + 0x40000) * 256;
1423  word dstColor = vram.readVRAMDirect(dstAddress + 0x00000) +
1424  vram.readVRAMDirect(dstAddress + 0x40000) * 256;
1425  word newColor = V9990Bpp16::logOp(lut, srcColor, dstColor, transp);
1426  word result = (dstColor & ~WM) | (newColor & WM);
1427  vram.writeVRAMDirect(dstAddress + 0x00000, result & 0xFF);
1428  vram.writeVRAMDirect(dstAddress + 0x40000, result >> 8);
1429  srcAddress = (srcAddress + 1) & 0x3FFFF;
1430  dstAddress = (dstAddress + 1) & 0x3FFFF;
1431  if (!--nbBytes) {
1432  cmdReady(engineTime);
1433  return;
1434  }
1435  }
1436 }
1437 
1438 template<typename Mode>
1439 void V9990CmdEngine::executeBMLL(EmuTime::param limit)
1440 {
1441  // TODO DIX DIY?
1442  auto delta = getTiming(*this, BMLL_TIMING);
1443  const byte* lut = Mode::getLogOpLUT(LOG);
1444  while (engineTime < limit) {
1445  engineTime += delta;
1446  // VRAM always mapped as in Bx modes
1447  byte srcColor = vram.readVRAMBx(srcAddress);
1448  unsigned addr = V9990VRAM::transformBx(dstAddress);
1449  byte dstColor = vram.readVRAMDirect(addr);
1450  byte newColor = Mode::logOp(lut, srcColor, dstColor);
1451  byte mask = (addr & 0x40000) ? (WM >> 8) : (WM & 0xFF);
1452  byte result = (dstColor & ~mask) | (newColor & mask);
1453  vram.writeVRAMDirect(addr, result);
1454  srcAddress = (srcAddress + 1) & 0x7FFFF;
1455  dstAddress = (dstAddress + 1) & 0x7FFFF;
1456  if (!--nbBytes) {
1457  cmdReady(engineTime);
1458  return;
1459  }
1460  }
1461 }
1462 
1463 // LINE
1464 void V9990CmdEngine::startLINE(EmuTime::param time)
1465 {
1466  engineTime = time;
1467  ASX = (NX - 1) / 2;
1468  ADX = DX;
1469  ANX = 0;
1470 }
1471 
1472 template<typename Mode>
1473 void V9990CmdEngine::executeLINE(EmuTime::param limit)
1474 {
1475  auto delta = getTiming(*this, LINE_TIMING);
1476  unsigned width = vdp.getImageWidth();
1477  unsigned pitch = Mode::getPitch(width);
1478 
1479  int TX = (ARG & DIX) ? -1 : 1;
1480  int TY = (ARG & DIY) ? -1 : 1;
1481  const byte* lut = Mode::getLogOpLUT(LOG);
1482 
1483  if ((ARG & MAJ) == 0) {
1484  // X-Axis is major direction.
1485  while (engineTime < limit) {
1486  engineTime += delta;
1487  Mode::psetColor(vram, ADX, DY, pitch, fgCol, WM, lut, LOG);
1488 
1489  ADX += TX;
1490  if (ASX < NY) {
1491  ASX += NX;
1492  DY += TY;
1493  }
1494  ASX -= NY;
1495  //ASX &= 1023; // mask to 10 bits range
1496  if (ANX++ == NX || (ADX & width)) {
1497  cmdReady(engineTime);
1498  break;
1499  }
1500  }
1501  } else {
1502  // Y-Axis is major direction.
1503  while (engineTime < limit) {
1504  engineTime += delta;
1505  Mode::psetColor(vram, ADX, DY, pitch, fgCol, WM, lut, LOG);
1506  DY += TY;
1507  if (ASX < NY) {
1508  ASX += NX;
1509  ADX += TX;
1510  }
1511  ASX -= NY;
1512  //ASX &= 1023; // mask to 10 bits range
1513  if (ANX++ == NX || (ADX & width)) {
1514  cmdReady(engineTime);
1515  break;
1516  }
1517  }
1518  }
1519 }
1520 
1521 // SRCH
1522 void V9990CmdEngine::startSRCH(EmuTime::param time)
1523 {
1524  engineTime = time;
1525  ASX = SX;
1526 }
1527 
1528 template<typename Mode>
1529 void V9990CmdEngine::executeSRCH(EmuTime::param limit)
1530 {
1531  auto delta = getTiming(*this, SRCH_TIMING);
1532  unsigned width = vdp.getImageWidth();
1533  unsigned pitch = Mode::getPitch(width);
1534  typename Mode::Type mask = (1 << Mode::BITS_PER_PIXEL) -1;
1535 
1536  int TX = (ARG & DIX) ? -1 : 1;
1537  bool AEQ = (ARG & NEQ) != 0;
1538 
1539  while (engineTime < limit) {
1540  engineTime += delta;
1541  typename Mode::Type value;
1542  typename Mode::Type col;
1543  typename Mode::Type mask2;
1544  if (Mode::BITS_PER_PIXEL == 16) {
1545  value = Mode::point(vram, ASX, SY, pitch);
1546  col = static_cast<typename Mode::Type>(fgCol);
1547  mask2 = static_cast<typename Mode::Type>(~0);
1548  } else {
1549  // TODO check
1550  unsigned addr = Mode::addressOf(ASX, SY, pitch);
1551  value = vram.readVRAMDirect(addr);
1552  col = (addr & 0x40000) ? (fgCol >> 8) : (fgCol & 0xFF);
1553  mask2 = Mode::shift(mask, 3, ASX);
1554  }
1555  if (((value & mask2) == (col & mask2)) ^ AEQ) {
1556  status |= BD; // border detected
1557  cmdReady(engineTime);
1558  borderX = ASX;
1559  break;
1560  }
1561  if ((ASX += TX) & width) {
1562  status &= ~BD; // border not detected
1563  cmdReady(engineTime);
1564  borderX = ASX;
1565  break;
1566  }
1567  }
1568 }
1569 
1570 // POINT
1571 template<typename Mode>
1572 void V9990CmdEngine::startPOINT(EmuTime::param /*time*/)
1573 {
1574  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
1575  auto d = Mode::point(vram, SX, SY, pitch);
1576 
1577  if (Mode::BITS_PER_PIXEL != 16) {
1578  data = byte(d);
1579  endAfterRead = true;
1580  } else {
1581  unsigned tmp = d; // workaround for VC++ warning C4333
1582  // (in case Mode::Type == byte and
1583  // Mode::BITS_PER_PIXEL == 8)
1584  data = tmp & 0xff;
1585  partial = tmp >> 8;
1586  endAfterRead = false;
1587  }
1588  status |= TR;
1589 }
1590 
1591 template<typename Mode>
1592 void V9990CmdEngine::executePOINT(EmuTime::param /*limit*/)
1593 {
1594  if (status & TR) return;
1595 
1596  assert(Mode::BITS_PER_PIXEL == 16);
1597  status |= TR;
1598  data = partial;
1599  endAfterRead = true;
1600 }
1601 
1602 // PSET
1603 template<typename Mode>
1604 void V9990CmdEngine::startPSET(EmuTime::param time)
1605 {
1606  unsigned pitch = Mode::getPitch(vdp.getImageWidth());
1607  const byte* lut = Mode::getLogOpLUT(LOG);
1608  Mode::psetColor(vram, DX, DY, pitch, fgCol, WM, lut, LOG);
1609 
1610  // TODO advance DX DY
1611 
1612  cmdReady(time);
1613 }
1614 
1615 void V9990CmdEngine::executePSET(EmuTime::param /*limit*/)
1616 {
1617  UNREACHABLE;
1618 }
1619 
1620 // ADVN
1621 void V9990CmdEngine::startADVN(EmuTime::param time)
1622 {
1623  std::cout << "V9990: ADVN not yet implemented\n";
1624  cmdReady(time); // TODO dummy implementation
1625 }
1626 
1627 void V9990CmdEngine::executeADVN(EmuTime::param /*limit*/)
1628 {
1629  UNREACHABLE;
1630 }
1631 
1632 // ====================================================================
1633 // CmdEngine methods
1634 
1635 void V9990CmdEngine::sync2(EmuTime::param time)
1636 {
1637  switch (cmdMode | (CMD >> 4)) {
1638  case 0x00: case 0x10: case 0x20: case 0x30: case 0x40: case 0x50:
1639  executeSTOP(time); break;
1640 
1641  case 0x01: executeLMMC<V9990P1 >(time); break;
1642  case 0x11: executeLMMC<V9990P2 >(time); break;
1643  case 0x21: executeLMMC<V9990Bpp2 >(time); break;
1644  case 0x31: executeLMMC<V9990Bpp4 >(time); break;
1645  case 0x41: executeLMMC<V9990Bpp8 >(time); break;
1646  case 0x51: executeLMMC<V9990Bpp16>(time); break;
1647 
1648  case 0x02: executeLMMV<V9990P1 >(time); break;
1649  case 0x12: executeLMMV<V9990P2 >(time); break;
1650  case 0x22: executeLMMV<V9990Bpp2 >(time); break;
1651  case 0x32: executeLMMV<V9990Bpp4 >(time); break;
1652  case 0x42: executeLMMV<V9990Bpp8 >(time); break;
1653  case 0x52: executeLMMV<V9990Bpp16>(time); break;
1654 
1655  case 0x03: executeLMCM<V9990P1 >(time); break;
1656  case 0x13: executeLMCM<V9990P2 >(time); break;
1657  case 0x23: executeLMCM<V9990Bpp2 >(time); break;
1658  case 0x33: executeLMCM<V9990Bpp4 >(time); break;
1659  case 0x43: executeLMCM<V9990Bpp8 >(time); break;
1660  case 0x53: executeLMCM<V9990Bpp16>(time); break;
1661 
1662  case 0x04: executeLMMM<V9990P1 >(time); break;
1663  case 0x14: executeLMMM<V9990P2 >(time); break;
1664  case 0x24: executeLMMM<V9990Bpp2 >(time); break;
1665  case 0x34: executeLMMM<V9990Bpp4 >(time); break;
1666  case 0x44: executeLMMM<V9990Bpp8 >(time); break;
1667  case 0x54: executeLMMM<V9990Bpp16>(time); break;
1668 
1669  case 0x05: executeCMMC<V9990P1 >(time); break;
1670  case 0x15: executeCMMC<V9990P2 >(time); break;
1671  case 0x25: executeCMMC<V9990Bpp2 >(time); break;
1672  case 0x35: executeCMMC<V9990Bpp4 >(time); break;
1673  case 0x45: executeCMMC<V9990Bpp8 >(time); break;
1674  case 0x55: executeCMMC<V9990Bpp16>(time); break;
1675 
1676  case 0x06: case 0x16: case 0x26: case 0x36: case 0x46: case 0x56:
1677  executeCMMK(time); break;
1678 
1679  case 0x07: executeCMMM<V9990P1 >(time); break;
1680  case 0x17: executeCMMM<V9990P2 >(time); break;
1681  case 0x27: executeCMMM<V9990Bpp2 >(time); break;
1682  case 0x37: executeCMMM<V9990Bpp4 >(time); break;
1683  case 0x47: executeCMMM<V9990Bpp8 >(time); break;
1684  case 0x57: executeCMMM<V9990Bpp16>(time); break;
1685 
1686  case 0x08: executeBMXL<V9990P1 >(time); break;
1687  case 0x18: executeBMXL<V9990P2 >(time); break;
1688  case 0x28: executeBMXL<V9990Bpp2 >(time); break;
1689  case 0x38: executeBMXL<V9990Bpp4 >(time); break;
1690  case 0x48: executeBMXL<V9990Bpp8 >(time); break;
1691  case 0x58: executeBMXL<V9990Bpp16>(time); break;
1692 
1693  case 0x09: executeBMLX<V9990P1 >(time); break;
1694  case 0x19: executeBMLX<V9990P2 >(time); break;
1695  case 0x29: executeBMLX<V9990Bpp2 >(time); break;
1696  case 0x39: executeBMLX<V9990Bpp4 >(time); break;
1697  case 0x49: executeBMLX<V9990Bpp8 >(time); break;
1698  case 0x59: executeBMLX<V9990Bpp16>(time); break;
1699 
1700  case 0x0A: executeBMLL<V9990P1 >(time); break;
1701  case 0x1A: executeBMLL<V9990P2 >(time); break;
1702  case 0x2A: executeBMLL<V9990Bpp2 >(time); break;
1703  case 0x3A: executeBMLL<V9990Bpp4 >(time); break;
1704  case 0x4A: executeBMLL<V9990Bpp8 >(time); break;
1705  case 0x5A: executeBMLL<V9990Bpp16>(time); break;
1706 
1707  case 0x0B: executeLINE<V9990P1 >(time); break;
1708  case 0x1B: executeLINE<V9990P2 >(time); break;
1709  case 0x2B: executeLINE<V9990Bpp2 >(time); break;
1710  case 0x3B: executeLINE<V9990Bpp4 >(time); break;
1711  case 0x4B: executeLINE<V9990Bpp8 >(time); break;
1712  case 0x5B: executeLINE<V9990Bpp16>(time); break;
1713 
1714  case 0x0C: executeSRCH<V9990P1 >(time); break;
1715  case 0x1C: executeSRCH<V9990P2 >(time); break;
1716  case 0x2C: executeSRCH<V9990Bpp2 >(time); break;
1717  case 0x3C: executeSRCH<V9990Bpp4 >(time); break;
1718  case 0x4C: executeSRCH<V9990Bpp8 >(time); break;
1719  case 0x5C: executeSRCH<V9990Bpp16>(time); break;
1720 
1721  case 0x0D: executePOINT<V9990P1 >(time); break;
1722  case 0x1D: executePOINT<V9990P2 >(time); break;
1723  case 0x2D: executePOINT<V9990Bpp2 >(time); break;
1724  case 0x3D: executePOINT<V9990Bpp4 >(time); break;
1725  case 0x4D: executePOINT<V9990Bpp8 >(time); break;
1726  case 0x5D: executePOINT<V9990Bpp16>(time); break;
1727 
1728  case 0x0E: case 0x1E: case 0x2E: case 0x3E: case 0x4E: case 0x5E:
1729  executePSET(time); break;
1730 
1731  case 0x0F: case 0x1F: case 0x2F: case 0x3F: case 0x4F: case 0x5F:
1732  executeADVN(time); break;
1733 
1734  default: UNREACHABLE;
1735  }
1736 }
1737 
1738 void V9990CmdEngine::setCmdData(byte value, EmuTime::param time)
1739 {
1740  sync(time);
1741  data = value;
1742  status &= ~TR;
1743 }
1744 
1745 byte V9990CmdEngine::getCmdData(EmuTime::param time)
1746 {
1747  sync(time);
1748 
1749  byte value = 0xFF;
1750  if (status & TR) {
1751  value = data;
1752  status &= ~TR;
1753  if (endAfterRead) {
1754  endAfterRead = false;
1755  cmdReady(time);
1756  }
1757  }
1758  return value;
1759 }
1760 
1761 byte V9990CmdEngine::peekCmdData(EmuTime::param time)
1762 {
1763  sync(time);
1764  return (status & TR) ? data : 0xFF;
1765 }
1766 
1767 void V9990CmdEngine::cmdReady(EmuTime::param /*time*/)
1768 {
1769  CMD = 0; // for deserialize
1770  status &= ~(CE | TR);
1771  vdp.cmdReady();
1772 }
1773 
1775 {
1776  EmuDuration delta;
1777  switch (CMD >> 4) {
1778  case 0x00: // STOP
1779  delta = EmuDuration::zero();
1780  break;
1781 
1782  case 0x01: // LMMC
1783  case 0x05: // CMMC
1784  // command terminates when CPU writes data, no need for extra sync
1785  delta = EmuDuration::zero();
1786  break;
1787 
1788  case 0x03: // LMCM
1789  case 0x0D: // POINT
1790  // command terminates when CPU reads data, no need for extra sync
1791  delta = EmuDuration::zero();
1792  break;
1793 
1794  case 0x02: // LMMV
1795  // Block commands.
1796  delta = getTiming(*this, LMMV_TIMING) * (ANX + (ANY - 1) * getWrappedNX());
1797  break;
1798  case 0x04: // LMMM
1799  delta = getTiming(*this, LMMM_TIMING) * (ANX + (ANY - 1) * getWrappedNX());
1800  break;
1801  case 0x07: // CMMM
1802  delta = getTiming(*this, CMMM_TIMING) * (ANX + (ANY - 1) * getWrappedNX());
1803  break;
1804  case 0x08: // BMXL
1805  delta = getTiming(*this, BMXL_TIMING) * (ANX + (ANY - 1) * getWrappedNX()); // TODO correct?
1806  break;
1807  case 0x09: // BMLX
1808  delta = getTiming(*this, BMLX_TIMING) * (ANX + (ANY - 1) * getWrappedNX()); // TODO correct?
1809  break;
1810 
1811  case 0x06: // CMMK
1812  // Not yet implemented.
1813  delta = EmuDuration::zero();
1814  break;
1815 
1816  case 0x0A: // BMLL
1817  delta = getTiming(*this, BMLL_TIMING) * nbBytes;
1818  break;
1819 
1820  case 0x0B: // LINE
1821  delta = getTiming(*this, LINE_TIMING) * (NX - ANX); // TODO ignores clipping
1822  break;
1823 
1824  case 0x0C: // SRCH
1825  // Can end at any next pixel.
1826  delta = getTiming(*this, SRCH_TIMING);
1827  break;
1828 
1829  case 0x0E: // PSET
1830  case 0x0F: // ADVN
1831  // Current implementation of these commands execute instantly, no need for extra sync.
1832  delta = EmuDuration::zero();
1833  break;
1834 
1835  default: UNREACHABLE;
1836  }
1837  return engineTime + delta;
1838 }
1839 
1840 // version 1: initial version
1841 // version 2: we forgot to serialize the time (or clock) member
1842 template<typename Archive>
1843 void V9990CmdEngine::serialize(Archive& ar, unsigned version)
1844 {
1845  // note: V9990Cmd objects are stateless
1846  if (ar.versionAtLeast(version, 2)) {
1847  ar.serialize("time", engineTime);
1848  } else {
1849  // In version 1 we forgot to serialize the time member (it was
1850  // also still a Clock back then). The best we can do is
1851  // initialize it with the current time, that's already done in
1852  // the constructor.
1853  }
1854  ar.serialize("srcAddress", srcAddress,
1855  "dstAddress", dstAddress,
1856  "nbBytes", nbBytes,
1857  "borderX", borderX,
1858  "ASX", ASX,
1859  "ADX", ADX,
1860  "ANX", ANX,
1861  "ANY", ANY,
1862  "SX", SX,
1863  "SY", SY,
1864  "DX", DX,
1865  "DY", DY,
1866  "NX", NX,
1867  "NY", NY,
1868  "WM", WM,
1869  "fgCol", fgCol,
1870  "bgCol", bgCol,
1871  "ARG", ARG,
1872  "LOG", LOG,
1873  "CMD", CMD,
1874  "status", status,
1875  "data", data,
1876  "bitsLeft", bitsLeft,
1877  "partial", partial,
1878  "endAfterRead", endAfterRead);
1879 
1880  if (ar.isLoader()) {
1881  setCommandMode();
1882  }
1883 }
1885 
1886 } // namespace openmsx
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 uint64_t length() const
Definition: EmuDuration.hh:50
static constexpr EmuDuration zero()
Definition: EmuDuration.hh:115
MSXMotherBoard & getMotherBoard() const
Get the mother board this device belongs to.
Definition: MSXDevice.cc:76
CommandController & getCommandController() const
Definition: MSXDevice.cc:156
std::shared_ptr< T > getSharedStuff(std::string_view name, Args &&...args)
Some MSX device parts are shared between several MSX devices (e.g.
const T * data() const
Returns pointer to the start of the memory buffer.
Definition: MemBuffer.hh:81
void resize(size_t size)
Grow or shrink the memory block.
Definition: MemBuffer.hh:111
Class containing all settings for renderers.
EnumSetting< bool > & getCmdTimingSetting()
CmdTiming [real, broken].
void detach(Observer< T > &observer)
Definition: Subject.hh:56
void attach(Observer< T > &observer)
Definition: Subject.hh:50
void sync(EmuTime::param time)
Synchronizes the command engine with the V9990.
void serialize(Archive &ar, unsigned version)
void reset(EmuTime::param time)
Re-initialise the command engine's state.
static constexpr byte CE
void setCmdData(byte value, EmuTime::param time)
set the data byte
void setCmdReg(byte reg, byte val, EmuTime::param time)
Set a value to one of the command registers.
static constexpr byte BD
byte getCmdData(EmuTime::param time)
read the command data byte
byte peekCmdData(EmuTime::param time)
read the command data byte (without side-effects)
V9990CmdEngine(V9990 &vdp, EmuTime::param time, RenderSettings &settings)
Constructor.
EmuTime estimateCmdEnd() const
Calculate an (under-)estimation for when the command will finish.
static constexpr byte TR
void sync2(EmuTime::param time)
static unsigned transformP2(unsigned address)
Definition: V9990VRAM.hh:43
static unsigned transformBx(unsigned address)
Definition: V9990VRAM.hh:37
static unsigned transformP1(unsigned address)
Definition: V9990VRAM.hh:40
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
Definition: V9990.hh:31
void cmdReady()
Command execution ready.
Definition: V9990.hh:285
V9990DisplayMode getDisplayMode() const
Return the current display mode.
Definition: V9990.hh:191
unsigned getImageWidth() const
Return the image width.
Definition: V9990.hh:256
V9990ColorMode getColorMode() const
Return the current color mode.
Definition: V9990.cc:801
#define unlikely(x)
Definition: likely.hh:15
This file implemented 3 utility functions:
Definition: Autofire.cc:5
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
constexpr byte DIX
constexpr Table table
Definition: CPUCore.cc:264
constexpr EDStorage CMMM_TIMING[4][3][4]
constexpr EDStorage LINE_TIMING[4][3][4]
constexpr EDStorage BMLX_TIMING[4][3][4]
constexpr unsigned maxLength
constexpr EDStorage LMMV_TIMING[4][3][4]
constexpr byte DIY
constexpr EDStorage SRCH_TIMING[4][3][4]
std::conditional_t<(MAX > detail::max32), EmuDuration, std::conditional_t<(MAX > detail::max16), EmuDuration32, std::conditional_t<(MAX > detail::max8), EmuDuration16, EmuDuration8 > >> EmuDurationStorageFor
Definition: EmuDuration.hh:169
EmuDurationStorageFor< d(maxLength).length()> EDStorage
constexpr EDStorage BMXL_TIMING[4][3][4]
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:124
constexpr byte NEQ
constexpr EDStorage BMLL_TIMING[4][3][4]
constexpr byte MAJ
constexpr nibble mask[4][13]
Definition: RP5C01.cc:34
constexpr EDStorage LMMM_TIMING[4][3][4]
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:983
#define UNREACHABLE
Definition: unreachable.hh:38
constexpr auto xrange(T e)
Definition: xrange.hh:155