diff --git a/README.md b/README.md index a6f838675..4870017f5 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,9 @@ Some pre-configured machines are shown below, ready to run BASIC, DOS, Windows 1 ![IBM PC w/CGA, Zork I](/disks/pc/games/infocom/zork1/thumbnail.jpg "link:/disks/pc/games/infocom/zork1/:200:100") Check out the rest of the PCjs [Application](/apps/pc/), [Boot Disk](/disks/pc/) and [Machine](/devices/pc/machine/) -demos, including the [IBM PC XT "Server Array"](/devices/pc/machine/5160/cga/256kb/array/) and -[Windows 1.01 "Server Array"](/devices/pc/machine/5160/ega/640kb/array/) demos of multiple PCs running side-by-side. +demos, including an [IBM PC Dual Display System](/devices/pc/machine/5150/dual/64kb/) demo of multiple monitor support, +and [IBM PC XT "Server Array"](/devices/pc/machine/5160/cga/256kb/array/) and [Windows 1.01 "Server Array"](/devices/pc/machine/5160/ega/640kb/array/) +demos of multiple machines running side-by-side. C1Pjs --- diff --git a/devices/pc/machine/5150/dual/64kb/machine.xml b/devices/pc/machine/5150/dual/64kb/machine.xml new file mode 100644 index 000000000..1abe4c6f8 --- /dev/null +++ b/devices/pc/machine/5150/dual/64kb/machine.xml @@ -0,0 +1,18 @@ + + + + IBM PC (Model 5150) with Dual Display + + + + + diff --git a/devices/pc/machine/README.md b/devices/pc/machine/README.md index 677987dbc..4fb5b0c04 100644 --- a/devices/pc/machine/README.md +++ b/devices/pc/machine/README.md @@ -13,6 +13,7 @@ Here you'll find sample Machine Configurations for all the IBM PC models that PC * [IBM PC, MDA, 64K, Debugger](/devices/pc/machine/5150/mda/64kb/debugger/) * [IBM PC, CGA, 64K, DONKEY.BAS](/devices/pc/machine/5150/cga/64kb/donkey/) * [IBM PC, CGA, 64K, DONKEY.BAS, Debugger](/devices/pc/machine/5150/cga/64kb/donkey/debugger/) +* [IBM PC, Dual Display, 64K](/devices/pc/machine/5150/dual/64kb/) ### Model 5160 Machine Configurations diff --git a/devices/pc/video/ibm/cga/ibm-cga-dual.xml b/devices/pc/video/ibm/cga/ibm-cga-dual.xml new file mode 100644 index 000000000..3db4e8fb5 --- /dev/null +++ b/devices/pc/video/ibm/cga/ibm-cga-dual.xml @@ -0,0 +1,6 @@ + + diff --git a/devices/pc/video/ibm/mda/ibm-mda-dual.xml b/devices/pc/video/ibm/mda/ibm-mda-dual.xml new file mode 100644 index 000000000..af46e7a3e --- /dev/null +++ b/devices/pc/video/ibm/mda/ibm-mda-dual.xml @@ -0,0 +1,11 @@ + + diff --git a/modules/pcjs/lib/keyboard.js b/modules/pcjs/lib/keyboard.js index 4965c051e..35a5338ab 100644 --- a/modules/pcjs/lib/keyboard.js +++ b/modules/pcjs/lib/keyboard.js @@ -984,7 +984,14 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control) if (this.bindings[id] === undefined) { switch (sBinding) { case "kbd": - this.bindings[id] = control; + /* + * Recording the binding ID prevents multiple controls (or components) from attempting to erroneously + * bind a control to the same ID, but in the case of a "dual display" configuration, we actually want + * to allow BOTH video components to call setBinding() for "kbd", so that it doesn't matter which + * display the user gives focus to. + * + * this.bindings[id] = control; + */ control.onkeydown = function onKeyDown(event) { return kbd.onKeyDown(event, true); }; diff --git a/modules/pcjs/lib/video.js b/modules/pcjs/lib/video.js index 51eba70a0..115ce6ef1 100644 --- a/modules/pcjs/lib/video.js +++ b/modules/pcjs/lib/video.js @@ -1046,7 +1046,7 @@ Card.STATUS1 = { * EGA/VGA Attribute Controller Registers (regATCIndx and regATCData) * * The current ATC INDX value is stored in cardEGA.regATCIndx (including the Card.ATC.INDX_ENABLE bit), and the - * ATC DATA values are stored in cardEGA.regATCData. Also, the state of the ATC INDX/DATA flip-flop is stored in fATCData. + * ATC DATA values are stored in cardEGA.regATCData. The state of the ATC INDX/DATA flip-flop is stored in fATCData. * * Note that the ATC palette registers (0x0-0xf) all use the following 6 bit assignments, with bits 6 and 7 unused: * @@ -2078,9 +2078,32 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg) this.cpu = cpu; this.dbg = dbg; - bus.addPortInputTable(this, Video.aPortInput); - bus.addPortOutputTable(this, Video.aPortOutput); + /* + * The only time we do NOT want to trap MDA ports is when the model has been specifically set to CGA. + */ + if (Video.CARD.NAMES[this.model] != Video.CARD.CGA) { + bus.addPortInputTable(this, Video.aMDAPortInput); + bus.addPortOutputTable(this, Video.aMDAPortOutput); + } + /* + * Similarly, the only time we do NOT want to trap CGA ports is when the model has been specifically set to MDA. + */ + if (Video.CARD.NAMES[this.model] != Video.CARD.MDA) { + bus.addPortInputTable(this, Video.aCGAPortInput); + bus.addPortOutputTable(this, Video.aCGAPortOutput); + } + + /* + * Note that in the case of EGA and VGA models, the above code ensures that we will trap both MDA and CGA + * port ranges -- which is good, because both the EGA and VGA can be reprogrammed to respond to those ports, + * but also potentially bad if you want to simulate a "dual display" system, where one of the displays is + * driven by either an MDA or CGA. + * + * However, you should still be able to make that work by loading the MDA or CGA video component first, because + * components should be initialized in the order they appear in the machine configuration file. Any attempt + * by another component to trap the same ports should be ignored. + */ if (this.nCard >= Video.CARD.EGA) { bus.addPortInputTable(this, Video.aEGAPortInput); bus.addPortOutputTable(this, Video.aEGAPortOutput); @@ -2560,7 +2583,8 @@ Video.prototype.reset = function() /* * As we noted in the constructor, when a model is specified, that takes precedence over any monitor - * switch settings. Conversely, when no model is specified, the nCard setting is considered provisional. + * switch settings. Conversely, when no model is specified, the nCard setting is considered provisional, + * so the monitor switch settings, if any, are allowed to determine the card type. */ if (!this.model) { this.nCard = (nMonitorType == ChipSet.MONITOR.MONO? Video.CARD.MDA : Video.CARD.CGA); @@ -5309,15 +5333,24 @@ Video.prototype.dumpVideo = function(sParm) * TODO: At one point, I'd added some "duplicate" entries for the MDA because, according to docs I'd read, * MDA ports are decoded at multiple addresses. However, if this is important, then it should be verified * and implemented consistently (eg, for CGA as well). For now, I'm decoding only the standard port addresses. + * + * For example, 0x3B5 is apparently also decoded at 0x3B1, 0x3B3, and 0x3B7, while 0x3B4 is also decoded at + * 0x3B0, 0x3B2, and 0x3B6. */ -Video.aPortInput = { -// 0x3B1: Video.prototype.inMDAData, // duplicate -// 0x3B3: Video.prototype.inMDAData, // duplicate +Video.aMDAPortInput = { 0x3B4: Video.prototype.inMDAIndx, // technically, not actually readable, but I want the Debugger to be able to read this 0x3B5: Video.prototype.inMDAData, // technically, the only Data registers that are readable are R14-R17 -// 0x3B7: Video.prototype.inMDAData, // duplicate 0x3B8: Video.prototype.inMDAMode, // technically, not actually readable, but I want the Debugger to be able to read this - 0x3BA: Video.prototype.inMDAStatus, + 0x3BA: Video.prototype.inMDAStatus +}; + +Video.aMDAPortOutput = { + 0x3B4: Video.prototype.outMDAIndx, + 0x3B5: Video.prototype.outMDAData, + 0x3B8: Video.prototype.outMDAMode +}; + +Video.aCGAPortInput = { 0x3D4: Video.prototype.inCGAIndx, // technically, not actually readable, but I want the Debugger to be able to read this 0x3D5: Video.prototype.inCGAData, // technically, the only Data registers that are readable are R14-R17 0x3D8: Video.prototype.inCGAMode, // technically, not actually readable, but I want the Debugger to be able to read this @@ -5325,16 +5358,7 @@ Video.aPortInput = { 0x3DA: Video.prototype.inCGAStatus }; -Video.aPortOutput = { -// 0x3B0: Video.prototype.outMDAIndx, // duplicate -// 0x3B1: Video.prototype.outMDAData, // duplicate -// 0x3B2: Video.prototype.outMDAIndx, // duplicate -// 0x3B3: Video.prototype.outMDAData, // duplicate - 0x3B4: Video.prototype.outMDAIndx, // 0x3B4 is decoded at 0x3B0, 0x3B2 and 0x3B6 as well (at least on an MDA), hence the duplicate mappings - 0x3B5: Video.prototype.outMDAData, // 0x3B5 is decoded at 0x3B1, 0x3B3 and 0x3B7 as well (at least on an MDA), hence the duplicate mappings -// 0x3B6: Video.prototype.outMDAIndx, // duplicate -// 0x3B7: Video.prototype.outMDAData, // duplicate - 0x3B8: Video.prototype.outMDAMode, +Video.aCGAPortOutput = { 0x3D4: Video.prototype.outCGAIndx, 0x3D5: Video.prototype.outCGAData, 0x3D8: Video.prototype.outCGAMode, @@ -5342,13 +5366,13 @@ Video.aPortOutput = { }; Video.aEGAPortInput = { - 0x3C0: Video.prototype.inATC, // technically, not actually readable, but I want the Debugger to be able to read this - 0x3C1: Video.prototype.inATC, // technically, not actually readable, but I want the Debugger to be able to read this + 0x3C0: Video.prototype.inATC, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too + 0x3C1: Video.prototype.inATC, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too 0x3C2: Video.prototype.inStatus0, - 0x3C4: Video.prototype.inSEQIndx, // technically, not actually readable, but I want the Debugger to be able to read this - 0x3C5: Video.prototype.inSEQData, // technically, not actually readable, but I want the Debugger to be able to read this - 0x3CE: Video.prototype.inGRCIndx, // technically, not actually readable, but I want the Debugger to be able to read this - 0x3CF: Video.prototype.inGRCData // technically, not actually readable, but I want the Debugger to be able to read this + 0x3C4: Video.prototype.inSEQIndx, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too + 0x3C5: Video.prototype.inSEQData, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too + 0x3CE: Video.prototype.inGRCIndx, // technically, only readable on a VGA, but I want the Debugger to be able to read this, too + 0x3CF: Video.prototype.inGRCData // technically, only readable on a VGA, but I want the Debugger to be able to read this, too }; Video.aEGAPortOutput = {