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