26static constexpr unsigned maxLength = 171;
27static constexpr EmuDuration d_(
unsigned x)
29 assert(x <= maxLength);
34using A = std::array<const EDStorage, 4>;
35using A2 = std::array<const A, 3>;
42static constexpr std::array LMMV_TIMING = {
43 A2{
A{ d( 8), d(11), d(15), d(30)},
A{d( 7), d(10), d(13), d(26)},
A{d( 7), d(10), d(13), d(25)}},
44 A2{
A{ d( 5), d( 7), d( 9), d(18)},
A{d( 5), d( 6), d( 8), d(17)},
A{d( 5), d( 6), d( 8), d(17)}},
45 A2{
A{ d(56), d(56), d(56), d(56)},
A{d(25), d(25), d(25), d(25)},
A{d( 9), d( 9), d( 9), d( 9)}},
46 A2{
A{ d(28), d(28), d(28), d(28)},
A{d(15), d(15), d(15), d(15)},
A{d( 6), d( 6), d( 6), d( 6)}}
48static constexpr std::array LMMM_TIMING = {
49 A2{
A{d (10),d (16),d( 32),d( 66)},
A{d( 8), d(14), d(28), d(57)},
A{d( 8), d(13), d(27), d(54)}},
50 A2{
A{d ( 6),d( 10),d( 20),d( 39)},
A{d( 5), d( 9), d(18), d(35)},
A{d( 5), d( 9), d(17), d(35)}},
51 A2{
A{d(115),d(115),d(115),d(115)},
A{d(52), d(52), d(52), d(52)},
A{d(18), d(18), d(18), d(18)}},
52 A2{
A{d( 57),d( 57),d( 57),d( 57)},
A{d(25), d(25), d(25), d(25)},
A{d( 9), d( 9), d( 9), d( 9)}}
54static constexpr std::array BMXL_TIMING = {
55 A2{
A{d( 38),d( 33),d( 32),d( 33)},
A{d(33), d(28), d(28), d(28)},
A{d(33), d(27), d(27), d(27)}},
56 A2{
A{d( 24),d( 20),d( 20),d (19)},
A{d(22), d(18), d(18), d(18)},
A{d(21), d(17), d(17), d(17)}},
57 A2{
A{d(171),d(171),d(171),d(171)},
A{d(82), d(82), d(82), d(82)},
A{d(29), d(29), d(29), d(29)}},
58 A2{
A{d(114),d(114),d(114),d(114)},
A{d(50), d(50), d(50), d(50)},
A{d(18), d(18), d(18), d(18)}}
60static constexpr std::array BMLX_TIMING = {
61 A2{
A{ d(10), d(16), d(32), d(66)},
A{d( 8), d(14), d(28), d(57)},
A{d( 8), d(13), d(27), d(54)}},
62 A2{
A{ d( 6), d(10), d(20), d(39)},
A{d( 5), d( 9), d(18), d(35)},
A{d( 5), d( 9), d(17), d(35)}},
63 A2{
A{ d(84), d(84), d(84), d(84)},
A{d(44), d(44), d(44), d(44)},
A{d(17), d(17), d(17), d(17)}},
64 A2{
A{ d(57), d(57), d(57), d(57)},
A{d(25), d(25), d(25), d(25)},
A{d( 9), d( 9), d( 9), d( 9)}}
66static constexpr std::array BMLL_TIMING = {
67 A2{
A{d( 33),d( 33),d( 33),d( 33)},
A{d(28), d(28), d(28), d(28)},
A{d(27), d(27), d(27), d(27)}},
68 A2{
A{d( 20),d( 20),d( 20),d( 20)},
A{d(18), d(18), d(18), d(18)},
A{d(18), d(18), d(18), d(18)}},
69 A2{
A{d(118),d(118),d(118),d(118)},
A{d(52), d(52), d(52), d(52)},
A{d(18), d(18), d(18), d(18)}},
70 A2{
A{d(118),d(118),d(118),d(118)},
A{d(52), d(52), d(52), d(52)},
A{d(18), d(18), d(18), d(18)}}
72static constexpr std::array CMMM_TIMING = {
73 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}},
74 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}},
75 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}},
76 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}}
78static constexpr std::array LINE_TIMING = {
79 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}},
80 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}},
81 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}},
82 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}}
84static constexpr std::array SRCH_TIMING = {
85 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}},
86 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}},
87 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}},
88 A2{
A{ d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)},
A{d(24), d(24), d(24), d(24)}}
91[[nodiscard]]
static EmuDuration getTiming(
const V9990CmdEngine& cmdEngine,
TimingTable table)
93 if (cmdEngine.getBrokenTiming()) [[unlikely]]
return {};
95 const auto& vdp = cmdEngine.getVDP();
96 auto mode = vdp.getDisplayMode();
97 unsigned idx1 = (mode ==
P1) ? 2 :
99 (vdp.isOverScan()) ? 0 : 1;
100 unsigned idx2 = vdp.isDisplayEnabled() ? (vdp.spritesEnabled() ? 0 : 1)
102 unsigned idx3 = vdp.getColorDepth();
103 return EmuDuration(table[idx1][idx2][idx3]);
116static std::array<std::array<MemBuffer<byte>, 16>, 4> logOpLUT;
119static constexpr auto bitLUT = [] {
120 std::array<std::array<std::array<std::array<byte, 2>, 2>, 16>, 8> result = {};
121 for (
auto op :
xrange(16)) {
123 for (
auto src :
xrange(2)) {
124 for (
auto dst :
xrange(2)) {
125 unsigned b = tmp & 1;
126 for (
auto bit :
xrange(8)) {
127 result[bit][op][src][dst] = narrow<byte>(b << bit);
138[[nodiscard]]
static constexpr byte func01(
unsigned op,
unsigned src,
unsigned dst)
140 if ((src & 0x03) == 0)
return dst & 0x03;
142 res |= bitLUT[0][op][(src & 0x01) >> 0][(dst & 0x01) >> 0];
143 res |= bitLUT[1][op][(src & 0x02) >> 1][(dst & 0x02) >> 1];
146[[nodiscard]]
static constexpr byte func23(
unsigned op,
unsigned src,
unsigned dst)
148 if ((src & 0x0C) == 0)
return dst & 0x0C;
150 res |= bitLUT[2][op][(src & 0x04) >> 2][(dst & 0x04) >> 2];
151 res |= bitLUT[3][op][(src & 0x08) >> 3][(dst & 0x08) >> 3];
154[[nodiscard]]
static constexpr byte func45(
unsigned op,
unsigned src,
unsigned dst)
156 if ((src & 0x30) == 0)
return dst & 0x30;
158 res |= bitLUT[4][op][(src & 0x10) >> 4][(dst & 0x10) >> 4];
159 res |= bitLUT[5][op][(src & 0x20) >> 5][(dst & 0x20) >> 5];
162[[nodiscard]]
static constexpr byte func67(
unsigned op,
unsigned src,
unsigned dst)
164 if ((src & 0xC0) == 0)
return dst & 0xC0;
166 res |= bitLUT[6][op][(src & 0x40) >> 6][(dst & 0x40) >> 6];
167 res |= bitLUT[7][op][(src & 0x80) >> 7][(dst & 0x80) >> 7];
171[[nodiscard]]
static constexpr byte func03(
unsigned op,
unsigned src,
unsigned dst)
173 if ((src & 0x0F) == 0)
return dst & 0x0F;
175 res |= bitLUT[0][op][(src & 0x01) >> 0][(dst & 0x01) >> 0];
176 res |= bitLUT[1][op][(src & 0x02) >> 1][(dst & 0x02) >> 1];
177 res |= bitLUT[2][op][(src & 0x04) >> 2][(dst & 0x04) >> 2];
178 res |= bitLUT[3][op][(src & 0x08) >> 3][(dst & 0x08) >> 3];
181[[nodiscard]]
static constexpr byte func47(
unsigned op,
unsigned src,
unsigned dst)
183 if ((src & 0xF0) == 0)
return dst & 0xF0;
185 res |= bitLUT[4][op][(src & 0x10) >> 4][(dst & 0x10) >> 4];
186 res |= bitLUT[5][op][(src & 0x20) >> 5][(dst & 0x20) >> 5];
187 res |= bitLUT[6][op][(src & 0x40) >> 6][(dst & 0x40) >> 6];
188 res |= bitLUT[7][op][(src & 0x80) >> 7][(dst & 0x80) >> 7];
192[[nodiscard]]
static constexpr byte func07(
unsigned op,
unsigned src,
unsigned dst)
196 res |= bitLUT[0][op][(src & 0x01) >> 0][(dst & 0x01) >> 0];
197 res |= bitLUT[1][op][(src & 0x02) >> 1][(dst & 0x02) >> 1];
198 res |= bitLUT[2][op][(src & 0x04) >> 2][(dst & 0x04) >> 2];
199 res |= bitLUT[3][op][(src & 0x08) >> 3][(dst & 0x08) >> 3];
200 res |= bitLUT[4][op][(src & 0x10) >> 4][(dst & 0x10) >> 4];
201 res |= bitLUT[5][op][(src & 0x20) >> 5][(dst & 0x20) >> 5];
202 res |= bitLUT[6][op][(src & 0x40) >> 6][(dst & 0x40) >> 6];
203 res |= bitLUT[7][op][(src & 0x80) >> 7][(dst & 0x80) >> 7];
207static constexpr void fillTableNoT(
unsigned op, std::span<byte, 256 * 256> table)
209 for (
auto dst :
xrange(256)) {
210 for (
auto src :
xrange(256)) {
211 table[dst * 256 + src] = func07(op, src, dst);
216static constexpr void fillTable2(
unsigned op, std::span<byte, 256 * 256> table)
218 for (
auto dst :
xrange(256)) {
219 for (
auto src :
xrange(256)) {
221 res |= func01(op, src, dst);
222 res |= func23(op, src, dst);
223 res |= func45(op, src, dst);
224 res |= func67(op, src, dst);
225 table[dst * 256 + src] = res;
230static constexpr void fillTable4(
unsigned op, std::span<byte, 256 * 256> table)
232 for (
auto dst :
xrange(256)) {
233 for (
auto src :
xrange(256)) {
235 res |= func03(op, src, dst);
236 res |= func47(op, src, dst);
237 table[dst * 256 + src] = res;
242static constexpr void fillTable8(
unsigned op, std::span<byte, 256 * 256> table)
244 for (
auto dst :
xrange(256)) {
246 table[dst * 256 + 0 ] = narrow_cast<byte>(dst);
248 for (
auto src :
xrange(1, 256)) {
249 table[dst * 256 + src] = func07(op, src, dst);
254[[nodiscard]]
static std::span<const byte, 256 * 256> getLogOpImpl(
unsigned mode,
unsigned op)
257 auto& lut = logOpLUT[mode][op];
259 lut.resize(256 * 256);
260 std::span<byte, 256 * 256> s{lut.data(), 256 * 256};
278 return std::span<byte, 256 * 256>{lut.data(), 256 * 256};
282static constexpr byte DIY = 0x08;
283static constexpr byte DIX = 0x04;
284static constexpr byte NEQ = 0x02;
285static constexpr byte MAJ = 0x01;
288inline unsigned V9990CmdEngine::V9990P1::getPitch(
unsigned width)
293inline unsigned V9990CmdEngine::V9990P1::addressOf(
294 unsigned x,
unsigned y,
unsigned pitch)
299 return (addr & 0x3FFFF) | ((x & 0x200) << 9);
302inline byte V9990CmdEngine::V9990P1::point(
303 const V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch)
305 return vram.readVRAMDirect(addressOf(x, y, pitch));
308inline byte V9990CmdEngine::V9990P1::shift(
309 byte value,
unsigned fromX,
unsigned toX)
311 int shift = 4 * (narrow<int>(toX & 1) - narrow<int>(fromX & 1));
312 return (shift > 0) ?
byte(value >> shift) :
byte(value << -shift);
315inline byte V9990CmdEngine::V9990P1::shiftMask(
unsigned x)
317 return (x & 1) ? 0x0F : 0xF0;
320inline std::span<const byte, 256 * 256> V9990CmdEngine::V9990P1::getLogOpLUT(
byte op)
325inline byte V9990CmdEngine::V9990P1::logOp(
326 std::span<const byte, 256 * 256> lut,
byte src,
byte dst)
328 return lut[256 * dst + src];
331inline void V9990CmdEngine::V9990P1::pset(
332 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
333 byte srcColor,
word mask, std::span<const byte, 256 * 256> lut,
byte )
335 unsigned addr = addressOf(x, y, pitch);
336 byte dstColor = vram.readVRAMDirect(addr);
337 byte newColor = logOp(lut, srcColor, dstColor);
338 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
339 byte mask2 = mask1 & shiftMask(x);
340 byte result = (dstColor & ~mask2) | (newColor & mask2);
341 vram.writeVRAMDirect(addr, result);
343inline void V9990CmdEngine::V9990P1::psetColor(
344 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
345 word color,
word mask, std::span<const byte, 256 * 256> lut,
byte )
347 unsigned addr = addressOf(x, y, pitch);
348 byte srcColor = narrow_cast<byte>((addr & 0x40000) ? (color >> 8) : (color & 0xFF));
349 byte dstColor = vram.readVRAMDirect(addr);
350 byte newColor = logOp(lut, srcColor, dstColor);
351 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
352 byte mask2 = mask1 & (0xF0 >> (4 * (x & 1)));
353 byte result = (dstColor & ~mask2) | (newColor & mask2);
354 vram.writeVRAMDirect(addr, result);
358inline unsigned V9990CmdEngine::V9990P2::getPitch(
unsigned width)
363inline unsigned V9990CmdEngine::V9990P2::addressOf(
364 unsigned x,
unsigned y,
unsigned pitch)
370inline byte V9990CmdEngine::V9990P2::point(
371 const V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch)
373 return vram.readVRAMDirect(addressOf(x, y, pitch));
376inline byte V9990CmdEngine::V9990P2::shift(
377 byte value,
unsigned fromX,
unsigned toX)
379 int shift = 4 * (narrow<int>(toX & 1) - narrow<int>(fromX & 1));
380 return (shift > 0) ?
byte(value >> shift) :
byte(value << -shift);
383inline byte V9990CmdEngine::V9990P2::shiftMask(
unsigned x)
385 return (x & 1) ? 0x0F : 0xF0;
388inline std::span<const byte, 256 * 256> V9990CmdEngine::V9990P2::getLogOpLUT(
byte op)
393inline byte V9990CmdEngine::V9990P2::logOp(
394 std::span<const byte, 256 * 256> lut,
byte src,
byte dst)
396 return lut[256 * dst + src];
399inline void V9990CmdEngine::V9990P2::pset(
400 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
401 byte srcColor,
word mask, std::span<const byte, 256 * 256> lut,
byte )
403 unsigned addr = addressOf(x, y, pitch);
404 byte dstColor = vram.readVRAMDirect(addr);
405 byte newColor = logOp(lut, srcColor, dstColor);
406 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
407 byte mask2 = mask1 & shiftMask(x);
408 byte result = (dstColor & ~mask2) | (newColor & mask2);
409 vram.writeVRAMDirect(addr, result);
412inline void V9990CmdEngine::V9990P2::psetColor(
413 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
414 word color,
word mask, std::span<const byte, 256 * 256> lut,
byte )
416 unsigned addr = addressOf(x, y, pitch);
417 byte srcColor = narrow_cast<byte>((addr & 0x40000) ? (color >> 8) : (color & 0xFF));
418 byte dstColor = vram.readVRAMDirect(addr);
419 byte newColor = logOp(lut, srcColor, dstColor);
420 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
421 byte mask2 = mask1 & (0xF0 >> (4 * (x & 1)));
422 byte result = (dstColor & ~mask2) | (newColor & mask2);
423 vram.writeVRAMDirect(addr, result);
427inline unsigned V9990CmdEngine::V9990Bpp2::getPitch(
unsigned width)
432inline unsigned V9990CmdEngine::V9990Bpp2::addressOf(
433 unsigned x,
unsigned y,
unsigned pitch)
438inline byte V9990CmdEngine::V9990Bpp2::point(
439 const V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch)
441 return vram.readVRAMDirect(addressOf(x, y, pitch));
444inline byte V9990CmdEngine::V9990Bpp2::shift(
445 byte value,
unsigned fromX,
unsigned toX)
447 int shift = 2 * (narrow<int>(toX & 3) - narrow<int>(fromX & 3));
448 return (shift > 0) ?
byte(value >> shift) :
byte(value << -shift);
451inline byte V9990CmdEngine::V9990Bpp2::shiftMask(
unsigned x)
453 return 0xC0 >> (2 * (x & 3));
456inline std::span<const byte, 256 * 256> V9990CmdEngine::V9990Bpp2::getLogOpLUT(
byte op)
461inline byte V9990CmdEngine::V9990Bpp2::logOp(
462 std::span<const byte, 256 * 256> lut,
byte src,
byte dst)
464 return lut[256 * dst + src];
467inline void V9990CmdEngine::V9990Bpp2::pset(
468 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
469 byte srcColor,
word mask, std::span<const byte, 256 * 256> lut,
byte )
471 unsigned addr = addressOf(x, y, pitch);
472 byte dstColor = vram.readVRAMDirect(addr);
473 byte newColor = logOp(lut, srcColor, dstColor);
474 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
475 byte mask2 = mask1 & shiftMask(x);
476 byte result = (dstColor & ~mask2) | (newColor & mask2);
477 vram.writeVRAMDirect(addr, result);
480inline void V9990CmdEngine::V9990Bpp2::psetColor(
481 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
482 word color,
word mask, std::span<const byte, 256 * 256> lut,
byte )
484 unsigned addr = addressOf(x, y, pitch);
485 byte srcColor = narrow_cast<byte>((addr & 0x40000) ? (color >> 8) : (color & 0xFF));
486 byte dstColor = vram.readVRAMDirect(addr);
487 byte newColor = logOp(lut, srcColor, dstColor);
488 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
489 byte mask2 = mask1 & (0xC0 >> (2 * (x & 3)));
490 byte result = (dstColor & ~mask2) | (newColor & mask2);
491 vram.writeVRAMDirect(addr, result);
495inline unsigned V9990CmdEngine::V9990Bpp4::getPitch(
unsigned width)
500inline unsigned V9990CmdEngine::V9990Bpp4::addressOf(
501 unsigned x,
unsigned y,
unsigned pitch)
506inline byte V9990CmdEngine::V9990Bpp4::point(
507 const V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch)
509 return vram.readVRAMDirect(addressOf(x, y, pitch));
512inline byte V9990CmdEngine::V9990Bpp4::shift(
513 byte value,
unsigned fromX,
unsigned toX)
515 int shift = 4 * (narrow<int>(toX & 1) - narrow<int>(fromX & 1));
516 return (shift > 0) ?
byte(value >> shift) :
byte(value << -shift);
519inline byte V9990CmdEngine::V9990Bpp4::shiftMask(
unsigned x)
521 return (x & 1) ? 0x0F : 0xF0;
524inline std::span<const byte, 256 * 256> V9990CmdEngine::V9990Bpp4::getLogOpLUT(
byte op)
529inline byte V9990CmdEngine::V9990Bpp4::logOp(
530 std::span<const byte, 256 * 256> lut,
byte src,
byte dst)
532 return lut[256 * dst + src];
535inline void V9990CmdEngine::V9990Bpp4::pset(
536 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
537 byte srcColor,
word mask, std::span<const byte, 256 * 256> lut,
byte )
539 unsigned addr = addressOf(x, y, pitch);
540 byte dstColor = vram.readVRAMDirect(addr);
541 byte newColor = logOp(lut, srcColor, dstColor);
542 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
543 byte mask2 = mask1 & shiftMask(x);
544 byte result = (dstColor & ~mask2) | (newColor & mask2);
545 vram.writeVRAMDirect(addr, result);
548inline void V9990CmdEngine::V9990Bpp4::psetColor(
549 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
550 word color,
word mask, std::span<const byte, 256 * 256> lut,
byte )
552 unsigned addr = addressOf(x, y, pitch);
553 byte srcColor = narrow_cast<byte>((addr & 0x40000) ? (color >> 8) : (color & 0xFF));
554 byte dstColor = vram.readVRAMDirect(addr);
555 byte newColor = logOp(lut, srcColor, dstColor);
556 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
557 byte mask2 = mask1 & (0xF0 >> (4 * (x & 1)));
558 byte result = (dstColor & ~mask2) | (newColor & mask2);
559 vram.writeVRAMDirect(addr, result);
563inline unsigned V9990CmdEngine::V9990Bpp8::getPitch(
unsigned width)
568inline unsigned V9990CmdEngine::V9990Bpp8::addressOf(
569 unsigned x,
unsigned y,
unsigned pitch)
574inline byte V9990CmdEngine::V9990Bpp8::point(
575 const V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch)
577 return vram.readVRAMDirect(addressOf(x, y, pitch));
580inline byte V9990CmdEngine::V9990Bpp8::shift(
581 byte value,
unsigned ,
unsigned )
586inline byte V9990CmdEngine::V9990Bpp8::shiftMask(
unsigned )
591inline std::span<const byte, 256 * 256> V9990CmdEngine::V9990Bpp8::getLogOpLUT(
byte op)
596inline byte V9990CmdEngine::V9990Bpp8::logOp(
597 std::span<const byte, 256 * 256> lut,
byte src,
byte dst)
599 return lut[256 * dst + src];
602inline void V9990CmdEngine::V9990Bpp8::pset(
603 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
604 byte srcColor,
word mask, std::span<const byte, 256 * 256> lut,
byte )
606 unsigned addr = addressOf(x, y, pitch);
607 byte dstColor = vram.readVRAMDirect(addr);
608 byte newColor = logOp(lut, srcColor, dstColor);
609 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
610 byte result = (dstColor & ~mask1) | (newColor & mask1);
611 vram.writeVRAMDirect(addr, result);
614inline void V9990CmdEngine::V9990Bpp8::psetColor(
615 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
616 word color,
word mask, std::span<const byte, 256 * 256> lut,
byte )
618 unsigned addr = addressOf(x, y, pitch);
619 byte srcColor = narrow_cast<byte>((addr & 0x40000) ? (color >> 8) : (color & 0xFF));
620 byte dstColor = vram.readVRAMDirect(addr);
621 byte newColor = logOp(lut, srcColor, dstColor);
622 byte mask1 = narrow_cast<byte>((addr & 0x40000) ? (mask >> 8) : (mask & 0xFF));
623 byte result = (dstColor & ~mask1) | (newColor & mask1);
624 vram.writeVRAMDirect(addr, result);
628inline unsigned V9990CmdEngine::V9990Bpp16::getPitch(
unsigned width)
634inline unsigned V9990CmdEngine::V9990Bpp16::addressOf(
635 unsigned x,
unsigned y,
unsigned pitch)
638 return ((x & (pitch - 1)) + y * pitch) & 0x3FFFF;
641inline word V9990CmdEngine::V9990Bpp16::point(
642 const V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch)
644 unsigned addr = addressOf(x, y, pitch);
645 return word(vram.readVRAMDirect(addr + 0x00000) +
646 vram.readVRAMDirect(addr + 0x40000) * 256);
649inline word V9990CmdEngine::V9990Bpp16::shift(
650 word value,
unsigned ,
unsigned )
655inline word V9990CmdEngine::V9990Bpp16::shiftMask(
unsigned )
660inline std::span<const byte, 256 * 256> V9990CmdEngine::V9990Bpp16::getLogOpLUT(
byte op)
665inline word V9990CmdEngine::V9990Bpp16::logOp(
666 std::span<const byte, 256 * 256> lut,
word src,
word dst,
bool transp)
668 if (transp && (src == 0))
return dst;
669 return word((lut[((dst & 0x00FF) << 8) + ((src & 0x00FF) >> 0)] << 0) +
670 (lut[((dst & 0xFF00) << 0) + ((src & 0xFF00) >> 8)] << 8));
673inline void V9990CmdEngine::V9990Bpp16::pset(
674 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
675 word srcColor,
word mask, std::span<const byte, 256 * 256> lut,
byte op)
677 unsigned addr = addressOf(x, y, pitch);
678 auto dstColor =
word(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, narrow_cast<byte>(result & 0xFF));
683 vram.writeVRAMDirect(addr + 0x40000, narrow_cast<byte>(result >> 8));
686inline void V9990CmdEngine::V9990Bpp16::psetColor(
687 V9990VRAM& vram,
unsigned x,
unsigned y,
unsigned pitch,
688 word srcColor,
word mask, std::span<const byte, 256 * 256> lut,
byte op)
690 unsigned addr = addressOf(x, y, pitch);
691 auto dstColor =
word(vram.readVRAMDirect(addr + 0x00000) +
692 vram.readVRAMDirect(addr + 0x40000) * 256);
693 word newColor = logOp(lut, srcColor, dstColor, (op & 0x10) != 0);
694 word result = (dstColor & ~mask) | (newColor & mask);
695 vram.writeVRAMDirect(addr + 0x00000, narrow_cast<byte>(result & 0xFF));
696 vram.writeVRAMDirect(addr + 0x40000, narrow_cast<byte>(result >> 8));
704 : settings(settings_), vdp(vdp_), vram(vdp.getVRAM()), engineTime(time_)
709 "V9990 command tracing on/off",
false);
712 update(cmdTimingSetting);
713 cmdTimingSetting.
attach(*
this);
718 srcAddress = dstAddress = nbBytes = 0;
719 ASX = ADX = ANX = ANY = 0;
720 SX = SY = DX = DY = NX = NY = 0;
721 WM = fgCol = bgCol = 0;
723 data = bitsLeft = partial = 0;
736 endAfterRead =
false;
744 SX =
word((SX & 0x0700) | ((value & 0xFF) << 0));
747 SX =
word((SX & 0x00FF) | ((value & 0x07) << 8));
750 SY =
word((SY & 0x0F00) | ((value & 0xFF) << 0));
753 SY =
word((SY & 0x00FF) | ((value & 0x0F) << 8));
756 DX =
word((DX & 0x0700) | ((value & 0xFF) << 0));
759 DX =
word((DX & 0x00FF) | ((value & 0x07) << 8));
762 DY =
word((DY & 0x0F00) | ((value & 0xFF) << 0));
765 DY =
word((DY & 0x00FF) | ((value & 0x0F) << 8));
768 NX =
word((NX & 0x0F00) | ((value & 0xFF) << 0));
771 NX =
word((NX & 0x00FF) | ((value & 0x0F) << 8));
774 NY =
word((NY & 0x0F00) | ((value & 0xFF) << 0));
777 NY =
word((NY & 0x00FF) | ((value & 0x0F) << 8));
786 WM =
word((WM & 0xFF00) | (value << 0));
789 WM =
word((WM & 0x00FF) | (value << 8));
792 fgCol =
word((fgCol & 0xFF00) | (value << 0));
795 fgCol =
word((fgCol & 0x00FF) | (value << 8));
798 bgCol =
word((bgCol & 0xFF00) | (value << 0));
801 bgCol =
word((bgCol & 0x00FF) | (value << 8));
805 if (cmdTraceSetting->getBoolean()) {
806 reportV9990Command();
814 switch (cmdMode | (CMD >> 4)) {
815 case 0x00:
case 0x10:
case 0x20:
case 0x30:
case 0x40:
case 0x50:
816 startSTOP(time);
break;
818 case 0x01:
case 0x11:
case 0x21:
case 0x31:
case 0x41:
819 startLMMC (time);
break;
821 startLMMC16(time);
break;
823 case 0x02:
case 0x12:
case 0x22:
case 0x32:
case 0x42:
case 0x52:
824 startLMMV(time);
break;
826 case 0x03:
case 0x13:
case 0x23:
case 0x33:
case 0x43:
827 startLMCM (time);
break;
829 startLMCM16(time);
break;
831 case 0x04:
case 0x14:
case 0x24:
case 0x34:
case 0x44:
case 0x54:
832 startLMMM(time);
break;
834 case 0x05:
case 0x15:
case 0x25:
case 0x35:
case 0x45:
case 0x55:
835 startCMMC(time);
break;
837 case 0x06:
case 0x16:
case 0x26:
case 0x36:
case 0x46:
case 0x56:
838 startCMMK(time);
break;
840 case 0x07:
case 0x17:
case 0x27:
case 0x37:
case 0x47:
case 0x57:
841 startCMMM(time);
break;
843 case 0x08:
case 0x18:
case 0x28:
case 0x38:
case 0x48:
case 0x58:
844 startBMXL(time);
break;
846 case 0x09:
case 0x19:
case 0x29:
case 0x39:
case 0x49:
case 0x59:
847 startBMLX(time);
break;
849 case 0x0A:
case 0x1A:
case 0x2A:
case 0x3A:
case 0x4A:
850 startBMLL (time);
break;
852 startBMLL16(time);
break;
854 case 0x0B:
case 0x1B:
case 0x2B:
case 0x3B:
case 0x4B:
case 0x5B:
855 startLINE(time);
break;
857 case 0x0C:
case 0x1C:
case 0x2C:
case 0x3C:
case 0x4C:
case 0x5C:
858 startSRCH(time);
break;
860 case 0x0D: startPOINT<V9990P1 >(time);
break;
861 case 0x1D: startPOINT<V9990P2 >(time);
break;
862 case 0x2D: startPOINT<V9990Bpp2 >(time);
break;
863 case 0x3D: startPOINT<V9990Bpp4 >(time);
break;
864 case 0x4D: startPOINT<V9990Bpp8 >(time);
break;
865 case 0x5D: startPOINT<V9990Bpp16>(time);
break;
867 case 0x0E: startPSET<V9990P1 >(time);
break;
868 case 0x1E: startPSET<V9990P2 >(time);
break;
869 case 0x2E: startPSET<V9990Bpp2 >(time);
break;
870 case 0x3E: startPSET<V9990Bpp4 >(time);
break;
871 case 0x4E: startPSET<V9990Bpp8 >(time);
break;
872 case 0x5E: startPSET<V9990Bpp16>(time);
break;
874 case 0x0F:
case 0x1F:
case 0x2F:
case 0x3F:
case 0x4F:
case 0x5F:
875 startADVN(time);
break;
889void V9990CmdEngine::setCommandMode()
892 if (dispMode ==
P1) {
894 }
else if (dispMode ==
P2) {
922void V9990CmdEngine::reportV9990Command()
const
924 static constexpr std::array<std::string_view, 16> COMMANDS = {
925 "STOP",
"LMMC",
"LMMV",
"LMCM",
926 "LMMM",
"CMMC",
"CMMK",
"CMMM",
927 "BMXL",
"BMLX",
"BMLL",
"LINE",
928 "SRCH",
"POINT",
"PSET",
"ADVN"
930 std::cerr <<
"V9990Cmd " << COMMANDS[CMD >> 4]
931 <<
" SX=" << std::dec << SX
932 <<
" SY=" << std::dec << SY
933 <<
" DX=" << std::dec << DX
934 <<
" DY=" << std::dec << DY
935 <<
" NX=" << std::dec << NX
936 <<
" NY=" << std::dec << NY
937 <<
" ARG=" << std::hex << int(ARG)
938 <<
" LOG=" << std::hex << int(LOG)
939 <<
" WM=" << std::hex << WM
940 <<
" FC=" << std::hex << fgCol
941 <<
" BC=" << std::hex << bgCol
942 <<
" CMD=" << std::hex << int(CMD)
946void V9990CmdEngine::update(
const Setting&
setting)
noexcept
948 brokenTiming = checked_cast<const EnumSetting<bool>&>(
setting).getEnum();
952void V9990CmdEngine::startSTOP(EmuTime::param time)
957void V9990CmdEngine::executeSTOP(EmuTime::param )
963void V9990CmdEngine::startLMMC(EmuTime::param )
965 ANX = getWrappedNX();
966 ANY = getWrappedNY();
969void V9990CmdEngine::startLMMC16(EmuTime::param time)
976void V9990CmdEngine::executeLMMC<V9990CmdEngine::V9990Bpp16>(EmuTime::param limit)
978 if (!(status & TR)) {
985 auto value =
word((data << 8) | partial);
986 unsigned pitch = V9990Bpp16::getPitch(vdp.getImageWidth());
987 auto lut = V9990Bpp16::getLogOpLUT(LOG);
988 V9990Bpp16::pset(vram, DX, DY, pitch, value, WM, lut, LOG);
989 word dx = (ARG & DIX) ?
word(-1) : 1;
992 word dy = (ARG & DIY) ?
word(-1) : 1;
998 ANX = getWrappedNX();
1005template<
typename Mode>
1006void V9990CmdEngine::executeLMMC(EmuTime::param limit)
1008 if (!(status &
TR)) {
1011 auto lut = Mode::getLogOpLUT(LOG);
1012 for (
int i = 0; (ANY > 0) && (i < Mode::PIXELS_PER_BYTE); ++i) {
1013 byte d = Mode::shift(data, i, DX);
1014 Mode::pset(vram, DX, DY, pitch, d, WM, lut, LOG);
1016 word dx = (ARG & DIX) ?
word(-1) : 1;
1019 word dy = (ARG & DIY) ?
word(-1) : 1;
1020 DX -=
word(NX * dx);
1033void V9990CmdEngine::startLMMV(EmuTime::param time)
1036 ANX = getWrappedNX();
1037 ANY = getWrappedNY();
1040template<
typename Mode>
1041void V9990CmdEngine::executeLMMV(EmuTime::param limit)
1045 auto delta = getTiming(*
this, LMMV_TIMING);
1047 word dx = (ARG & DIX) ?
word(-1) : 1;
1048 word dy = (ARG & DIY) ?
word(-1) : 1;
1049 auto lut = Mode::getLogOpLUT(LOG);
1050 while (engineTime < limit) {
1051 engineTime += delta;
1052 Mode::psetColor(vram, DX, DY, pitch, fgCol, WM, lut, LOG);
1056 DX -=
word(NX * dx);
1059 cmdReady(engineTime);
1062 ANX = getWrappedNX();
1069void V9990CmdEngine::startLMCM(EmuTime::param )
1071 ANX = getWrappedNX();
1072 ANY = getWrappedNY();
1074 endAfterRead =
false;
1076void V9990CmdEngine::startLMCM16(EmuTime::param time)
1082template<
typename Mode>
1083void V9990CmdEngine::executeLMCM(EmuTime::param )
1085 if (!(status &
TR)) {
1087 if ((Mode::BITS_PER_PIXEL == 16) && bitsLeft) {
1093 using Type =
typename Mode::Type;
1095 for (
int i = 0; (ANY > 0) && (i < Mode::PIXELS_PER_BYTE); ++i) {
1096 auto src = Mode::point(vram, SX, SY, pitch);
1097 d |= Type(Mode::shift(src, SX, i) & Mode::shiftMask(i));
1099 word dx = (ARG & DIX) ?
word(-1) : 1;
1102 word dy = (ARG & DIY) ?
word(-1) : 1;
1103 SX -=
word(NX * dx);
1106 endAfterRead =
true;
1108 ANX = getWrappedNX();
1112 if constexpr (Mode::BITS_PER_PIXEL == 16) {
1116 data = narrow_cast<byte>(tmp & 0xff);
1117 partial = narrow_cast<byte>(tmp >> 8);
1126void V9990CmdEngine::startLMMM(EmuTime::param time)
1129 ANX = getWrappedNX();
1130 ANY = getWrappedNY();
1133template<
typename Mode>
1134void V9990CmdEngine::executeLMMM(EmuTime::param limit)
1138 auto delta = getTiming(*
this, LMMM_TIMING);
1140 word dx = (ARG & DIX) ?
word(-1) : 1;
1141 word dy = (ARG & DIY) ?
word(-1) : 1;
1142 auto lut = Mode::getLogOpLUT(LOG);
1143 while (engineTime < limit) {
1144 engineTime += delta;
1145 auto src = Mode::point(vram, SX, SY, pitch);
1146 src = Mode::shift(src, SX, DX);
1147 Mode::pset(vram, DX, DY, pitch, src, WM, lut, LOG);
1152 DX -=
word(NX * dx);
1153 SX -=
word(NX * dx);
1157 cmdReady(engineTime);
1160 ANX = getWrappedNX();
1167void V9990CmdEngine::startCMMC(EmuTime::param )
1169 ANX = getWrappedNX();
1170 ANY = getWrappedNY();
1174template<
typename Mode>
1175void V9990CmdEngine::executeCMMC(EmuTime::param limit)
1177 if (!(status &
TR)) {
1181 word dx = (ARG & DIX) ?
word(-1) : 1;
1182 word dy = (ARG & DIY) ?
word(-1) : 1;
1183 auto lut = Mode::getLogOpLUT(LOG);
1184 for (
auto i :
xrange(8)) {
1186 bool bit = (data & 0x80) != 0;
1189 word src = bit ? fgCol : bgCol;
1190 Mode::psetColor(vram, DX, DY, pitch, src, WM, lut, LOG);
1194 DX -=
word(NX * dx);
1200 ANX = getWrappedNX();
1208void V9990CmdEngine::startCMMK(EmuTime::param time)
1210 std::cout <<
"V9990: CMMK not yet implemented\n";
1214void V9990CmdEngine::executeCMMK(EmuTime::param )
1220void V9990CmdEngine::startCMMM(EmuTime::param time)
1223 srcAddress = (SX & 0xFF) + ((SY & 0x7FF) << 8);
1224 ANX = getWrappedNX();
1225 ANY = getWrappedNY();
1229template<
typename Mode>
1230void V9990CmdEngine::executeCMMM(EmuTime::param limit)
1234 auto delta = getTiming(*
this, CMMM_TIMING);
1236 word dx = (ARG & DIX) ?
word(-1) : 1;
1237 word dy = (ARG & DIY) ?
word(-1) : 1;
1238 auto lut = Mode::getLogOpLUT(LOG);
1239 while (engineTime < limit) {
1240 engineTime += delta;
1242 data = vram.readVRAMBx(srcAddress++);
1246 bool bit = (data & 0x80) != 0;
1249 word color = bit ? fgCol : bgCol;
1250 Mode::psetColor(vram, DX, DY, pitch, color, WM, lut, LOG);
1254 DX -=
word(NX * dx);
1257 cmdReady(engineTime);
1260 ANX = getWrappedNX();
1267void V9990CmdEngine::startBMXL(EmuTime::param time)
1270 srcAddress = (SX & 0xFF) + ((SY & 0x7FF) << 8);
1271 ANX = getWrappedNX();
1272 ANY = getWrappedNY();
1276void V9990CmdEngine::executeBMXL<V9990CmdEngine::V9990Bpp16>(EmuTime::param limit)
1279 auto delta = getTiming(*
this, BMXL_TIMING) * 2;
1280 unsigned pitch = V9990Bpp16::getPitch(vdp.getImageWidth());
1281 word dx = (ARG & DIX) ?
word(-1) : 1;
1282 word dy = (ARG & DIY) ?
word(-1) : 1;
1283 auto lut = V9990Bpp16::getLogOpLUT(LOG);
1285 while (engineTime < limit) {
1286 engineTime += delta;
1287 auto src =
word(vram.readVRAMBx(srcAddress + 0) +
1288 vram.readVRAMBx(srcAddress + 1) * 256);
1290 V9990Bpp16::pset(vram, DX, DY, pitch, src, WM, lut, LOG);
1293 DX -=
word(NX * dx);
1296 cmdReady(engineTime);
1299 ANX = getWrappedNX();
1305template<
typename Mode>
1306void V9990CmdEngine::executeBMXL(EmuTime::param limit)
1308 auto delta = getTiming(*
this, BMXL_TIMING);
1310 word dx = (ARG & DIX) ?
word(-1) : 1;
1311 word dy = (ARG & DIY) ?
word(-1) : 1;
1312 auto lut = Mode::getLogOpLUT(LOG);
1314 while (engineTime < limit) {
1315 engineTime += delta;
1316 byte d = vram.readVRAMBx(srcAddress++);
1317 for (
int i = 0; (ANY > 0) && (i < Mode::PIXELS_PER_BYTE); ++i) {
1318 auto d2 = Mode::shift(d, i, DX);
1319 Mode::pset(vram, DX, DY, pitch, d2, WM, lut, LOG);
1322 DX -=
word(NX * dx);
1325 cmdReady(engineTime);
1328 ANX = getWrappedNX();
1336void V9990CmdEngine::startBMLX(EmuTime::param time)
1339 dstAddress = (DX & 0xFF) + ((DY & 0x7FF) << 8);
1340 ANX = getWrappedNX();
1341 ANY = getWrappedNY();
1345void V9990CmdEngine::executeBMLX<V9990CmdEngine::V9990Bpp16>(EmuTime::param limit)
1348 auto delta = getTiming(*
this, BMLX_TIMING);
1349 unsigned pitch = V9990Bpp16::getPitch(vdp.getImageWidth());
1350 word dx = (ARG & DIX) ?
word(-1) : 1;
1351 word dy = (ARG & DIY) ?
word(-1) : 1;
1353 while (engineTime < limit) {
1354 engineTime += delta;
1355 auto src = V9990Bpp16::point(vram, SX, SY, pitch);
1356 vram.writeVRAMBx(dstAddress++, narrow_cast<byte>(src & 0xFF));
1357 vram.writeVRAMBx(dstAddress++, narrow_cast<byte>(src >> 8));
1360 SX -=
word(NX * dx);
1363 cmdReady(engineTime);
1366 ANX = getWrappedNX();
1371template<
typename Mode>
1372void V9990CmdEngine::executeBMLX(EmuTime::param limit)
1375 auto delta = getTiming(*
this, BMLX_TIMING);
1377 word dx = (ARG & DIX) ?
word(-1) : 1;
1378 word dy = (ARG & DIY) ?
word(-1) : 1;
1380 while (engineTime < limit) {
1381 engineTime += delta;
1383 for (
auto i :
xrange(Mode::PIXELS_PER_BYTE)) {
1384 auto src = Mode::point(vram, SX, SY, pitch);
1385 d |=
byte(Mode::shift(src, SX, i) & Mode::shiftMask(i));
1388 SX -=
word(NX * dx);
1391 vram.writeVRAMBx(dstAddress++, d);
1392 cmdReady(engineTime);
1395 ANX = getWrappedNX();
1399 vram.writeVRAMBx(dstAddress++, d);
1404void V9990CmdEngine::startBMLL(EmuTime::param time)
1407 srcAddress = (SX & 0xFF) + ((SY & 0x7FF) << 8);
1408 dstAddress = (DX & 0xFF) + ((DY & 0x7FF) << 8);
1409 nbBytes = (NX & 0xFF) + ((NY & 0x7FF) << 8);
1414void V9990CmdEngine::startBMLL16(EmuTime::param time)
1425void V9990CmdEngine::executeBMLL<V9990CmdEngine::V9990Bpp16>(EmuTime::param limit)
1429 auto delta = getTiming(*
this, BMLL_TIMING) * 2;
1430 auto lut = V9990Bpp16::getLogOpLUT(LOG);
1431 bool transp = (LOG & 0x10) != 0;
1432 while (engineTime < limit) {
1433 engineTime += delta;
1435 auto srcColor =
word(vram.readVRAMDirect(srcAddress + 0x00000) +
1436 vram.readVRAMDirect(srcAddress + 0x40000) * 256);
1437 auto dstColor =
word(vram.readVRAMDirect(dstAddress + 0x00000) +
1438 vram.readVRAMDirect(dstAddress + 0x40000) * 256);
1439 word newColor = V9990Bpp16::logOp(lut, srcColor, dstColor, transp);
1440 word result = (dstColor & ~WM) | (newColor & WM);
1441 vram.writeVRAMDirect(dstAddress + 0x00000, narrow_cast<byte>(result & 0xFF));
1442 vram.writeVRAMDirect(dstAddress + 0x40000, narrow_cast<byte>(result >> 8));
1443 srcAddress = (srcAddress + 1) & 0x3FFFF;
1444 dstAddress = (dstAddress + 1) & 0x3FFFF;
1446 cmdReady(engineTime);
1452template<
typename Mode>
1453void V9990CmdEngine::executeBMLL(EmuTime::param limit)
1456 auto delta = getTiming(*
this, BMLL_TIMING);
1457 auto lut = Mode::getLogOpLUT(LOG);
1458 while (engineTime < limit) {
1459 engineTime += delta;
1461 byte srcColor = vram.readVRAMBx(srcAddress);
1463 byte dstColor = vram.readVRAMDirect(addr);
1464 byte newColor = Mode::logOp(lut, srcColor, dstColor);
1465 byte mask = narrow_cast<byte>((addr & 0x40000) ? (WM >> 8) : (WM & 0xFF));
1466 byte result = (dstColor & ~mask) | (newColor & mask);
1467 vram.writeVRAMDirect(addr, result);
1468 srcAddress = (srcAddress + 1) & 0x7FFFF;
1469 dstAddress = (dstAddress + 1) & 0x7FFFF;
1471 cmdReady(engineTime);
1478void V9990CmdEngine::startLINE(EmuTime::param time)
1481 ASX =
word((NX - 1) / 2);
1486template<
typename Mode>
1487void V9990CmdEngine::executeLINE(EmuTime::param limit)
1489 auto delta = getTiming(*
this, LINE_TIMING);
1491 unsigned pitch = Mode::getPitch(width);
1493 word TX = (ARG & DIX) ?
word(-1) : 1;
1494 word TY = (ARG & DIY) ?
word(-1) : 1;
1495 auto lut = Mode::getLogOpLUT(LOG);
1497 if ((ARG & MAJ) == 0) {
1499 while (engineTime < limit) {
1500 engineTime += delta;
1501 Mode::psetColor(vram, ADX, DY, pitch, fgCol, WM, lut, LOG);
1510 if (ANX++ == NX || (ADX & width)) {
1511 cmdReady(engineTime);
1517 while (engineTime < limit) {
1518 engineTime += delta;
1519 Mode::psetColor(vram, ADX, DY, pitch, fgCol, WM, lut, LOG);
1527 if (ANX++ == NX || (ADX & width)) {
1528 cmdReady(engineTime);
1536void V9990CmdEngine::startSRCH(EmuTime::param time)
1542template<
typename Mode>
1543void V9990CmdEngine::executeSRCH(EmuTime::param limit)
1545 using Type =
typename Mode::Type;
1546 auto delta = getTiming(*
this, SRCH_TIMING);
1548 unsigned pitch = Mode::getPitch(width);
1549 Type mask = (1 << Mode::BITS_PER_PIXEL) -1;
1551 word TX = (ARG & DIX) ?
word(-1) : 1;
1552 bool AEQ = (ARG & NEQ) != 0;
1554 while (engineTime < limit) {
1555 engineTime += delta;
1559 if constexpr (Mode::BITS_PER_PIXEL == 16) {
1560 value = Mode::point(vram, ASX, SY, pitch);
1561 col =
static_cast<Type
>(fgCol);
1562 mask2 =
static_cast<Type
>(~0);
1565 unsigned addr = Mode::addressOf(ASX, SY, pitch);
1566 value = vram.readVRAMDirect(addr);
1567 col = narrow_cast<byte>((addr & 0x40000) ? (fgCol >> 8) : (fgCol & 0xFF));
1568 mask2 = Mode::shift(mask, 3, ASX);
1570 if (((value & mask2) == (col & mask2)) ^ AEQ) {
1572 cmdReady(engineTime);
1579 cmdReady(engineTime);
1587template<
typename Mode>
1588void V9990CmdEngine::startPOINT(EmuTime::param )
1591 auto d = Mode::point(vram, SX, SY, pitch);
1593 if constexpr (Mode::BITS_PER_PIXEL != 16) {
1595 endAfterRead =
true;
1600 data = narrow_cast<byte>(tmp & 0xff);
1601 partial = narrow_cast<byte>(tmp >> 8);
1602 endAfterRead =
false;
1607template<
typename Mode>
1608void V9990CmdEngine::executePOINT(EmuTime::param )
1610 if (status &
TR)
return;
1612 assert(Mode::BITS_PER_PIXEL == 16);
1615 endAfterRead =
true;
1619template<
typename Mode>
1620void V9990CmdEngine::startPSET(EmuTime::param time)
1623 auto lut = Mode::getLogOpLUT(LOG);
1624 Mode::psetColor(vram, DX, DY, pitch, fgCol, WM, lut, LOG);
1631void V9990CmdEngine::executePSET(EmuTime::param )
1637void V9990CmdEngine::startADVN(EmuTime::param time)
1639 std::cout <<
"V9990: ADVN not yet implemented\n";
1643void V9990CmdEngine::executeADVN(EmuTime::param )
1653 switch (cmdMode | (CMD >> 4)) {
1654 case 0x00:
case 0x10:
case 0x20:
case 0x30:
case 0x40:
case 0x50:
1655 executeSTOP(time);
break;
1657 case 0x01: executeLMMC<V9990P1 >(time);
break;
1658 case 0x11: executeLMMC<V9990P2 >(time);
break;
1659 case 0x21: executeLMMC<V9990Bpp2 >(time);
break;
1660 case 0x31: executeLMMC<V9990Bpp4 >(time);
break;
1661 case 0x41: executeLMMC<V9990Bpp8 >(time);
break;
1662 case 0x51: executeLMMC<V9990Bpp16>(time);
break;
1664 case 0x02: executeLMMV<V9990P1 >(time);
break;
1665 case 0x12: executeLMMV<V9990P2 >(time);
break;
1666 case 0x22: executeLMMV<V9990Bpp2 >(time);
break;
1667 case 0x32: executeLMMV<V9990Bpp4 >(time);
break;
1668 case 0x42: executeLMMV<V9990Bpp8 >(time);
break;
1669 case 0x52: executeLMMV<V9990Bpp16>(time);
break;
1671 case 0x03: executeLMCM<V9990P1 >(time);
break;
1672 case 0x13: executeLMCM<V9990P2 >(time);
break;
1673 case 0x23: executeLMCM<V9990Bpp2 >(time);
break;
1674 case 0x33: executeLMCM<V9990Bpp4 >(time);
break;
1675 case 0x43: executeLMCM<V9990Bpp8 >(time);
break;
1676 case 0x53: executeLMCM<V9990Bpp16>(time);
break;
1678 case 0x04: executeLMMM<V9990P1 >(time);
break;
1679 case 0x14: executeLMMM<V9990P2 >(time);
break;
1680 case 0x24: executeLMMM<V9990Bpp2 >(time);
break;
1681 case 0x34: executeLMMM<V9990Bpp4 >(time);
break;
1682 case 0x44: executeLMMM<V9990Bpp8 >(time);
break;
1683 case 0x54: executeLMMM<V9990Bpp16>(time);
break;
1685 case 0x05: executeCMMC<V9990P1 >(time);
break;
1686 case 0x15: executeCMMC<V9990P2 >(time);
break;
1687 case 0x25: executeCMMC<V9990Bpp2 >(time);
break;
1688 case 0x35: executeCMMC<V9990Bpp4 >(time);
break;
1689 case 0x45: executeCMMC<V9990Bpp8 >(time);
break;
1690 case 0x55: executeCMMC<V9990Bpp16>(time);
break;
1692 case 0x06:
case 0x16:
case 0x26:
case 0x36:
case 0x46:
case 0x56:
1693 executeCMMK(time);
break;
1695 case 0x07: executeCMMM<V9990P1 >(time);
break;
1696 case 0x17: executeCMMM<V9990P2 >(time);
break;
1697 case 0x27: executeCMMM<V9990Bpp2 >(time);
break;
1698 case 0x37: executeCMMM<V9990Bpp4 >(time);
break;
1699 case 0x47: executeCMMM<V9990Bpp8 >(time);
break;
1700 case 0x57: executeCMMM<V9990Bpp16>(time);
break;
1702 case 0x08: executeBMXL<V9990P1 >(time);
break;
1703 case 0x18: executeBMXL<V9990P2 >(time);
break;
1704 case 0x28: executeBMXL<V9990Bpp2 >(time);
break;
1705 case 0x38: executeBMXL<V9990Bpp4 >(time);
break;
1706 case 0x48: executeBMXL<V9990Bpp8 >(time);
break;
1707 case 0x58: executeBMXL<V9990Bpp16>(time);
break;
1709 case 0x09: executeBMLX<V9990P1 >(time);
break;
1710 case 0x19: executeBMLX<V9990P2 >(time);
break;
1711 case 0x29: executeBMLX<V9990Bpp2 >(time);
break;
1712 case 0x39: executeBMLX<V9990Bpp4 >(time);
break;
1713 case 0x49: executeBMLX<V9990Bpp8 >(time);
break;
1714 case 0x59: executeBMLX<V9990Bpp16>(time);
break;
1716 case 0x0A: executeBMLL<V9990P1 >(time);
break;
1717 case 0x1A: executeBMLL<V9990P2 >(time);
break;
1718 case 0x2A: executeBMLL<V9990Bpp2 >(time);
break;
1719 case 0x3A: executeBMLL<V9990Bpp4 >(time);
break;
1720 case 0x4A: executeBMLL<V9990Bpp8 >(time);
break;
1721 case 0x5A: executeBMLL<V9990Bpp16>(time);
break;
1723 case 0x0B: executeLINE<V9990P1 >(time);
break;
1724 case 0x1B: executeLINE<V9990P2 >(time);
break;
1725 case 0x2B: executeLINE<V9990Bpp2 >(time);
break;
1726 case 0x3B: executeLINE<V9990Bpp4 >(time);
break;
1727 case 0x4B: executeLINE<V9990Bpp8 >(time);
break;
1728 case 0x5B: executeLINE<V9990Bpp16>(time);
break;
1730 case 0x0C: executeSRCH<V9990P1 >(time);
break;
1731 case 0x1C: executeSRCH<V9990P2 >(time);
break;
1732 case 0x2C: executeSRCH<V9990Bpp2 >(time);
break;
1733 case 0x3C: executeSRCH<V9990Bpp4 >(time);
break;
1734 case 0x4C: executeSRCH<V9990Bpp8 >(time);
break;
1735 case 0x5C: executeSRCH<V9990Bpp16>(time);
break;
1737 case 0x0D: executePOINT<V9990P1 >(time);
break;
1738 case 0x1D: executePOINT<V9990P2 >(time);
break;
1739 case 0x2D: executePOINT<V9990Bpp2 >(time);
break;
1740 case 0x3D: executePOINT<V9990Bpp4 >(time);
break;
1741 case 0x4D: executePOINT<V9990Bpp8 >(time);
break;
1742 case 0x5D: executePOINT<V9990Bpp16>(time);
break;
1744 case 0x0E:
case 0x1E:
case 0x2E:
case 0x3E:
case 0x4E:
case 0x5E:
1745 executePSET(time);
break;
1747 case 0x0F:
case 0x1F:
case 0x2F:
case 0x3F:
case 0x4F:
case 0x5F:
1748 executeADVN(time);
break;
1770 endAfterRead =
false;
1780 return (status &
TR) ? data : 0xFF;
1783void V9990CmdEngine::cmdReady(EmuTime::param )
1786 status &= ~(
CE |
TR);
1812 delta = getTiming(*
this, LMMV_TIMING) * (ANX + (ANY - 1) * getWrappedNX());
1815 delta = getTiming(*
this, LMMM_TIMING) * (ANX + (ANY - 1) * getWrappedNX());
1818 delta = getTiming(*
this, CMMM_TIMING) * (ANX + (ANY - 1) * getWrappedNX());
1821 delta = getTiming(*
this, BMXL_TIMING) * (ANX + (ANY - 1) * getWrappedNX());
1824 delta = getTiming(*
this, BMLX_TIMING) * (ANX + (ANY - 1) * getWrappedNX());
1833 delta = getTiming(*
this, BMLL_TIMING) * nbBytes;
1837 delta = getTiming(*
this, LINE_TIMING) * (NX - ANX);
1842 delta = getTiming(*
this, SRCH_TIMING);
1853 return engineTime + delta;
1858template<
typename Archive>
1862 if (ar.versionAtLeast(version, 2)) {
1870 ar.serialize(
"srcAddress", srcAddress,
1871 "dstAddress", dstAddress,
1892 "bitsLeft", bitsLeft,
1894 "endAfterRead", endAfterRead);
1896 if constexpr (Archive::IS_LOADER) {
static constexpr EmuDuration duration(unsigned ticks)
Calculates the duration of the given number of ticks at this clock's frequency.
void serialize(Archive &ar, unsigned)
constexpr uint64_t length() const
static constexpr EmuDuration zero()
MSXMotherBoard & getMotherBoard() const
Get the mother board this device belongs to.
CommandController & getCommandController() const
std::shared_ptr< T > getSharedStuff(std::string_view name, Args &&...args)
Some MSX device parts are shared between several MSX devices (e.g.
Class containing all settings for renderers.
EnumSetting< bool > & getCmdTimingSetting()
CmdTiming [real, broken].
void detach(Observer< T > &observer)
void attach(Observer< T > &observer)
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.
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.
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.
void sync2(EmuTime::param time)
static unsigned transformP2(unsigned address)
static unsigned transformBx(unsigned address)
static unsigned transformP1(unsigned address)
Implementation of the Yamaha V9990 VDP as used in the GFX9000 cartridge by Sunrise.
void cmdReady()
Command execution ready.
V9990DisplayMode getDisplayMode() const
Return the current display mode.
unsigned getImageWidth() const
Return the image width.
V9990ColorMode getColorMode() const
Return the current color mode.
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
std::span< const A2, 4 > TimingTable
std::conditional_t<(MAX > detail::max32), EmuDuration, std::conditional_t<(MAX > detail::max16), EmuDuration32, std::conditional_t<(MAX > detail::max8), EmuDuration16, EmuDuration8 > > > EmuDurationStorageFor
uint16_t word
16 bit unsigned integer
EmuDurationStorageFor< d_(maxLength).length()> EDStorage
std::array< const EDStorage, 4 > A
std::array< const A, 3 > A2
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)