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 
1765 // version 1: initial version
1766 // version 2: we forgot to serialize the time (or clock) member
1767 template<typename Archive>
1768 void V9990CmdEngine::serialize(Archive& ar, unsigned version)
1769 {
1770  // note: V9990Cmd objects are stateless
1771  if (ar.versionAtLeast(version, 2)) {
1772  ar.serialize("time", engineTime);
1773  } else {
1774  // In version 1 we forgot to serialize the time member (it was
1775  // also still a Clock back then). The best we can do is
1776  // initialize it with the current time, that's already done in
1777  // the constructor.
1778  }
1779  ar.serialize("srcAddress", srcAddress);
1780  ar.serialize("dstAddress", dstAddress);
1781  ar.serialize("nbBytes", nbBytes);
1782  ar.serialize("borderX", borderX);
1783  ar.serialize("ASX", ASX);
1784  ar.serialize("ADX", ADX);
1785  ar.serialize("ANX", ANX);
1786  ar.serialize("ANY", ANY);
1787  ar.serialize("SX", SX);
1788  ar.serialize("SY", SY);
1789  ar.serialize("DX", DX);
1790  ar.serialize("DY", DY);
1791  ar.serialize("NX", NX);
1792  ar.serialize("NY", NY);
1793  ar.serialize("WM", WM);
1794  ar.serialize("fgCol", fgCol);
1795  ar.serialize("bgCol", bgCol);
1796  ar.serialize("ARG", ARG);
1797  ar.serialize("LOG", LOG);
1798  ar.serialize("CMD", CMD);
1799  ar.serialize("status", status);
1800  ar.serialize("data", data);
1801  ar.serialize("bitsLeft", bitsLeft);
1802  ar.serialize("partial", partial);
1803  ar.serialize("endAfterRead", endAfterRead);
1804 
1805  if (ar.isLoader()) {
1806  setCommandMode();
1807  }
1808 }
1810 
1811 } // 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)
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:790
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
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:840
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:51
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