48 static byte getDelayCycles(
const XMLElement& devices) {
50 const XMLElement* t9769Dev = devices.findChild(
"T9769");
52 if (t9769Dev->getChildData(
"subtype") ==
"C") {
57 }
else if (devices.findChild(
"S1990")) {
68 , syncDisplayStart(*this)
71 , syncHorAdjust(*this)
74 , syncSetSprites(*this)
75 , syncCpuVramAccess(*this)
77 , display(getReactor().getDisplay())
78 , cmdTiming (display.getRenderSettings().getCmdTimingSetting())
79 , tooFastAccess(display.getRenderSettings().getTooFastAccessSetting())
81 , vdpStatusRegDebug(*this)
82 , vdpPaletteDebug (*this)
83 , vramPointerDebug (*this)
84 , frameCountInfo (*this)
85 , cycleInFrameInfo (*this)
86 , lineInFrameInfo (*this)
87 , cycleInLineInfo (*this)
89 , msxX256PosInfo (*this)
90 , msxX512PosInfo (*this)
91 , frameStartTime(getCurrentTime())
92 , irqVertical (getMotherBoard(),
getName() +
".IRQvertical", config)
93 , irqHorizontal(getMotherBoard(),
getName() +
".IRQhorizontal", config)
94 , displayStartSyncTime(getCurrentTime())
95 , vScanSyncTime(getCurrentTime())
96 , hScanSyncTime(getCurrentTime())
98 getCommandController(),
99 getName() +
".too_fast_vram_access_callback",
100 "Tcl proc called when the VRAM is read or written too fast")
101 , warningPrinted(false)
103 , fixedVDPIOdelayCycles(getDelayCycles(getMotherBoard().getMachineConfig()->getConfig().getChild(
"devices")))
117 int defaultSaturation = 54;
119 std::string versionString = config.
getChildData(
"version");
120 if (versionString ==
"TMS99X8A") version = TMS99X8A;
121 else if (versionString ==
"TMS9918A") {
123 defaultSaturation = 100;
124 }
else if (versionString ==
"TMS9928A") version = TMS99X8A;
125 else if (versionString ==
"T6950PAL") version = T6950PAL;
126 else if (versionString ==
"T6950NTSC") version = T6950NTSC;
127 else if (versionString ==
"T7937APAL") version = T7937APAL;
128 else if (versionString ==
"T7937ANTSC") version = T7937ANTSC;
129 else if (versionString ==
"TMS91X8") version = TMS91X8;
130 else if (versionString ==
"TMS9118") {
132 defaultSaturation = 100;
133 }
else if (versionString ==
"TMS9128") version = TMS91X8;
134 else if (versionString ==
"TMS9929A") version = TMS9929A;
135 else if (versionString ==
"TMS9129") version = TMS9129;
136 else if (versionString ==
"V9938") version = V9938;
137 else if (versionString ==
"V9958") version = V9958;
138 else if (versionString ==
"YM2220PAL") version = YM2220PAL;
139 else if (versionString ==
"YM2220NTSC") version = YM2220NTSC;
140 else throw MSXException(
"Unknown VDP version \"", versionString,
'"');
143 if ((versionString.find(
"TMS") != 0) && ((config.
findChild(
"saturationPr") !=
nullptr) || (config.
findChild(
"saturationPb") !=
nullptr) || (config.
findChild(
"saturation") !=
nullptr))) {
144 throw MSXException(
"Specifying saturation parameters only makes sense for TMS VDPs");
148 if (!((0 <= saturation) && (saturation <= 100))) {
150 "Saturation percentage is not in range 0..100: ", saturationPr);
153 if (!((0 <= saturationPr) && (saturationPr <= 100))) {
155 "Saturation percentage for Pr component is not in range 0..100: ",
159 if (!((0 <= saturationPb) && (saturationPb <= 100))) {
161 "Saturation percentage for Pb component is not in range 0..100: ",
166 static constexpr
byte VALUE_MASKS_MSX1[32] = {
167 0x03, 0xFB, 0x0F, 0xFF, 0x07, 0x7F, 0x07, 0xFF
169 static constexpr
byte VALUE_MASKS_MSX2[32] = {
170 0x7E, 0x7F, 0x7F, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF,
171 0xFB, 0xBF, 0x07, 0x03, 0xFF, 0xFF, 0x07, 0x0F,
172 0x0F, 0xBF, 0xFF, 0xFF, 0x3F, 0x3F, 0x3F, 0xFF,
173 0, 0, 0, 0, 0, 0, 0, 0,
175 controlRegMask = (
isMSX1VDP() ? 0x07 : 0x3F);
176 memcpy(controlValueMasks,
177 isMSX1VDP() ? VALUE_MASKS_MSX1 : VALUE_MASKS_MSX2,
178 sizeof(controlValueMasks));
179 if (version == V9958) {
181 controlValueMasks[25] = 0x7F;
182 controlValueMasks[26] = 0x3F;
183 controlValueMasks[27] = 0x07;
192 if (vramSize !=
one_of(16u, 64u, 128u, 192u)) {
194 "VRAM size of ", vramSize,
"kB is not supported!");
196 vram = std::make_unique<VDPVRAM>(*
this, vramSize * 1024, time);
200 spriteChecker = std::make_unique<SpriteChecker>(*
this, renderSettings, time);
201 vram->setSpriteChecker(spriteChecker.get());
205 vram->setCmdEngine(cmdEngine.get());
215 tooFastAccess.
attach(*
this);
216 update(tooFastAccess);
221 tooFastAccess.
detach(*
this);
226 void VDP::preVideoSystemChange() noexcept
231 void VDP::postVideoSystemChange() noexcept
236 void VDP::createRenderer()
242 vram->setRenderer(renderer.get(), frameStartTime.
getTime());
247 return renderer->getPostProcessor();
250 void VDP::resetInit()
257 controlRegs[9] |= 0x02;
261 controlRegs[21] = 0x3B;
262 controlRegs[22] = 0x05;
270 cpuVramReqIsRead =
false;
272 cpuExtendedVram =
false;
273 registerDataStored =
false;
274 paletteDataStored =
false;
277 horizontalAdjust = 7;
280 isDisplayArea =
false;
281 displayEnabled =
false;
282 spriteEnabled =
true;
283 superimposing =
nullptr;
284 externalVideo =
nullptr;
288 statusReg1 = (version == V9958 ? 0x04 : 0x00);
293 irqHorizontal.
reset();
296 const word V9938_PALETTE[16] = {
297 0x000, 0x000, 0x611, 0x733, 0x117, 0x327, 0x151, 0x627,
298 0x171, 0x373, 0x661, 0x664, 0x411, 0x265, 0x555, 0x777
301 memcpy(palette, V9938_PALETTE,
sizeof(V9938_PALETTE));
304 void VDP::resetMasks(EmuTime::param time)
306 updateNameBase(time);
307 updateColorBase(time);
308 updatePatternBase(time);
309 updateSpriteAttributeBase(time);
310 updateSpritePatternBase(time);
324 syncVSync .removeSyncPoint();
325 syncDisplayStart .removeSyncPoint();
326 syncVScan .removeSyncPoint();
327 syncHScan .removeSyncPoint();
328 syncHorAdjust .removeSyncPoint();
329 syncSetMode .removeSyncPoint();
330 syncSetBlank .removeSyncPoint();
331 syncSetSprites .removeSyncPoint();
332 syncCpuVramAccess.removeSyncPoint();
333 syncCmdDone .removeSyncPoint();
334 pendingCpuAccess =
false;
337 cmdEngine->sync(time);
339 spriteChecker->reset(time);
340 cmdEngine->reset(time);
349 assert(frameCount == 0);
352 void VDP::execVSync(EmuTime::param time)
357 renderer->frameEnd(time);
358 spriteChecker->frameEnd(time);
362 std::tie(blinkState, blinkCount) =
367 cmdEngine->sync(time);
373 void VDP::execDisplayStart(EmuTime::param time)
377 if (!isDisplayArea) {
378 if (displayEnabled) {
379 vram->updateDisplayEnabled(
true, time);
381 isDisplayArea =
true;
385 void VDP::execVScan(EmuTime::param time)
394 vram->updateDisplayEnabled(
false, time);
396 isDisplayArea =
false;
400 if (controlRegs[1] & 0x20) {
405 void VDP::execHScan()
408 if (controlRegs[0] & 0x10) {
413 void VDP::execHorAdjust(EmuTime::param time)
415 int newHorAdjust = (controlRegs[18] & 0x0F) ^ 0x07;
416 if (controlRegs[25] & 0x08) {
419 renderer->updateHorizontalAdjust(newHorAdjust, time);
420 horizontalAdjust = newHorAdjust;
423 void VDP::execSetMode(EmuTime::param time)
426 DisplayMode(controlRegs[0], controlRegs[1], controlRegs[25]),
431 void VDP::execSetBlank(EmuTime::param time)
433 bool newDisplayEnabled = (controlRegs[1] & 0x40) != 0;
435 vram->updateDisplayEnabled(newDisplayEnabled, time);
437 displayEnabled = newDisplayEnabled;
440 void VDP::execSetSprites(EmuTime::param time)
442 bool newSpriteEnabled = (controlRegs[8] & 0x02) == 0;
443 vram->updateSpritesEnabled(newSpriteEnabled, time);
444 spriteEnabled = newSpriteEnabled;
447 void VDP::execCpuVramAccess(EmuTime::param time)
449 assert(!allowTooFastAccess);
450 pendingCpuAccess =
false;
451 executeCpuVramAccess(time);
454 void VDP::execSyncCmdDone(EmuTime::param time)
456 cmdEngine->sync(time);
462 void VDP::scheduleDisplayStart(EmuTime::param time)
465 if (displayStartSyncTime > time) {
466 syncDisplayStart.removeSyncPoint();
475 (palTiming ? 36 : 9) +
476 (controlRegs[9] & 0x80 ? 0 : 10) +
481 displayStartSyncTime = frameStartTime + displayStart;
485 if (displayStartSyncTime > time) {
486 syncDisplayStart.setSyncPoint(displayStartSyncTime);
495 void VDP::scheduleVScan(EmuTime::param time)
507 if (vScanSyncTime > time) {
508 syncVScan.removeSyncPoint();
513 vScanSyncTime = frameStartTime +
518 if (vScanSyncTime > time) {
519 syncVScan.setSyncPoint(vScanSyncTime);
524 void VDP::scheduleHScan(EmuTime::param time)
527 if (hScanSyncTime > time) {
528 syncHScan.removeSyncPoint();
529 hScanSyncTime = time;
533 horizontalScanOffset = displayStart - (100 + 102)
543 if (horizontalScanOffset >= ticksPerFrame) {
544 horizontalScanOffset -= ticksPerFrame;
558 int lineCountResetTicks = (8 + getVerticalAdjust()) *
TICKS_PER_LINE;
562 if (horizontalScanOffset >= lineCountResetTicks) {
569 if ((controlRegs[0] & 0x10) && horizontalScanOffset >= 0) {
577 hScanSyncTime = frameStartTime + horizontalScanOffset;
578 if (hScanSyncTime > time) {
579 syncHScan.setSyncPoint(hScanSyncTime);
590 void VDP::frameStart(EmuTime::param time)
605 palTiming = (controlRegs[9] & 0x02) != 0;
611 if (blinkCount == 0) {
612 renderer->updateBlinkState(!blinkState, time);
613 blinkState = !blinkState;
614 blinkCount = (blinkState
615 ? controlRegs[13] >> 4 : controlRegs[13] & 0x0F) * 10;
624 const RawFrame* newSuperimposing = (controlRegs[0] & 1) ? externalVideo :
nullptr;
625 if (superimposing != newSuperimposing) {
626 superimposing = newSuperimposing;
627 renderer->updateSuperimposing(superimposing, time);
631 frameStartTime.
reset(time);
634 scheduleDisplayStart(time);
638 renderer->frameStart(time);
639 spriteChecker->frameStart(time);
655 EmuTime time = time_;
661 if (fixedVDPIOdelayCycles > 0) {
666 switch (port & (
isMSX1VDP() ? 0x01 : 0x03)) {
668 vramWrite(value, time);
669 registerDataStored =
false;
672 if (registerDataStored) {
677 value & controlRegMask,
692 vramPointer = (value << 8 | (vramPointer & 0xFF)) & 0x3FFF;
696 vramPointer = (value << 8 | dataLatch) & 0x3FFF;
697 if (!(value & 0x40)) {
699 (void)vramRead(time);
702 registerDataStored =
false;
709 vramPointer = (vramPointer & 0x3F00) | value;
712 registerDataStored =
true;
716 if (paletteDataStored) {
717 int index = controlRegs[16];
718 int grb = ((value << 8) | dataLatch) & 0x777;
719 setPalette(index, grb, time);
720 controlRegs[16] = (index + 1) & 0x0F;
721 paletteDataStored =
false;
724 paletteDataStored =
true;
731 byte regNr = controlRegs[17];
732 changeRegister(regNr & 0x3F, value, time);
733 if ((regNr & 0x80) == 0) {
735 controlRegs[17] = (regNr + 1) & 0x3F;
742 void VDP::setPalette(
int index,
word grb, EmuTime::param time)
744 if (palette[index] != grb) {
745 renderer->updatePalette(index, grb, time);
746 palette[index] = grb;
750 void VDP::vramWrite(
byte value, EmuTime::param time)
752 scheduleCpuVramAccess(
false, value, time);
755 byte VDP::vramRead(EmuTime::param time)
760 byte result = cpuVramData;
763 scheduleCpuVramAccess(
true, dummy, time);
767 void VDP::scheduleCpuVramAccess(
bool isRead,
byte write, EmuTime::param time)
771 if (!isRead) cpuVramData = write;
772 cpuVramReqIsRead = isRead;
776 assert(!allowTooFastAccess);
792 assert(!pendingCpuAccess);
793 executeCpuVramAccess(time);
821 pendingCpuAccess =
true;
829 void VDP::executeCpuVramAccess(EmuTime::param time)
831 int addr = (controlRegs[14] << 14) | vramPointer;
836 addr = ((addr << 16) | (addr >> 1)) & 0x1FFFF;
839 bool doAccess = [&] {
840 if (
likely(!cpuExtendedVram)) {
842 }
else if (
likely(vram->getSize() == 192 * 1024)) {
843 addr = 0x20000 | (addr & 0xFFFF);
850 if (cpuVramReqIsRead) {
851 cpuVramData = vram->cpuRead(addr, time);
853 vram->cpuWrite(addr, cpuVramData, time);
856 if (cpuVramReqIsRead) {
863 vramPointer = (vramPointer + 1) & 0x3FFF;
864 if (vramPointer == 0 && displayMode.
isV9938Mode()) {
866 controlRegs[14] = (controlRegs[14] + 1) & 0x07;
877 EmuTime::param time, EmuTime::param limit)
const
883 byte VDP::peekStatusReg(
byte reg, EmuTime::param time)
const
887 spriteChecker->sync(time);
890 if (controlRegs[0] & 0x10) {
891 return statusReg1 | (irqHorizontal.
getState() ? 1:0);
898 if (afterMatch < 0) {
903 int matchLength = (displayMode.
isTextMode() ? 87 : 59)
906 (0 <= afterMatch && afterMatch < matchLength);
916 || ticksThisFrame >= displayEnd;
918 | (getHR(ticksThisFrame) ? 0x20 : 0x00)
920 | cmdEngine->getStatus(time);
923 return byte(spriteChecker->getCollisionX(time));
925 return byte(spriteChecker->getCollisionX(time) >> 8) | 0xFE;
927 return byte(spriteChecker->getCollisionY(time));
929 return byte(spriteChecker->getCollisionY(time) >> 8) | 0xFC;
931 return cmdEngine->readColor(time);
933 return byte(cmdEngine->getBorderX(time));
935 return byte(cmdEngine->getBorderX(time) >> 8) | 0xFE;
941 byte VDP::readStatusReg(
byte reg, EmuTime::param time)
943 byte ret = peekStatusReg(reg, time);
946 spriteChecker->resetStatus();
951 if (controlRegs[0] & 0x10) {
952 irqHorizontal.
reset();
956 spriteChecker->resetCollision();
959 cmdEngine->resetColor();
969 registerDataStored =
false;
971 switch (port & (
isMSX1VDP() ? 0x01 : 0x03)) {
973 return vramRead(time);
976 return readStatusReg(controlRegs[15], time);
991 void VDP::changeRegister(
byte reg,
byte val, EmuTime::param time)
997 cpuExtendedVram = (val & 0x40) != 0;
1001 cmdEngine->setCmdReg(reg - 32, val, time);
1007 val &= controlValueMasks[reg];
1009 byte change = val ^ controlRegs[reg];
1015 if (blinkState == ((val & 0xF0) == 0)) {
1016 renderer->updateBlinkState(!blinkState, time);
1017 blinkState = !blinkState;
1020 if ((val & 0xF0) && (val & 0x0F)) {
1022 blinkCount = (val >> 4) * 10;
1033 if (!change)
return;
1039 syncAtNextLine(syncSetMode, time);
1043 if (change & 0x03) {
1045 spriteChecker->updateSpriteSizeMag(val, time);
1049 syncAtNextLine(syncSetMode, time);
1051 if (change & 0x40) {
1052 syncAtNextLine(syncSetBlank, time);
1056 int base = (val << 10) | ~(~0u << 10);
1069 renderer->updateNameBase(base, time);
1074 if (change & 0xF0) {
1075 renderer->updateForegroundColor(val >> 4, time);
1077 if (change & 0x0F) {
1078 renderer->updateBackgroundColor(val & 0x0F, time);
1081 renderer->updateBackgroundColor(val, time);
1085 if (change & 0x20) {
1086 renderer->updateTransparency((val & 0x20) == 0, time);
1087 spriteChecker->updateTransparency((val & 0x20) == 0, time);
1089 if (change & 0x02) {
1090 syncAtNextLine(syncSetSprites, time);
1092 if (change & 0x08) {
1093 vram->updateVRMode((val & 0x08) != 0, time);
1097 if (change & 0xF0) {
1098 renderer->updateBlinkForegroundColor(val >> 4, time);
1100 if (change & 0x0F) {
1101 renderer->updateBlinkBackgroundColor(val & 0x0F, time);
1106 paletteDataStored =
false;
1109 if (change & 0x0F) {
1110 syncAtNextLine(syncHorAdjust, time);
1114 spriteChecker->updateVerticalScroll(val, time);
1115 renderer->updateVerticalScroll(val, time);
1123 if (change & 0x08) {
1124 syncAtNextLine(syncHorAdjust, time);
1126 if (change & 0x02) {
1127 renderer->updateBorderMask((val & 0x02) != 0, time);
1129 if (change & 0x01) {
1130 renderer->updateMultiPage((val & 0x01) != 0, time);
1134 renderer->updateHorizontalScrollHigh(val, time);
1137 renderer->updateHorizontalScrollLow(val, time);
1142 controlRegs[reg] = val;
1149 if (change & 0x10) {
1151 scheduleHScan(time);
1153 irqHorizontal.
reset();
1158 if (change & 0x20) {
1163 if (statusReg0 & 0x80) {
1167 irqVertical.
reset();
1173 vram->change4k8kMapping((val & 0x80) != 0);
1177 updateNameBase(time);
1181 updateColorBase(time);
1185 updatePatternBase(time);
1189 updateSpriteAttributeBase(time);
1192 updateSpritePatternBase(time);
1195 if ((val & 1) && ! warningPrinted) {
1196 warningPrinted =
true;
1198 "The running MSX software has set bit 0 of VDP register 9 "
1199 "(dot clock direction) to one. In an ordinary MSX, "
1200 "the screen would go black and the CPU would stop running.");
1203 if (change & 0x80) {
1213 if (time < displayStartSyncTime) {
1215 scheduleDisplayStart(time);
1218 scheduleVScan(time);
1224 scheduleHScan(time);
1227 if (change & 0x01) {
1228 updateNameBase(time);
1234 void VDP::syncAtNextLine(SyncBase& type, EmuTime::param time)
1238 int offset = 144 + (horizontalAdjust - 7) * 4;
1241 EmuTime nextTime = frameStartTime + ticks;
1242 type.setSyncPoint(nextTime);
1245 void VDP::updateNameBase(EmuTime::param time)
1247 int base = (controlRegs[2] << 10) | ~(~0u << 10);
1263 : ~0u << (displayMode.
isTextMode() ? 12 : 10);
1264 if (controlRegs[25] & 0x01) {
1268 indexMask &= ~0x8000;
1270 vram->nameTable.setMask(base, indexMask, time);
1273 void VDP::updateColorBase(EmuTime::param time)
1275 int base = (controlRegs[10] << 14) | (controlRegs[3] << 6) | ~(~0u << 6);
1276 renderer->updateColorBase(base, time);
1277 switch (displayMode.
getBase()) {
1280 vram->colorTable.setMask(base, ~0u << 9, time);
1283 vram->colorTable.setMask(base, ~0u << 6, time);
1286 vram->colorTable.setMask(base | (
vdpLacksMirroring() ? 0x1800 : 0), ~0u << 13, time);
1289 vram->colorTable.setMask(base, ~0u << 13, time);
1293 vram->colorTable.disable(time);
1297 void VDP::updatePatternBase(EmuTime::param time)
1299 int base = (controlRegs[4] << 11) | ~(~0u << 11);
1300 renderer->updatePatternBase(base, time);
1301 switch (displayMode.
getBase()) {
1308 vram->patternTable.setMask(base, ~0u << 11, time);
1316 base = (controlRegs[4] << 11)
1317 | ((controlRegs[3] & 0x1f) << 6)
1320 vram->patternTable.setMask(base | (
vdpLacksMirroring() ? 0x1800 : 0), ~0u << 13, time);
1323 vram->patternTable.setMask(base, ~0u << 13, time);
1327 vram->patternTable.disable(time);
1331 void VDP::updateSpriteAttributeBase(EmuTime::param time)
1335 vram->spriteAttribTable.disable(time);
1338 int baseMask = (controlRegs[11] << 15) | (controlRegs[5] << 7) | ~(~0u << 7);
1339 int indexMask = mode == 1 ? ~0u << 7 : ~0u << 10;
1341 baseMask = ((baseMask << 16) | (baseMask >> 1)) & 0x1FFFF;
1342 indexMask = ((unsigned(indexMask) << 16) | ~(1 << 16)) & (indexMask >> 1);
1344 vram->spriteAttribTable.setMask(baseMask, indexMask, time);
1347 void VDP::updateSpritePatternBase(EmuTime::param time)
1350 vram->spritePatternTable.disable(time);
1353 int baseMask = (controlRegs[6] << 11) | ~(~0u << 11);
1354 int indexMask = ~0u << 11;
1356 baseMask = ((baseMask << 16) | (baseMask >> 1)) & 0x1FFFF;
1357 indexMask = ((unsigned(indexMask) << 16) | ~(1 << 16)) & (indexMask >> 1);
1359 vram->spritePatternTable.setMask(baseMask, indexMask, time);
1362 void VDP::updateDisplayMode(DisplayMode newMode,
bool cmdBit, EmuTime::param time)
1365 vram->updateDisplayMode(newMode, cmdBit, time);
1372 newMode.isPlanar() != displayMode.
isPlanar();
1375 bool spriteModeChange =
1376 newMode.getSpriteMode(msx1) != displayMode.
getSpriteMode(msx1);
1379 displayMode = newMode;
1385 updateColorBase(time);
1386 updatePatternBase(time);
1388 if (planarChange || spriteModeChange) {
1389 updateSpritePatternBase(time);
1390 updateSpriteAttributeBase(time);
1392 updateNameBase(time);
1401 void VDP::update(
const Setting& setting) noexcept
1403 assert(&setting ==
one_of(&cmdTiming, &tooFastAccess));
1405 brokenCmdTiming = cmdTiming .getEnum();
1406 allowTooFastAccess = tooFastAccess.getEnum();
1408 if (
unlikely(allowTooFastAccess && pendingCpuAccess)) {
1410 syncCpuVramAccess.removeSyncPoint();
1411 pendingCpuAccess =
false;
1412 executeCpuVramAccess(getCurrentTime());
1509 { 0.00f, 0.47f, 0.47f },
1510 { 0.00f, 0.47f, 0.47f },
1511 { 0.53f, 0.07f, 0.20f },
1512 { 0.67f, 0.17f, 0.27f },
1513 { 0.40f, 0.40f, 1.00f },
1514 { 0.53f, 0.43f, 0.93f },
1515 { 0.47f, 0.83f, 0.30f },
1516 { 0.73f, 0.00f, 0.70f },
1517 { 0.53f, 0.93f, 0.27f },
1518 { 0.67f, 0.93f, 0.27f },
1519 { 0.73f, 0.57f, 0.07f },
1520 { 0.80f, 0.57f, 0.17f },
1521 { 0.47f, 0.13f, 0.23f },
1522 { 0.53f, 0.73f, 0.67f },
1523 { 0.80f, 0.47f, 0.47f },
1524 { 1.00f, 0.47f, 0.47f },
1533 if ((version & VM_TOSHIBA_PALETTE) != 0) {
1536 if ((version & VM_YM2220_PALETTE) != 0) {
1539 std::array<std::array<uint8_t, 3>, 16> tmsPalette;
1540 for (
auto color :
xrange(16)) {
1546 Pr *= (saturationPr / 100.0f);
1547 Pb *= (saturationPb / 100.0f);
1554 float R = Y + 0 + 1.402f * Pr;
1555 float G = Y - 0.344f * Pb - 0.714f * Pr;
1556 float B = Y + 1.722f * Pb + 0;
1577 VDP::RegDebug::RegDebug(
VDP& vdp_)
1579 vdp_.
getName() +
" regs",
"VDP registers.", 0x40)
1583 byte VDP::RegDebug::read(
unsigned address)
1585 auto& vdp =
OUTER(
VDP, vdpRegDebug);
1586 if (address < 0x20) {
1587 return vdp.controlRegs[address];
1588 }
else if (address < 0x2F) {
1589 return vdp.cmdEngine->peekCmdReg(address - 0x20);
1595 void VDP::RegDebug::write(
unsigned address,
byte value, EmuTime::param time)
1597 auto& vdp =
OUTER(
VDP, vdpRegDebug);
1602 if ((address >= 8) && vdp.isMSX1VDP())
return;
1603 vdp.changeRegister(address, value, time);
1609 VDP::StatusRegDebug::StatusRegDebug(
VDP& vdp_)
1610 : SimpleDebuggable(vdp_.getMotherBoard(),
1611 vdp_.
getName() +
" status regs",
"VDP status registers.", 0x10)
1615 byte VDP::StatusRegDebug::read(
unsigned address, EmuTime::param time)
1617 auto& vdp =
OUTER(
VDP, vdpStatusRegDebug);
1618 return vdp.peekStatusReg(address, time);
1624 VDP::PaletteDebug::PaletteDebug(
VDP& vdp_)
1625 : SimpleDebuggable(vdp_.getMotherBoard(),
1626 vdp_.
getName() +
" palette",
"V99x8 palette (RBG format)", 0x20)
1630 byte VDP::PaletteDebug::read(
unsigned address)
1632 auto& vdp =
OUTER(
VDP, vdpPaletteDebug);
1633 word grb = vdp.getPalette(address / 2);
1634 return (address & 1) ? (grb >> 8) : (grb & 0xff);
1637 void VDP::PaletteDebug::write(
unsigned address,
byte value, EmuTime::param time)
1639 auto& vdp =
OUTER(
VDP, vdpPaletteDebug);
1643 if (vdp.isMSX1VDP())
return;
1645 int index = address / 2;
1646 word grb = vdp.getPalette(index);
1648 ? (grb & 0x0077) | ((value & 0x07) << 8)
1649 : (grb & 0x0700) | (value & 0x77);
1650 vdp.setPalette(index, grb, time);
1656 VDP::VRAMPointerDebug::VRAMPointerDebug(
VDP& vdp_)
1657 : SimpleDebuggable(vdp_.getMotherBoard(), vdp_.
getName() ==
"VDP" ?
1658 "VRAM pointer" : vdp_.
getName() +
" VRAM pointer",
1659 "VDP VRAM pointer (14 lower bits)", 2)
1663 byte VDP::VRAMPointerDebug::read(
unsigned address)
1665 auto& vdp =
OUTER(
VDP, vramPointerDebug);
1667 return vdp.vramPointer >> 8;
1669 return vdp.vramPointer & 0xFF;
1673 void VDP::VRAMPointerDebug::write(
unsigned address,
byte value, EmuTime::param )
1675 auto& vdp =
OUTER(
VDP, vramPointerDebug);
1676 int& ptr = vdp.vramPointer;
1678 ptr = (ptr & 0x00FF) | ((value & 0x3F) << 8);
1680 ptr = (ptr & 0xFF00) | value;
1688 : InfoTopic(vdp_.getMotherBoard().getMachineInfoCommand(),
1691 , helpText(std::move(helpText_))
1697 result = calc(vdp.getCurrentTime());
1700 string VDP::Info::help(
const vector<string>& )
const
1708 VDP::FrameCountInfo::FrameCountInfo(
VDP& vdp_)
1709 :
Info(vdp_,
"frame_count",
1710 "The current frame number, starts counting at 0 "
1711 "when MSX is powered up or reset.")
1715 int VDP::FrameCountInfo::calc(
const EmuTime& )
const
1717 return vdp.frameCount;
1723 VDP::CycleInFrameInfo::CycleInFrameInfo(
VDP& vdp_)
1724 :
Info(vdp_,
"cycle_in_frame",
1725 "The number of VDP cycles since the beginning of "
1726 "the current frame. The VDP runs at 6 times the Z80 "
1727 "clock frequency, so at approximately 21.5MHz.")
1731 int VDP::CycleInFrameInfo::calc(
const EmuTime& time)
const
1733 return vdp.getTicksThisFrame(time);
1739 VDP::LineInFrameInfo::LineInFrameInfo(
VDP& vdp_)
1740 :
Info(vdp_,
"line_in_frame",
1741 "The absolute line number since the beginning of "
1742 "the current frame. Goes from 0 till 262 (NTSC) or "
1743 "313 (PAL). Note that this number includes the "
1744 "border lines, use 'msx_y_pos' to get MSX "
1749 int VDP::LineInFrameInfo::calc(
const EmuTime& time)
const
1757 VDP::CycleInLineInfo::CycleInLineInfo(
VDP& vdp_)
1758 :
Info(vdp_,
"cycle_in_line",
1759 "The number of VDP cycles since the beginning of "
1760 "the current line. See also 'cycle_in_frame'."
1761 "Note that this includes the cycles in the border, "
1762 "use 'msx_x256_pos' or 'msx_x512_pos' to get MSX "
1767 int VDP::CycleInLineInfo::calc(
const EmuTime& time)
const
1775 VDP::MsxYPosInfo::MsxYPosInfo(
VDP& vdp_)
1776 :
Info(vdp_,
"msx_y_pos",
1777 "Similar to 'line_in_frame', but expressed in MSX "
1778 "coordinates. So lines in the top border have "
1779 "negative coordinates, lines in the bottom border "
1780 "have coordinates bigger or equal to 192 or 212.")
1784 int VDP::MsxYPosInfo::calc(
const EmuTime& time)
const
1793 VDP::MsxX256PosInfo::MsxX256PosInfo(
VDP& vdp_)
1794 :
Info(vdp_,
"msx_x256_pos",
1795 "Similar to 'cycle_in_frame', but expressed in MSX "
1796 "coordinates. So a position in the left border has "
1797 "a negative coordinate and a position in the right "
1798 "border has a coordinated bigger or equal to 256. "
1799 "See also 'msx_x512_pos'.")
1803 int VDP::MsxX256PosInfo::calc(
const EmuTime& time)
const
1806 vdp.getLeftSprites()) / 4;
1812 VDP::MsxX512PosInfo::MsxX512PosInfo(
VDP& vdp_)
1813 :
Info(vdp_,
"msx_x512_pos",
1814 "Similar to 'cycle_in_frame', but expressed in "
1815 "'narrow' (screen 7) MSX coordinates. So a position "
1816 "in the left border has a negative coordinate and "
1817 "a position in the right border has a coordinated "
1818 "bigger or equal to 512. See also 'msx_x256_pos'.")
1822 int VDP::MsxX512PosInfo::calc(
const EmuTime& time)
const
1825 vdp.getLeftSprites()) / 2;
1838 template<
typename Archive>
1841 ar.template serializeBase<MSXDevice>(*
this);
1843 if (ar.versionAtLeast(serVersion, 8)) {
1844 ar.serialize(
"syncVSync", syncVSync,
1845 "syncDisplayStart", syncDisplayStart,
1846 "syncVScan", syncVScan,
1847 "syncHScan", syncHScan,
1848 "syncHorAdjust", syncHorAdjust,
1849 "syncSetMode", syncSetMode,
1850 "syncSetBlank", syncSetBlank,
1851 "syncCpuVramAccess", syncCpuVramAccess);
1855 {&syncVSync, &syncDisplayStart, &syncVScan,
1856 &syncHScan, &syncHorAdjust, &syncSetMode,
1857 &syncSetBlank, &syncCpuVramAccess});
1867 ar.serialize(
"irqVertical", irqVertical,
1868 "irqHorizontal", irqHorizontal,
1869 "frameStartTime", frameStartTime,
1870 "displayStartSyncTime", displayStartSyncTime,
1871 "vScanSyncTime", vScanSyncTime,
1872 "hScanSyncTime", hScanSyncTime,
1873 "displayStart", displayStart,
1874 "horizontalScanOffset", horizontalScanOffset,
1875 "horizontalAdjust", horizontalAdjust,
1876 "registers", controlRegs,
1877 "blinkCount", blinkCount,
1878 "vramPointer", vramPointer,
1880 "isDisplayArea", isDisplayArea,
1881 "palTiming", palTiming,
1882 "interlaced", interlaced,
1883 "statusReg0", statusReg0,
1884 "statusReg1", statusReg1,
1885 "statusReg2", statusReg2,
1886 "blinkState", blinkState,
1887 "dataLatch", dataLatch,
1888 "registerDataStored", registerDataStored,
1889 "paletteDataStored", paletteDataStored);
1890 if (ar.versionAtLeast(serVersion, 5)) {
1891 ar.serialize(
"cpuVramData", cpuVramData,
1892 "cpuVramReqIsRead", cpuVramReqIsRead);
1894 ar.serialize(
"readAhead", cpuVramData);
1896 ar.serialize(
"cpuExtendedVram", cpuExtendedVram,
1897 "displayEnabled", displayEnabled);
1898 byte mode = displayMode.
getByte();
1899 ar.serialize(
"displayMode", mode);
1902 ar.serialize(
"cmdEngine", *cmdEngine,
1903 "spriteChecker", *spriteChecker,
1905 if (ar.isLoader()) {
1906 pendingCpuAccess = syncCpuVramAccess.pendingSyncPoint();
1907 update(tooFastAccess);
1910 if (ar.versionAtLeast(serVersion, 2)) {
1911 ar.serialize(
"frameCount", frameCount);
1913 assert(ar.isLoader());
1920 if (ar.versionAtLeast(serVersion, 9)) {
1921 ar.serialize(
"syncSetSprites", syncSetSprites);
1922 ar.serialize(
"spriteEnabled", spriteEnabled);
1924 assert(ar.isLoader());
1925 spriteEnabled = (controlRegs[8] & 0x02) == 0;
1936 if (ar.isLoader()) {
void printWarning(std::string_view message)
constexpr void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
constexpr EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
const XMLElement * findChild(std::string_view name) const
const std::string & getChildData(std::string_view name) const
int getChildDataAsInt(std::string_view name, int defaultValue=0) const
constexpr byte getBase() const
Get the base dispay mode as an integer: M5..M1 combined.
constexpr void setByte(byte mode_)
Used for de-serialization.
constexpr bool isPlanar() const
Is VRAM "planar" in the current display mode? Graphic 6 and 7 spread their bytes over two VRAM ICs,...
constexpr bool isBitmapMode() const
Is the current mode a bitmap mode? Graphic4 and higher are bitmap modes.
static constexpr byte REG25_MASK
Bits of VDP register 25 that encode part of the display mode.
constexpr bool isV9938Mode() const
Was this mode introduced by the V9938?
constexpr void reset()
Bring the display mode to its initial state.
static constexpr byte REG1_MASK
Bits of VDP register 1 that encode part of the display mode.
constexpr bool isTextMode() const
Is the current mode a text mode? Text1 and Text2 are text modes.
constexpr byte getByte() const
Get the dispay mode as a byte: YAE YJK M5..M1 combined.
constexpr int getSpriteMode(bool isMSX1) const
Get the sprite mode of this display mode.
static constexpr byte REG0_MASK
Bits of VDP register 0 that encode part of the display mode.
void detach(VideoSystemChangeListener &listener)
void attach(VideoSystemChangeListener &listener)
RenderSettings & getRenderSettings()
void set()
Set the interrupt request on the bus.
void reset()
Reset the interrupt request on the bus.
bool getState() const
Get the interrupt state.
EmuTime waitCyclesZ80(EmuTime::param time, unsigned cycles)
An MSXDevice is an emulated hardware component connected to the bus of the emulated MSX.
CliComm & getCliComm() const
Reactor & getReactor() const
const XMLElement & getDeviceConfig() const
Get the configuration section for this device.
CommandController & getCommandController() const
EmuTime::param getCurrentTime() const
Abstract base class for post processors.
static void restoreOld(Archive &ar, std::vector< Schedulable * > schedulables)
void detach(Observer< T > &observer)
void attach(Observer< T > &observer)
VDP-VRAM access slot calculator, meant to be used in the inner loops of the VDPCmdEngine commands.
Unified implementation of MSX Video Display Processors (VDPs).
int getLinesPerFrame() const
Gets the number of lines per frame.
void writeIO(word port, byte value, EmuTime::param time) override
Write a byte to a given IO port at a certain time to this device.
bool isFastBlinkEnabled() const
Get 'fast-blink' status.
std::array< std::array< uint8_t, 3 >, 16 > getMSX1Palette() const
Get the (fixed) palette for this MSX1 VDP.
PostProcessor * getPostProcessor() const
Used by Video9000 to be able to couple the VDP and V9990 output.
bool isVDPwithPALonly() const
Is this a VDP only capable of PAL?
bool getCmdBit() const
Are commands possible in non Graphic modes? (V9958 only)
VDPAccessSlots::Calculator getAccessSlotCalculator(EmuTime::param time, EmuTime::param limit) const
Same as getAccessSlot(), but it can be much faster for repeated calls, e.g.
void reset(EmuTime::param time) override
This method is called on reset.
std::pair< bool, int > calculateLineBlinkState(unsigned line) const
Calculates what 'blinkState' and 'blinkCount' would be at a specific line.
static constexpr int TICKS_PER_LINE
Number of VDP clock ticks per line.
int getTicksPerFrame() const
Gets the number of VDP clockticks (21MHz) per frame.
bool isInsideFrame(EmuTime::param time) const
Is the given timestamp inside the current frame? Mainly useful for debugging, because relevant timest...
void serialize(Archive &ar, unsigned version)
int getRightBorder() const
Gets the number of VDP clockticks between start of line and the start of the right border.
DisplayMode getDisplayMode() const
Get the display mode the VDP is in.
bool isMSX1VDP() const
Is this an MSX1 VDP?
bool isVDPwithVRAMremapping() const
Does this VDP have VRAM remapping when switching from 4k to 8/16k mode?
EmuTime::param getFrameStartTime() const
byte peekIO(word port, EmuTime::param time) const override
Read a byte from a given IO port.
EmuTime getAccessSlot(EmuTime::param time, VDPAccessSlots::Delta delta) const
Get the earliest access slot that is at least 'delta' cycles in the future.
bool vdpHasPatColMirroring() const
Is this a VDP that has pattern/colortable mirroring?
byte readIO(word port, EmuTime::param time) override
Read a byte from an IO port at a certain time from this device.
bool isDisplayEnabled() const
Is the display enabled? Both the regular border and forced blanking by clearing the display enable bi...
int getTicksThisFrame(EmuTime::param time) const
Gets the number of VDP clock ticks (21MHz) elapsed between a given time and the start of this frame.
bool vdpLacksMirroring() const
Is this a VDP that lacks mirroring?
VDP(const DeviceConfig &config)
void powerUp(EmuTime::param time) override
This method is called when MSX is powered up.
uint8_t clipIntToByte(int x)
Clip x to range [0,255].
string getName(KeyCode keyCode)
Translate key code to key name.
unique_ptr< Renderer > createRenderer(VDP &vdp, Display &display)
Create the Renderer selected by the current renderer setting.
EmuTime getAccessSlot(EmuTime::param frame_, EmuTime::param time, Delta delta, const VDP &vdp)
Return the time of the next available access slot that is at least 'delta' cycles later than 'time'.
Calculator getCalculator(EmuTime::param frame, EmuTime::param time, EmuTime::param limit, const VDP &vdp)
When many calls to getAccessSlot() are needed, it's more efficient to instead use this function.
This file implemented 3 utility functions:
uint8_t byte
8 bit unsigned integer
constexpr std::array< std::array< uint8_t, 3 >, 16 > TOSHIBA_PALETTE
constexpr std::array< std::array< uint8_t, 3 >, 16 > THREE_BIT_RGB_PALETTE
REGISTER_MSXDEVICE(ChakkariCopy, "ChakkariCopy")
AmdFlash::SectorInfo Info
uint16_t word
16 bit unsigned integer
constexpr std::array< std::array< uint8_t, 3 >, 16 > YM2220_PALETTE
constexpr float TMS9XXXA_ANALOG_OUTPUT[16][3]
void fill(ForwardRange &&range, const T &value)
#define OUTER(type, member)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
std::string strCat(Ts &&...ts)
constexpr auto xrange(T e)