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