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