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