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