90 virtual void execute(
const vector<TclObject>& ,
96 virtual string help(
const vector<string>& )
const
103 VDPInfo(
VDP& vdp_,
const string& name,
const string& helpText_)
104 :
InfoTopic(vdp_.getMotherBoard().getMachineInfoCommand(),
118 "The current frame number, starts counting at 0 "
119 "when MSX is powered up or reset.") {}
122 return vdp.frameCount;
130 :
VDPInfo(vdp,
"cycle_in_frame",
131 "The number of VDP cycles since the beginning of "
132 "the current frame. The VDP runs at 6 times the Z80 "
133 "clock frequency, so at approximately 21.5MHz.") {}
144 :
VDPInfo(vdp,
"line_in_frame",
145 "The absolute line number since the beginning of "
146 "the current frame. Goes from 0 till 262 (NTSC) or "
147 "313 (PAL). Note that this number includes the "
148 "border lines, use 'msx_y_pos' to get MSX "
160 :
VDPInfo(vdp,
"cycle_in_line",
161 "The number of VDP cycles since the beginning of "
162 "the current line. See also 'cycle_in_frame'."
163 "Note that this includes the cycles in the border, "
164 "use 'msx_x256_pos' or 'msx_x512_pos' to get MSX "
177 "Similar to 'line_in_frame', but expressed in MSX "
178 "coordinates. So lines in the top border have "
179 "negative coordinates, lines in the bottom border "
180 "have coordinates bigger or equal to 192 or 212.") {}
193 "Similar to 'cycle_in_frame', but expressed in MSX "
194 "coordinates. So a position in the left border has "
195 "a negative coordinate and a position in the right "
196 "border has a coordinated bigger or equal to 256. "
197 "See also 'msx_x512_pos'.") {}
210 "Similar to 'cycle_in_frame', but expressed in "
211 "'narrow' (screen 7) MSX coordinates. So a position "
212 "in the left border has a negative coordinate and "
213 "a position in the right border has a coordinated "
214 "bigger or equal to 512. See also 'msx_x256_pos'.") {}
227 , display(getReactor().getDisplay())
240 , irqVertical (getMotherBoard(),
getName() +
".IRQvertical")
241 , irqHorizontal(getMotherBoard(),
getName() +
".IRQhorizontal")
242 , displayStartSyncTime(
Schedulable::getCurrentTime())
245 , warningPrinted(false)
249 std::string versionString = config.
getChildData(
"version");
250 if (versionString ==
"TMS99X8A") version = TMS99X8A;
251 else if (versionString ==
"TMS9929A") version = TMS9929A;
252 else if (versionString ==
"V9938") version = V9938;
253 else if (versionString ==
"V9958") version = V9958;
254 else throw MSXException(
"Unknown VDP version \"" + versionString +
"\"");
257 static const byte VALUE_MASKS_MSX1[32] = {
258 0x03, 0xFB, 0x0F, 0xFF, 0x07, 0x7F, 0x07, 0xFF
260 static const byte VALUE_MASKS_MSX2[32] = {
261 0x7E, 0x7B, 0x7F, 0xFF, 0x3F, 0xFF, 0x3F, 0xFF,
262 0xFB, 0xBF, 0x07, 0x03, 0xFF, 0xFF, 0x07, 0x0F,
263 0x0F, 0xBF, 0xFF, 0xFF, 0x3F, 0x3F, 0x3F, 0xFF,
264 0, 0, 0, 0, 0, 0, 0, 0,
266 controlRegMask = (
isMSX1VDP() ? 0x07 : 0x3F);
267 memcpy(controlValueMasks,
268 isMSX1VDP() ? VALUE_MASKS_MSX1 : VALUE_MASKS_MSX2,
269 sizeof(controlValueMasks));
270 if (version == V9958) {
272 controlValueMasks[25] = 0x7F;
273 controlValueMasks[26] = 0x3F;
274 controlValueMasks[27] = 0x07;
283 if ((vramSize != 16) && (vramSize != 64) &&
284 (vramSize != 128) && (vramSize != 192)) {
286 "VRAM size of " << vramSize <<
"kB is not supported!");
288 vram = make_unique<VDPVRAM>(*
this, vramSize * 1024, time);
293 spriteChecker = make_unique<SpriteChecker>(*
this, renderSettings, time);
294 vram->setSpriteChecker(spriteChecker.get());
297 cmdEngine = make_unique<VDPCmdEngine>(
299 vram->setCmdEngine(cmdEngine.get());
315 void VDP::preVideoSystemChange()
320 void VDP::postVideoSystemChange()
325 void VDP::createRenderer()
331 vram->setRenderer(renderer.get(), frameStartTime.
getTime());
336 return renderer->getPostProcessor();
339 void VDP::resetInit()
343 for (
int i = 0; i < 32; i++) {
346 if (version == TMS9929A) {
348 controlRegs[9] |= 0x02;
352 controlRegs[21] = 0x3B;
353 controlRegs[22] = 0x05;
362 cpuExtendedVram =
false;
363 registerDataStored =
false;
364 paletteDataStored =
false;
367 horizontalAdjust = 7;
370 isDisplayArea =
false;
371 displayEnabled =
false;
372 superimposing =
nullptr;
373 externalVideo =
nullptr;
377 statusReg1 = (version == V9958 ? 0x04 : 0x00);
382 irqHorizontal.
reset();
385 const word V9938_PALETTE[16] = {
386 0x000, 0x000, 0x611, 0x733, 0x117, 0x327, 0x151, 0x627,
387 0x171, 0x373, 0x661, 0x664, 0x411, 0x265, 0x555, 0x777
390 memcpy(palette, V9938_PALETTE,
sizeof(V9938_PALETTE));
395 updateNameBase(time);
396 updateColorBase(time);
397 updatePatternBase(time);
398 updateSpriteAttributeBase(time);
399 updateSpritePatternBase(time);
423 cmdEngine->sync(time);
425 spriteChecker->reset(time);
426 cmdEngine->reset(time);
435 assert(frameCount == 0);
463 renderer->frameEnd(time);
464 spriteChecker->frameEnd(time);
471 if (!isDisplayArea) {
472 if (displayEnabled) {
473 vram->updateDisplayEnabled(
true, time);
475 isDisplayArea =
true;
486 vram->updateDisplayEnabled(
false, time);
488 isDisplayArea =
false;
492 if (controlRegs[1] & 0x20) {
498 if (controlRegs[0] & 0x10) {
503 int newHorAdjust = (controlRegs[18] & 0x0F) ^ 0x07;
504 if (controlRegs[25] & 0x08) {
507 renderer->updateHorizontalAdjust(newHorAdjust, time);
508 horizontalAdjust = newHorAdjust;
513 DisplayMode(controlRegs[0], controlRegs[1], controlRegs[25]),
517 bool newDisplayEnabled = (controlRegs[1] & 0x40) != 0;
519 vram->updateDisplayEnabled(newDisplayEnabled, time);
521 displayEnabled = newDisplayEnabled;
524 case CPU_VRAM_ACCESS:
525 executeCpuVramAccess(time);
538 if (displayStartSyncTime > time) {
544 int verticalAdjust = (controlRegs[18] >> 4) ^ 0x07;
549 (palTiming ? 36 : 9) +
550 (controlRegs[9] & 0x80 ? 0 : 10) +
555 displayStartSyncTime = frameStartTime + displayStart;
559 if (displayStartSyncTime > time) {
581 if (vScanSyncTime > time) {
587 vScanSyncTime = frameStartTime +
592 if (vScanSyncTime > time) {
601 if (hScanSyncTime > time) {
603 hScanSyncTime = time;
607 horizontalScanOffset = displayStart - (100 + 102)
608 + ((controlRegs[19] - controlRegs[23]) & 0xFF) * TICKS_PER_LINE
617 if (horizontalScanOffset >= ticksPerFrame) {
618 horizontalScanOffset -= ticksPerFrame;
621 if (horizontalScanOffset >= LINE_COUNT_RESET_TICKS) {
628 if ((controlRegs[0] & 0x10) && horizontalScanOffset >= 0) {
636 hScanSyncTime = frameStartTime + horizontalScanOffset;
637 if (hScanSyncTime > time) {
664 palTiming = (controlRegs[9] & 0x02) != 0;
665 interlaced = (controlRegs[9] & 0x08) != 0;
668 if (blinkCount != 0) {
670 if (blinkCount == 0) {
671 renderer->updateBlinkState(!blinkState, time);
672 blinkState = !blinkState;
673 blinkCount = ( blinkState
674 ? controlRegs[13] >> 4 : controlRegs[13] & 0x0F ) * 10;
683 const RawFrame* newSuperimposing = (controlRegs[0] & 1) ? externalVideo :
nullptr;
684 if (superimposing != newSuperimposing) {
685 superimposing = newSuperimposing;
686 renderer->updateSuperimposing(superimposing, time);
690 frameStartTime.
reset(time);
693 scheduleDisplayStart(time);
697 renderer->frameStart(time);
698 spriteChecker->frameStart(time);
715 switch (port & 0x03) {
717 vramWrite(value, time);
718 registerDataStored =
false;
721 if (registerDataStored) {
723 if (!(value & 0x40)) {
726 value & controlRegMask,
742 vramPointer = (value << 8 | (vramPointer & 0xFF)) & 0x3FFF;
746 vramPointer = (value << 8 | dataLatch) & 0x3FFF;
747 if (!(value & 0x40)) {
752 registerDataStored =
false;
759 vramPointer = (vramPointer & 0x3F00) | value;
762 registerDataStored =
true;
766 if (paletteDataStored) {
767 int index = controlRegs[16];
768 int grb = ((value << 8) | dataLatch) & 0x777;
769 setPalette(index, grb, time);
770 controlRegs[16] = (index + 1) & 0x0F;
771 paletteDataStored =
false;
774 paletteDataStored =
true;
781 byte regNr = controlRegs[17];
782 changeRegister(regNr & 0x3F, value, time);
783 if ((regNr & 0x80) == 0) {
785 controlRegs[17] = (regNr + 1) & 0x3F;
794 if (palette[index] != grb) {
795 renderer->updatePalette(index, grb, time);
796 palette[index] = grb;
805 scheduleCpuVramAccess(
false, time);
810 scheduleCpuVramAccess(
true, time);
816 cpuVramReqIsRead = isRead;
827 int addr = (controlRegs[14] << 14) | vramPointer;
832 addr = ((addr << 16) | (addr >> 1)) & 0x1FFFF;
836 if (
likely(!cpuExtendedVram)) {
838 }
else if (
likely(vram->getSize() == 192 * 1024)) {
839 addr = 0x20000 | (addr & 0xFFFF);
845 if (cpuVramReqIsRead) {
846 cpuVramData = vram->cpuRead(addr, time);
848 vram->cpuWrite(addr, cpuVramData, time);
851 if (cpuVramReqIsRead) {
858 vramPointer = (vramPointer + 1) & 0x3FFF;
859 if (vramPointer == 0 && displayMode.
isV9938Mode()) {
861 controlRegs[14] = (controlRegs[14] + 1) & 0x07;
874 static const unsigned screenOff[154 + 17] = {
875 0, 8, 16, 24, 32, 40, 48, 56, 64, 72,
876 80, 88, 96, 104, 112, 120, 164, 172, 180, 188,
877 196, 204, 212, 220, 228, 236, 244, 252, 260, 268,
878 276, 292, 300, 308, 316, 324, 332, 340, 348, 356,
879 364, 372, 380, 388, 396, 404, 420, 428, 436, 444,
880 452, 460, 468, 476, 484, 492, 500, 508, 516, 524,
881 532, 548, 556, 564, 572, 580, 588, 596, 604, 612,
882 620, 628, 636, 644, 652, 660, 676, 684, 692, 700,
883 708, 716, 724, 732, 740, 748, 756, 764, 772, 780,
884 788, 804, 812, 820, 828, 836, 844, 852, 860, 868,
885 876, 884, 892, 900, 908, 916, 932, 940, 948, 956,
886 964, 972, 980, 988, 996, 1004, 1012, 1020, 1028, 1036,
887 1044, 1060, 1068, 1076, 1084, 1092, 1100, 1108, 1116, 1124,
888 1132, 1140, 1148, 1156, 1164, 1172, 1188, 1196, 1204, 1212,
889 1220, 1228, 1268, 1276, 1284, 1292, 1300, 1308, 1316, 1324,
890 1334, 1344, 1352, 1360,
891 1368+ 0, 1368+ 8, 1368+16, 1368+ 24, 1368+ 32,
892 1368+ 40, 1368+ 48, 1368+56, 1368+ 64, 1368+ 72,
893 1368+ 80, 1368+ 88, 1368+96, 1368+104, 1368+112,
897 static const unsigned spritesOff[88 + 16] = {
898 6, 14, 22, 30, 38, 46, 54, 62, 70, 78,
899 86, 94, 102, 110, 118, 162, 170, 182, 188, 214,
900 220, 246, 252, 278, 310, 316, 342, 348, 374, 380,
901 406, 438, 444, 470, 476, 502, 508, 534, 566, 572,
902 598, 604, 630, 636, 662, 694, 700, 726, 732, 758,
903 764, 790, 822, 828, 854, 860, 886, 892, 918, 950,
904 956, 982, 988, 1014, 1020, 1046, 1078, 1084, 1110, 1116,
905 1142, 1148, 1174, 1206, 1212, 1266, 1274, 1282, 1290, 1298,
906 1306, 1314, 1322, 1332, 1342, 1350, 1358, 1366,
907 1368+ 6, 1368+14, 1368+ 22, 1368+ 30, 1368+ 38,
908 1368+ 46, 1368+54, 1368+ 62, 1368+ 70, 1368+ 78,
909 1368+ 86, 1368+94, 1368+102, 1368+110, 1368+118,
913 static const unsigned spritesOn[31 + 3] = {
914 28, 92, 162, 170, 188, 220, 252, 316, 348, 380,
915 444, 476, 508, 572, 604, 636, 700, 732, 764, 828,
916 860, 892, 956, 988, 1020, 1084, 1116, 1148, 1212, 1264,
918 1368+28, 1368+92, 1368+162,
936 assert(delta <= 136);
938 auto slots = getExtendedAccessSlots(
942 auto it = std::lower_bound(slots.begin(), slots.end(), ticks + delta);
943 assert(it != slots.end());
950 auto slots = getAccessSlots(
959 spriteChecker->sync(time);
962 if (controlRegs[0] & 0x10) {
963 return statusReg1 | (irqHorizontal.
getState() ? 1:0);
970 if (afterMatch < 0) {
975 int matchLength = (displayMode.
isTextMode() ? 87 : 59)
978 (0 <= afterMatch && afterMatch < matchLength);
987 bool vr = ticksThisFrame < displayStart - TICKS_PER_LINE
988 || ticksThisFrame >= displayEnd;
990 | (getHR(ticksThisFrame) ? 0x20 : 0x00)
992 | cmdEngine->getStatus(time);
995 return byte(spriteChecker->getCollisionX(time));
997 return byte(spriteChecker->getCollisionX(time) >> 8) | 0xFE;
999 return byte(spriteChecker->getCollisionY(time));
1001 return byte(spriteChecker->getCollisionY(time) >> 8) | 0xFC;
1003 return cmdEngine->readColor(time);
1005 return byte(cmdEngine->getBorderX(time));
1007 return byte(cmdEngine->getBorderX(time) >> 8) | 0xFE;
1015 byte ret = peekStatusReg(reg, time);
1018 spriteChecker->resetStatus();
1019 statusReg0 &= ~0x80;
1020 irqVertical.
reset();
1023 if (controlRegs[0] & 0x10) {
1024 irqHorizontal.
reset();
1028 spriteChecker->resetCollision();
1031 cmdEngine->resetColor();
1041 registerDataStored =
false;
1043 switch (port & 0x03) {
1045 return vramRead(time);
1048 return readStatusReg(controlRegs[15], time);
1069 cpuExtendedVram = (val & 0x40) != 0;
1073 cmdEngine->setCmdReg(reg - 32, val, time);
1079 val &= controlValueMasks[reg];
1081 byte change = val ^ controlRegs[reg];
1087 if (blinkState == ((val & 0xF0) == 0)) {
1088 renderer->updateBlinkState(!blinkState, time);
1089 blinkState = !blinkState;
1092 if ((val & 0xF0) && (val & 0x0F)) {
1094 blinkCount = (val >> 4) * 10;
1101 if (!change)
return;
1107 syncAtNextLine(SET_MODE, time);
1111 if (change & 0x03) {
1113 spriteChecker->updateSpriteSizeMag(val, time);
1117 syncAtNextLine(SET_MODE, time);
1119 if (change & 0x40) {
1120 syncAtNextLine(SET_BLANK, time);
1124 int base = (val << 10) | ~(-1 << 10);
1137 renderer->updateNameBase(base, time);
1142 if (change & 0xF0) {
1143 renderer->updateForegroundColor(val >> 4, time);
1145 if (change & 0x0F) {
1146 renderer->updateBackgroundColor(val & 0x0F, time);
1149 renderer->updateBackgroundColor(val, time);
1153 if (change & 0x20) {
1154 renderer->updateTransparency((val & 0x20) == 0, time);
1156 if (change & 0x02) {
1157 vram->updateSpritesEnabled((val & 0x02) == 0, time);
1159 if (change & 0x08) {
1160 vram->updateVRMode((val & 0x08) != 0, time);
1164 if (change & 0xF0) {
1165 renderer->updateBlinkForegroundColor(val >> 4, time);
1167 if (change & 0x0F) {
1168 renderer->updateBlinkBackgroundColor(val & 0x0F, time);
1173 paletteDataStored =
false;
1176 if (change & 0x0F) {
1177 syncAtNextLine(HOR_ADJUST, time);
1181 spriteChecker->updateVerticalScroll(val, time);
1182 renderer->updateVerticalScroll(val, time);
1189 if (change & 0x08) {
1190 syncAtNextLine(HOR_ADJUST, time);
1192 if (change & 0x02) {
1193 renderer->updateBorderMask((val & 0x02) != 0, time);
1195 if (change & 0x01) {
1196 renderer->updateMultiPage((val & 0x01) != 0, time);
1200 renderer->updateHorizontalScrollHigh(val, time);
1203 renderer->updateHorizontalScrollLow(val, time);
1208 controlRegs[reg] = val;
1215 if (change & 0x10) {
1217 scheduleHScan(time);
1219 irqHorizontal.
reset();
1224 if (change & 0x20) {
1229 if (statusReg0 & 0x80) {
1233 irqVertical.
reset();
1239 vram->change4k8kMapping((val & 0x80) != 0);
1243 updateNameBase(time);
1247 updateColorBase(time);
1250 updatePatternBase(time);
1254 updateSpriteAttributeBase(time);
1257 updateSpritePatternBase(time);
1260 if ((val & 1) && ! warningPrinted) {
1261 warningPrinted =
true;
1263 (
"The running MSX software has set bit 0 of VDP register 9 "
1264 "(dot clock direction) to one. In an ordinary MSX, "
1265 "the screen would go black and the CPU would stop running.");
1268 if (change & 0x80) {
1278 if (time < displayStartSyncTime) {
1280 scheduleDisplayStart(time);
1283 scheduleVScan(time);
1289 scheduleHScan(time);
1292 if (change & 0x01) {
1293 updateNameBase(time);
1302 int ticks = (line + 1) * TICKS_PER_LINE;
1303 EmuTime nextTime = frameStartTime + ticks;
1309 int base = (controlRegs[2] << 10) | ~(-1 << 10);
1325 : -1 << (displayMode.
isTextMode() ? 12 : 10);
1326 if (controlRegs[25] & 0x01) {
1330 indexMask &= ~0x8000;
1332 vram->nameTable.setMask(base, indexMask, time);
1337 int base = (controlRegs[10] << 14) | (controlRegs[3] << 6) | ~(-1 << 6);
1338 renderer->updateColorBase(base, time);
1339 switch (displayMode.
getBase()) {
1342 vram->colorTable.setMask(base, -1 << 9, time);
1345 vram->colorTable.setMask(base, -1 << 6, time);
1349 vram->colorTable.setMask(base, -1 << 13, time);
1353 vram->colorTable.disable(time);
1359 int base = (controlRegs[4] << 11) | ~(-1 << 11);
1360 renderer->updatePatternBase(base, time);
1361 switch (displayMode.
getBase()) {
1368 vram->patternTable.setMask(base, -1 << 11, time);
1372 vram->patternTable.setMask(base, -1 << 13, time);
1376 vram->patternTable.disable(time);
1384 vram->spriteAttribTable.disable(time);
1387 int baseMask = (controlRegs[11] << 15) | (controlRegs[5] << 7) | ~(-1 << 7);
1388 int indexMask = mode == 1 ? -1 << 7 : -1 << 10;
1390 baseMask = ((baseMask << 16) | (baseMask >> 1)) & 0x1FFFF;
1391 indexMask = ((indexMask << 16) | ~(1 << 16)) & (indexMask >> 1);
1393 vram->spriteAttribTable.setMask(baseMask, indexMask, time);
1399 vram->spritePatternTable.disable(time);
1402 int baseMask = (controlRegs[6] << 11) | ~(-1 << 11);
1403 int indexMask = -1 << 11;
1405 baseMask = ((baseMask << 16) | (baseMask >> 1)) & 0x1FFFF;
1406 indexMask = ((indexMask << 16) | ~(1 << 16)) & (indexMask >> 1);
1408 vram->spritePatternTable.setMask(baseMask, indexMask, time);
1411 void VDP::updateDisplayMode(DisplayMode newMode,
EmuTime::param time)
1416 vram->updateDisplayMode(newMode, time);
1423 newMode.isPlanar() != displayMode.
isPlanar();
1425 bool spriteModeChange =
1429 displayMode = newMode;
1435 updateColorBase(time);
1436 updatePatternBase(time);
1438 if (planarChange || spriteModeChange) {
1439 updateSpritePatternBase(time);
1440 updateSpriteAttributeBase(time);
1442 updateNameBase(time);
1453 externalVideo = externalSource;
1460 vdp_.
getName() +
" regs",
"VDP registers.", 0x40)
1467 if (address < 0x20) {
1468 return vdp.controlRegs[address];
1469 }
else if (address < 0x2F) {
1470 return vdp.cmdEngine->peekCmdReg(address - 0x20);
1478 vdp.changeRegister(address, value, time);
1486 vdp_.
getName() +
" status regs",
"VDP status registers.", 0x10)
1493 return vdp.peekStatusReg(address, time);
1501 vdp_.
getName() +
" palette",
"V99x8 palette (RBG format)", 0x20)
1509 return (address & 1) ? (grb >> 8) : (grb & 0xff);
1514 int index = address / 2;
1517 ? (grb & 0x0077) | ((value & 0x07) << 8)
1518 : (grb & 0x0700) | (value & 0x77);
1519 vdp.setPalette(index, grb, time);
1527 "VRAM pointer" : vdp_.
getName() +
" VRAM pointer",
1528 "VDP VRAM pointer (14 lower bits)", 2)
1536 return vdp.vramPointer >> 8;
1538 return vdp.vramPointer & 0xFF;
1544 int& ptr = vdp.vramPointer;
1546 ptr = (ptr & 0x00FF) | ((value & 0x3F) << 8);
1548 ptr = (ptr & 0xFF00) | value;
1558 template<
typename Archive>
1561 ar.template serializeBase<MSXDevice>(*this);
1562 ar.template serializeBase<Schedulable>(*this);
1571 ar.serialize(
"irqVertical", irqVertical);
1572 ar.serialize(
"irqHorizontal", irqHorizontal);
1573 ar.serialize(
"frameStartTime", frameStartTime);
1574 ar.serialize(
"displayStartSyncTime", displayStartSyncTime);
1575 ar.serialize(
"vScanSyncTime", vScanSyncTime);
1576 ar.serialize(
"hScanSyncTime", hScanSyncTime);
1577 ar.serialize(
"displayStart", displayStart);
1578 ar.serialize(
"horizontalScanOffset", horizontalScanOffset);
1579 ar.serialize(
"horizontalAdjust", horizontalAdjust);
1580 ar.serialize(
"registers", controlRegs);
1581 ar.serialize(
"blinkCount", blinkCount);
1582 ar.serialize(
"vramPointer", vramPointer);
1583 ar.serialize(
"palette", palette);
1584 ar.serialize(
"isDisplayArea", isDisplayArea);
1585 ar.serialize(
"palTiming", palTiming);
1586 ar.serialize(
"interlaced", interlaced);
1587 ar.serialize(
"statusReg0", statusReg0);
1588 ar.serialize(
"statusReg1", statusReg1);
1589 ar.serialize(
"statusReg2", statusReg2);
1590 ar.serialize(
"blinkState", blinkState);
1591 ar.serialize(
"dataLatch", dataLatch);
1592 ar.serialize(
"registerDataStored", registerDataStored);
1593 ar.serialize(
"paletteDataStored", paletteDataStored);
1594 if (ar.versionAtLeast(version, 5)) {
1595 ar.serialize(
"cpuVramData", cpuVramData);
1596 ar.serialize(
"cpuVramReqIsRead", cpuVramReqIsRead);
1598 ar.serialize(
"readAhead", cpuVramData);
1600 ar.serialize(
"cpuExtendedVram", cpuExtendedVram);
1601 ar.serialize(
"displayEnabled", displayEnabled);
1603 ar.serialize(
"displayMode", mode);
1606 ar.serialize(
"cmdEngine", *cmdEngine);
1607 ar.serialize(
"spriteChecker", *spriteChecker);
1608 ar.serialize(
"vram", *vram);
1610 if (ar.versionAtLeast(version, 2)) {
1611 ar.serialize(
"frameCount", frameCount);
1613 assert(ar.isLoader());
1628 if (ar.isLoader()) {