38 static const byte ALLOW_READ = 1;
39 static const byte ALLOW_WRITE = 2;
40 static const byte NO_ACCESS = 0;
41 static const byte RD_ONLY = ALLOW_READ;
42 static const byte WR_ONLY = ALLOW_WRITE;
43 static const byte RD_WR = ALLOW_READ | ALLOW_WRITE;
44 static const byte regAccess[64] = {
45 WR_ONLY, WR_ONLY, WR_ONLY,
46 WR_ONLY, WR_ONLY, WR_ONLY,
49 RD_WR, RD_WR, RD_WR, RD_WR,
54 RD_WR, RD_WR, RD_WR, RD_WR,
55 RD_WR, RD_WR, RD_WR, RD_WR,
60 NO_ACCESS, NO_ACCESS, NO_ACCESS,
61 WR_ONLY, WR_ONLY, WR_ONLY, WR_ONLY,
62 WR_ONLY, WR_ONLY, WR_ONLY, WR_ONLY,
63 WR_ONLY, WR_ONLY, WR_ONLY, WR_ONLY,
64 WR_ONLY, WR_ONLY, WR_ONLY, WR_ONLY,
65 WR_ONLY, WR_ONLY, WR_ONLY, WR_ONLY,
66 WR_ONLY, RD_ONLY, RD_ONLY,
67 NO_ACCESS, NO_ACCESS, NO_ACCESS,
68 NO_ACCESS, NO_ACCESS, NO_ACCESS,
69 NO_ACCESS, NO_ACCESS, NO_ACCESS
81 , irq(getMotherBoard(),
getName() +
".IRQ")
82 , display(getReactor().getDisplay())
86 , externalVideoSource(false)
89 memset(regs, 0,
sizeof(regs));
93 for (
int i = 0; i < 64; ++i) {
94 palette[4 * i + 0] = 0x9F;
95 palette[4 * i + 1] = 0x1F;
96 palette[4 * i + 2] = 0x1F;
97 palette[4 * i + 3] = 0x00;
102 vram = make_unique<V9990VRAM>(*
this, time);
105 cmdEngine = make_unique<V9990CmdEngine>(
107 vram->setCmdEngine(*cmdEngine);
115 isDisplayArea =
false;
116 displayEnabled =
false;
117 superimposing =
false;
118 createRenderer(time);
131 return renderer->getPostProcessor();
153 memset(regs, 0,
sizeof(regs));
162 isDisplayArea =
false;
163 displayEnabled =
false;
164 superimposing =
false;
167 writeIO(INTERRUPT_FLAG, 0xFF, time);
171 cmdEngine->sync(time);
172 renderer->reset(time);
173 cmdEngine->reset(time);
187 result = cmdEngine->getCmdData(time);
199 case REGISTER_SELECT:
202 result =
peekIO(port, time);
205 if (systemReset)
return result;
210 if (!(regs[VRAM_READ_ADDRESS_2] & 0x80)) {
211 vramReadPtr = getVRAMAddr(VRAM_READ_ADDRESS_0) + 1;
212 setVRAMAddr(VRAM_READ_ADDRESS_0, vramReadPtr);
214 vramReadBuffer = vram->readVRAMCPU(vramReadPtr, time);
219 if (!(regs[PALETTE_CONTROL] & 0x10)) {
220 byte& palPtr = regs[PALETTE_POINTER];
221 switch (palPtr & 3) {
222 case 0: palPtr += 1;
break;
223 case 1: palPtr += 1;
break;
224 case 2: palPtr += 2;
break;
225 default: palPtr -= 3;
break;
231 if (!(regSelect & 0x40)) {
234 regSelect = (regSelect + 1) & ~0x40;
244 switch (port & 0x0F) {
250 result = vramReadBuffer;
254 result = palette[regs[PALETTE_POINTER]];
258 result = cmdEngine->peekCmdData(time);
262 result = readRegister(regSelect & 0x3F, time);
266 result = pendingIRQs;
277 bool hr = (x < left) || (right <= x);
278 bool vr = (y < top) || (bottom <= y);
280 result = cmdEngine->getStatus(time) |
292 case REGISTER_SELECT:
316 unsigned addr = getVRAMAddr(VRAM_WRITE_ADDRESS_0);
317 vram->writeVRAMCPU(addr, val, time);
318 if (!(regs[VRAM_WRITE_ADDRESS_2] & 0x80)) {
319 setVRAMAddr(VRAM_WRITE_ADDRESS_0, addr + 1);
328 writePaletteRegister(0, 0, time);
331 byte& palPtr = regs[PALETTE_POINTER];
332 writePaletteRegister(palPtr, val, time);
333 switch (palPtr & 3) {
334 case 0: palPtr += 1;
break;
335 case 1: palPtr += 1;
break;
336 case 2: palPtr += 2;
break;
337 default: palPtr -= 3;
break;
345 cmdEngine->setCmdData(val, time);
348 case REGISTER_DATA: {
360 writeRegister(regSelect & 0x3F, val, time);
361 if (!(regSelect & 0x80)) {
362 regSelect = ( regSelect & 0xC0) |
363 ((regSelect + 1) & 0x3F);
367 case REGISTER_SELECT:
387 if (!(pendingIRQs & regs[INTERRUPT_0])) {
393 case SYSTEM_CONTROL: {
396 status = (status & 0xFB) | ((val & 1) << 2);
397 syncAtNextLine(V9990_SET_MODE, time);
399 bool newSystemReset = (val & 2) != 0;
400 if (newSystemReset != systemReset) {
401 systemReset = newSystemReset;
406 for (
int i = 0; i < 64; ++i) {
407 writeRegister(i, 0, time);
410 writeIO(INTERRUPT_FLAG, 0xFF, time);
441 renderer->frameEnd(time);
445 case V9990_DISPLAY_START:
446 if (displayEnabled) {
447 renderer->updateDisplayEnabled(
true, time);
449 isDisplayArea =
true;
454 renderer->updateDisplayEnabled(
false, time);
456 isDisplayArea =
false;
479 void V9990::preVideoSystemChange()
484 void V9990::postVideoSystemChange()
487 createRenderer(time);
488 renderer->frameStart(time);
497 v9990_.
getName() +
" regs",
"V9990 registers", 0x40)
504 return v9990.regs[address];
509 v9990.writeRegister(address, value, time);
519 "V9990 palette (format is R, G, B, 0).", 0x100)
526 return v9990.palette[address];
531 v9990.writePaletteRegister(address, value, time);
538 inline unsigned V9990::getVRAMAddr(RegisterId base)
const
540 return regs[base + 0] +
541 (regs[base + 1] << 8) +
542 ((regs[base + 2] & 0x07) << 16);
545 inline void V9990::setVRAMAddr(RegisterId base,
unsigned addr)
547 regs[base + 0] = addr & 0xFF;
548 regs[base + 1] = (addr & 0xFF00) >> 8;
549 regs[base + 2] = ((addr & 0x070000) >> 16) | (regs[base + 2] & 0x80);
556 if (systemReset)
return 255;
560 if (regAccess[reg] & ALLOW_READ) {
561 if (reg < CMD_PARAM_BORDER_X_0) {
564 word borderX = cmdEngine->getBorderX(time);
565 result = (reg == CMD_PARAM_BORDER_X_0)
566 ? (borderX & 0xFF) : (borderX >> 8);
574 void V9990::syncAtNextLine(V9990SyncType type,
EmuTime::param time)
578 EmuTime nextTime = frameStartTime + ticks;
586 static const byte regWriteMask[32] = {
587 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
588 0xFF, 0x87, 0xFF, 0x83, 0x0F, 0xFF, 0xFF, 0xFF,
589 0xFF, 0xFF, 0xDF, 0x07, 0xFF, 0xFF, 0xC1, 0x07,
590 0x3F, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
594 if (!(regAccess[reg] & ALLOW_WRITE)) {
598 if (reg >= CMD_PARAM_SRC_ADDRESS_0) {
599 cmdEngine->setCmdReg(reg, val, time);
603 val &= regWriteMask[reg];
615 syncAtNextLine(V9990_SET_MODE, time);
617 case PALETTE_CONTROL:
620 case BACK_DROP_COLOR:
621 renderer->updateBackgroundColor(val & 63, time);
623 case SCROLL_CONTROL_AY0:
624 renderer->updateScrollAYLow(time);
626 case SCROLL_CONTROL_BY0:
627 renderer->updateScrollBYLow(time);
629 case SCROLL_CONTROL_AX0:
630 case SCROLL_CONTROL_AX1:
631 renderer->updateScrollAX(time);
633 case SCROLL_CONTROL_BX0:
634 case SCROLL_CONTROL_BX1:
635 renderer->updateScrollBX(time);
647 case VRAM_WRITE_ADDRESS_2:
649 vramWritePtr = getVRAMAddr(VRAM_WRITE_ADDRESS_0);
651 case VRAM_READ_ADDRESS_2:
653 vramReadPtr = getVRAMAddr(VRAM_READ_ADDRESS_0);
655 vramReadBuffer = vram->readVRAMCPU(vramReadPtr, time);
658 if (pendingIRQs & val) {
675 case 0: val &= 0x9F;
break;
676 case 1: val &= 0x1F;
break;
677 case 2: val &= 0x1F;
break;
678 case 3: val = 0x00;
break;
682 byte index = reg / 4;
684 renderer->updatePalette(index, palette[reg + 0] & 0x1F, palette[reg + 1],
685 palette[reg + 2], ys, time);
686 if (index == regs[BACK_DROP_COLOR]) {
687 renderer->updateBackgroundColor(index, time);
693 r = palette[4 * index + 0] & 0x1F;
694 g = palette[4 * index + 1];
695 b = palette[4 * index + 2];
701 assert(!renderer.get());
703 renderer->reset(time);
709 displayEnabled = (regs[CONTROL] & 0x80) != 0;
710 palTiming = (regs[SCREEN_MODE_1] & 0x08) != 0;
711 interlaced = (regs[SCREEN_MODE_1] & 0x02) != 0;
712 scrollAYHigh = regs[SCROLL_CONTROL_AY1];
713 scrollBYHigh = regs[SCROLL_CONTROL_BY1];
717 bool newSuperimposing = (regs[CONTROL] & 0x20) && externalVideoSource;
718 if (superimposing != newSuperimposing) {
719 superimposing = newSuperimposing;
720 renderer->updateSuperimposing(superimposing, time);
723 frameStartTime.
reset(time);
733 V9990_DISPLAY_START);
740 renderer->frameStart(time);
743 void V9990::raiseIRQ(IRQType irqType)
745 pendingIRQs |= irqType;
746 if (pendingIRQs & regs[INTERRUPT_0]) {
751 void V9990::setHorizontalTiming()
755 case B1:
case B3:
case B7:
758 case B0:
case B2:
case B4:
767 void V9990::setVerticalTiming()
771 case B1:
case B3:
case B7:
776 case B0:
case B2:
case B4:
791 if (!(regs[SCREEN_MODE_0] & 0x80)) {
794 switch (regs[SCREEN_MODE_0] & 0x03) {
795 case 0x00: mode =
BP2;
break;
796 case 0x01: mode =
BP4;
break;
798 switch (pal_ctrl & 0xC0) {
799 case 0x00: mode =
BP6;
break;
800 case 0x40: mode =
BD8;
break;
801 case 0x80: mode =
BYJK;
break;
802 case 0xC0: mode =
BYUV;
break;
806 case 0x03: mode =
BD16;
break;
821 void V9990::calcDisplayMode()
824 switch (regs[SCREEN_MODE_0] & 0xC0) {
833 switch(regs[SCREEN_MODE_0] & 0x30) {
834 case 0x00: mode =
B0;
break;
835 case 0x10: mode =
B2;
break;
836 case 0x20: mode =
B4;
break;
841 switch(regs[SCREEN_MODE_0] & 0x30) {
842 case 0x00: mode =
B1;
break;
843 case 0x10: mode =
B3;
break;
844 case 0x20: mode =
B7;
break;
857 setHorizontalTiming();
863 if (hScanSyncTime > time) {
865 hScanSyncTime = time;
868 if (pendingIRQs & HOR_IRQ) {
875 if (regs[INTERRUPT_2] & 0x80) {
879 int line = regs[INTERRUPT_1] + 256 * (regs[INTERRUPT_2] & 3) +
883 int mult = (status & 0x04) ? 3 : 2;
884 offset += (regs[INTERRUPT_3] & 0x0F) * 64 * mult;
885 if (offset <= ticks) {
889 hScanSyncTime = frameStartTime +
offset;
893 static enum_string<V9990DisplayMode> displayModeInfo[] = {
895 {
"P1",
P1 }, {
"P2",
P2 },
896 {
"B0",
B0 }, {
"B1",
B1 }, {
"B2",
B2 }, {
"B3",
B3 },
897 {
"B4",
B4 }, {
"B5",
B5 }, {
"B6",
B6 }, {
"B7",
B7 }
904 template<
typename Archive>
907 ar.template serializeBase<MSXDevice>(*this);
908 ar.template serializeBase<Schedulable>(*this);
910 ar.serialize(
"vram", *vram);
911 ar.serialize(
"cmdEngine", *cmdEngine);
912 ar.serialize(
"irq", irq);
913 ar.serialize(
"frameStartTime", frameStartTime);
914 ar.serialize(
"hScanSyncTime", hScanSyncTime);
915 ar.serialize(
"displayMode", mode);
916 ar.serialize_blob(
"palette", palette,
sizeof(palette));
917 ar.serialize(
"status", status);
918 ar.serialize(
"pendingIRQs", pendingIRQs);
919 ar.serialize_blob(
"registers", regs,
sizeof(regs));
920 ar.serialize(
"regSelect", regSelect);
921 ar.serialize(
"palTiming", palTiming);
922 ar.serialize(
"interlaced", interlaced);
923 ar.serialize(
"isDisplayArea", isDisplayArea);
924 ar.serialize(
"displayEnabled", displayEnabled);
925 ar.serialize(
"scrollAYHigh", scrollAYHigh);
926 ar.serialize(
"scrollBYHigh", scrollBYHigh);
928 if (ar.versionBelow(version, 2)) {
931 ar.serialize(
"systemReset", systemReset);
934 if (ar.versionBelow(version, 3)) {
935 vramReadPtr = getVRAMAddr(VRAM_READ_ADDRESS_0);
936 vramWritePtr = getVRAMAddr(VRAM_WRITE_ADDRESS_0);
939 ar.serialize(
"vramReadPtr", vramReadPtr);
940 ar.serialize(
"vramWritePtr", vramWritePtr);
941 ar.serialize(
"vramReadBuffer", vramReadBuffer);
956 setHorizontalTiming();