diff --git a/devices/pc/machine/compaq/deskpro386/vga/2048kb/machine.xml b/devices/pc/machine/compaq/deskpro386/vga/2048kb/machine.xml
index 3af76c866..30def430e 100644
--- a/devices/pc/machine/compaq/deskpro386/vga/2048kb/machine.xml
+++ b/devices/pc/machine/compaq/deskpro386/vga/2048kb/machine.xml
@@ -14,7 +14,7 @@
-
+
diff --git a/modules/pcjs/lib/chipset.js b/modules/pcjs/lib/chipset.js
index 45ca16ca7..5438b9af1 100644
--- a/modules/pcjs/lib/chipset.js
+++ b/modules/pcjs/lib/chipset.js
@@ -53,7 +53,7 @@ if (typeof module !== 'undefined') {
* sound: true to enable (experimental) sound support (default); false to disable
* scaleTimers: true to divide timer cycle counts by the CPU's cycle multiplier (default is false)
* floppies: array of floppy drive sizes in Kb (default is "[360, 360]" if no sw1 value provided)
- * monitor: none|tv|color|mono (if no sw1 value provided, default is "ega" for 5170, "mono" otherwise)
+ * monitor: none|tv|color|mono|ega|vga (if no sw1 value provided, default is "ega" for 5170, "mono" otherwise)
* rtcDate: optional RTC date/time (in GMT) to use on reset; use the ISO 8601 format; eg: "2014-10-01T08:00:00"
*
* The conventions used for the sw1 and sw2 strings are that the left-most character represents DIP switch [1],
@@ -297,7 +297,8 @@ ChipSet.MONITOR = {
COLOR: 2, // Color Display (5153)
MONO: 3, // Monochrome Display (5151)
EGACOLOR: 4, // Enhanced Color Display (5154) in High-Res Mode
- EGAEMULATION: 6 // Enhanced Color Display (5154) in Emulation Mode
+ EGAEMULATION: 6, // Enhanced Color Display (5154) in Emulation Mode
+ VGACOLOR: 7 // VGA Color Display
};
/*
@@ -309,7 +310,8 @@ ChipSet.aMonitorSwitches = {
"tv": 0x1,
"color": 0x2,
"mono": 0x3,
- "ega": 0x0
+ "ega": 0x0,
+ "vga": 0x0
};
/*
diff --git a/modules/pcjs/lib/video.js b/modules/pcjs/lib/video.js
index 72e6fef37..adb39baea 100644
--- a/modules/pcjs/lib/video.js
+++ b/modules/pcjs/lib/video.js
@@ -101,24 +101,22 @@ function Video(parmsVideo, canvas, context, textarea, container)
Component.call(this, "Video", parmsVideo, Video, Messages.VIDEO);
/*
- * This records the model specified (eg, "mda", "cga", "ega" or "" if none specified);
+ * This records the model specified (eg, "mda", "cga", "ega", "vga" or "" if none specified);
* when a model is specified, it overrides whatever model we infer from the ChipSet's switches
* (since those motherboard switches tell us only the type of monitor, not the type of card).
*/
this.model = parmsVideo['model'];
- this.cbMemory = 0;
+ this.nCard = Video.CARD.NAMES[this.model] || Video.CARD.MDA;
- if (this.model == "ega") {
- this.sEGASW = parmsVideo['switches'];
- this.cbMemory = parmsVideo['memory'] || 0; // zero means fallback to the cardSpec's default size
- }
+ this.cbMemory = parmsVideo['memory'] || 0; // zero means fallback to the cardSpec's default size
+ this.sSwitches = parmsVideo['switches'];
/*
* powerUp() uses the default mode ONLY if ChipSet doesn't give us a default.
*/
this.nModeDefault = parmsVideo['mode'];
if (this.nModeDefault === undefined || Video.aModeParms[this.nModeDefault] === undefined) {
- this.nModeDefault = Video.MODES.MDA_80X25;
+ this.nModeDefault = Video.MODE.MDA_80X25;
}
/*
@@ -336,12 +334,20 @@ Video.TRAPALL = true; // monitor all I/O by default (not just deltas)
/*
* Supported Cards
*
- * Note that we choose IDs that match the default font ID for each card as well, just for consistency.
+ * Note that we choose IDs that match the default font ID for each card as well, for convenience.
*/
-Video.CARDS = {};
-Video.CARDS.MDA = 1;
-Video.CARDS.CGA = 3;
-Video.CARDS.EGA = 5;
+Video.CARD = {
+ MDA: 1,
+ CGA: 3,
+ EGA: 5,
+ VGA: 7,
+ NAMES: {
+ "mda": 1,
+ "cga": 3,
+ "ega": 5,
+ "vga": 7
+ }
+};
/*
* Supported Monitors
@@ -448,7 +454,7 @@ Video.monitorSpecs[ChipSet.MONITOR.EGACOLOR] = {
* 0x0: return SW4
*
* These 4 bits are also copied to the byte at 40:88h by the EGA BIOS, where bit 0 is SW1, bit 1 is SW2,
- * bit 2 is SW3 and bit 3 is SW4. Our switch settings come from bEGASW, which in turn comes from sEGASW,
+ * bit 2 is SW3 and bit 3 is SW4. Our switch settings come from bEGASwitches, which in turn comes from sSwitches,
* which in turn comes from the "switches" property passed to the Video component, if any.
*
* As usual, the switch settings are reversed in both direction and sense from the switch settings; the
@@ -462,7 +468,7 @@ Video.aEGAMonitorSwitches = {
0x06: [ChipSet.MONITOR.TV, ChipSet.MONITOR.MONO, true], // "1001"
0x07: [ChipSet.MONITOR.COLOR, ChipSet.MONITOR.MONO, true], // "0001"
0x08: [ChipSet.MONITOR.EGAEMULATION, ChipSet.MONITOR.MONO, true], // "1110"
- 0x09: [ChipSet.MONITOR.EGACOLOR, ChipSet.MONITOR.MONO, true], // "0110" [our default; see bEGASW below]
+ 0x09: [ChipSet.MONITOR.EGACOLOR, ChipSet.MONITOR.MONO, true], // "0110" [our default; see bEGASwitches below]
0x0a: [ChipSet.MONITOR.MONO, ChipSet.MONITOR.TV, true], // "1010"
0x0b: [ChipSet.MONITOR.MONO, ChipSet.MONITOR.COLOR, true], // "0010"
0x00: [ChipSet.MONITOR.TV, ChipSet.MONITOR.MONO, false], // "1111"
@@ -490,20 +496,24 @@ Video.aEGAMonitorSwitches = {
* situations as best they can (and when they don't, it should be considered a bug if some application is
* broken as a result), but realistically, this is never going to be a 100% accurate hardware emulation.
*/
-Video.MODES = {};
-Video.MODES.CGA_40X25_BW = 0;
-Video.MODES.CGA_40X25 = 1;
-Video.MODES.CGA_80X25_BW = 2;
-Video.MODES.CGA_80X25 = 3;
-Video.MODES.CGA_320X200 = 4;
-Video.MODES.CGA_320X200_BW = 5;
-Video.MODES.CGA_640X200 = 6;
-Video.MODES.MDA_80X25 = 7;
-Video.MODES.EGA_320X200 = 0x0D; // mapped at A000:0000
-Video.MODES.EGA_640X200 = 0x0E; // mapped at A000:0000
-Video.MODES.EGA_640X350_MONO = 0x0F; // mapped at A000:0000, monochrome
-Video.MODES.EGA_640X350 = 0x10; // mapped at A000:0000, color
-Video.MODES.UNKNOWN = 0xFF;
+Video.MODE = {
+ CGA_40X25_BW: 0,
+ CGA_40X25: 1,
+ CGA_80X25_BW: 2,
+ CGA_80X25: 3,
+ CGA_320X200: 4,
+ CGA_320X200_BW: 5,
+ CGA_640X200: 6,
+ MDA_80X25: 7,
+ EGA_320X200: 0x0D, // mapped at A000:0000
+ EGA_640X200: 0x0E, // mapped at A000:0000
+ EGA_640X350_MONO: 0x0F, // mapped at A000:0000, monochrome
+ EGA_640X350: 0x10, // mapped at A000:0000, color
+ VGA_640X480_MONO: 0x11, // mapped at A000:0000, monochrome
+ VGA_640X480: 0x12, // mapped at A000:0000, color
+ VGA_320X200: 0x13, // mapped at A000:0000, color
+ UNKNOWN: 0xFF
+};
/**
* @class Font
@@ -535,13 +545,16 @@ Video.MODES.UNKNOWN = 0xFF;
* dot patterns for 256 different characters. The character sets are identical to those provided by the
* IBM Monochrome Display Adapter and the IBM Color/Graphics Monitor Adapter."
*/
-Video.FONTS = {};
-Video.FONTS.MDA = 1; // 9x14 monochrome font
-Video.FONTS.MDAD = 2; // 18x28 monochrome font (this is the 9x14 font doubled)
-Video.FONTS.CGA = 3; // 8x8 color font
-Video.FONTS.CGAD = 6; // 16x16 color font (this is the 8x8 CGA font doubled)
-Video.FONTS.EGA = 5; // 9x14 color font
-Video.FONTS.EGAD = 10; // 18x28 color font (this is the 9x14 EGA font doubled)
+Video.FONT = {
+ MDA: 1, // 9x14 monochrome font
+ MDAD: 2, // 18x28 monochrome font (this is the 9x14 font doubled)
+ CGA: 3, // 8x8 color font
+ CGAD: 6, // 16x16 color font (this is the 8x8 CGA font doubled)
+ EGA: 5, // 8x14 color font
+ EGAD: 10, // 16x28 color font (this is the 8x14 EGA font doubled)
+ VGA: 7, // 8x16 color font
+ VGAD: 14 // 16x32 color font (this is the 8x16 VGA font doubled)
+};
/*
* For each video mode, we need to know the following pieces of information:
@@ -563,19 +576,22 @@ Video.FONTS.EGAD = 10; // 18x28 color font (this is the 9x14 EGA font d
* and it might not always be the best fit.
*/
Video.aModeParms = []; // Mode
-Video.aModeParms[Video.MODES.CGA_40X25] = [ 40, 25, 1, 0, Video.FONTS.CGA]; // 0x00
-Video.aModeParms[Video.MODES.CGA_80X25] = [ 80, 25, 1, 0, Video.FONTS.CGA]; // 0x02
-Video.aModeParms[Video.MODES.CGA_320X200] = [320, 200, 8, 192]; // 0x04
-Video.aModeParms[Video.MODES.CGA_640X200] = [640, 200, 16, 192]; // 0x06
-Video.aModeParms[Video.MODES.MDA_80X25] = [ 80, 25, 1, 0, Video.FONTS.MDA]; // 0x07
-Video.aModeParms[Video.MODES.EGA_320X200] = [320, 200, 16]; // 0x0D
-Video.aModeParms[Video.MODES.EGA_640X200] = [640, 200, 16]; // 0x0E
-Video.aModeParms[Video.MODES.EGA_640X350_MONO] = [640, 350, 16]; // 0x0F
-Video.aModeParms[Video.MODES.EGA_640X350] = [640, 350, 16]; // 0x10
+Video.aModeParms[Video.MODE.CGA_40X25] = [ 40, 25, 1, 0, Video.FONT.CGA]; // 0x00
+Video.aModeParms[Video.MODE.CGA_80X25] = [ 80, 25, 1, 0, Video.FONT.CGA]; // 0x02
+Video.aModeParms[Video.MODE.CGA_320X200] = [320, 200, 8, 192]; // 0x04
+Video.aModeParms[Video.MODE.CGA_640X200] = [640, 200, 16, 192]; // 0x06
+Video.aModeParms[Video.MODE.MDA_80X25] = [ 80, 25, 1, 0, Video.FONT.MDA]; // 0x07
+Video.aModeParms[Video.MODE.EGA_320X200] = [320, 200, 16]; // 0x0D
+Video.aModeParms[Video.MODE.EGA_640X200] = [640, 200, 16]; // 0x0E
+Video.aModeParms[Video.MODE.EGA_640X350_MONO] = [640, 350, 16]; // 0x0F
+Video.aModeParms[Video.MODE.EGA_640X350] = [640, 350, 16]; // 0x10
+Video.aModeParms[Video.MODE.VGA_640X480_MONO] = [640, 480, 16]; // 0x11
+Video.aModeParms[Video.MODE.VGA_640X480] = [640, 480, 16]; // 0x12
+Video.aModeParms[Video.MODE.VGA_320X200] = [320, 200, 16]; // 0x13
-Video.aModeParms[Video.MODES.CGA_40X25_BW] = Video.aModeParms[Video.MODES.CGA_40X25]; // 0x01
-Video.aModeParms[Video.MODES.CGA_80X25_BW] = Video.aModeParms[Video.MODES.CGA_80X25]; // 0x03
-Video.aModeParms[Video.MODES.CGA_320X200_BW] = Video.aModeParms[Video.MODES.CGA_320X200]; // 0x05
+Video.aModeParms[Video.MODE.CGA_40X25_BW] = Video.aModeParms[Video.MODE.CGA_40X25]; // 0x01
+Video.aModeParms[Video.MODE.CGA_80X25_BW] = Video.aModeParms[Video.MODE.CGA_80X25]; // 0x03
+Video.aModeParms[Video.MODE.CGA_320X200_BW] = Video.aModeParms[Video.MODE.CGA_320X200]; // 0x05
/*
* MDA attribute byte definitions
@@ -746,7 +762,7 @@ Video.aEGADWToByte[0x80808080|0] = 0xf;
*
* @constructor
* @param {Video} [video]
- * @param {number} [iCard] (see Video.CARDS.*)
+ * @param {number} [iCard] (see Video.CARD.*)
* @param {Array|null} [data]
* @param {number} [cbMemory] is specified if the card must allocate its own memory buffer
*/
@@ -806,7 +822,7 @@ function Card(video, iCard, data, cbMemory)
this.nCRTCRegs = Card.CRTC.TOTAL_REGS;
this.asCRTCRegs = DEBUGGER? Card.CRTC.REGS : [];
- if (iCard == Video.CARDS.EGA) {
+ if (iCard >= Video.CARD.EGA) {
this.nCRTCRegs = Card.CRTC.EGA.TOTAL_REGS;
this.asCRTCRegs = DEBUGGER? Card.CRTC.EGA_REGS : [];
this.initEGA(data[6], nMonitorType);
@@ -1055,9 +1071,10 @@ Card.FEAT_CTRL.BITS = 0x03; // feature control bits
Card.STATUS0 = {};
Card.STATUS0.PORT = 0x3C2; // read-only (aka STATUS0, to distinguish it from PORT_CGA_STATUS)
+Card.STATUS0.RESERVED = 0x0F;
Card.STATUS0.SWSENSE = 0x10;
Card.STATUS0.SWSENSE_SHIFT = 4;
-Card.STATUS0.FEAT = 0x60;
+Card.STATUS0.FEAT = 0x60; // VGA: reserved
Card.STATUS0.INTERRUPT = 0x80; // 1: video is being displayed; 0: vertical retrace is occurring
/*
@@ -1707,7 +1724,7 @@ Card.prototype.saveCard = function()
data[3] = this.statusReg;
data[4] = this.iCRTCReg | (this.iCRTCPrev << 8);
data[5] = this.aCRTCRegs;
- if (this.iCard == Video.CARDS.EGA) {
+ if (this.iCard >= Video.CARD.EGA) {
data[6] = this.saveEGA();
}
data[7] = this.nInitCycles;
@@ -1765,16 +1782,16 @@ Card.prototype.dumpCard = function()
*/
this.dumpRegs("CRTC", this.iCRTCReg, this.aCRTCRegs, this.asCRTCRegs);
- if (this.iCard == Video.CARDS.MDA || this.iCard == Video.CARDS.CGA) {
+ if (this.iCard == Video.CARD.MDA || this.iCard == Video.CARD.CGA) {
this.dumpRegs(" MODEREG", this.modeReg);
this.dumpRegs(" STATUS1", this.statusReg);
}
- if (this.iCard == Video.CARDS.CGA) {
+ if (this.iCard == Video.CARD.CGA) {
this.dumpRegs(" COLOR", this.colorReg);
}
- if (this.iCard == Video.CARDS.EGA) {
+ if (this.iCard >= Video.CARD.EGA) {
this.dbg.println(" ATCDATA: " + this.fATCData);
this.dumpRegs(" ATC", this.iATCReg, this.aATCRegs, this.asATCRegs);
this.dumpRegs(" GRC", this.iGRCReg, this.aGRCRegs, this.asGRCRegs);
@@ -1947,9 +1964,10 @@ Card.prototype.setMemoryAccess = function(nAccess)
* to access the buffer directly, instead of going through the Bus memory interface.
*/
Video.cardSpecs = [];
-Video.cardSpecs[Video.CARDS.MDA] = ["MDA", Card.MDA.CRTC.INDX.PORT, 0xB0000, 0x01000, 0, ChipSet.MONITOR.MONO];
-Video.cardSpecs[Video.CARDS.CGA] = ["CGA", Card.CGA.CRTC.INDX.PORT, 0xB8000, 0x04000, 0, ChipSet.MONITOR.COLOR];
-Video.cardSpecs[Video.CARDS.EGA] = ["EGA", Card.CGA.CRTC.INDX.PORT, 0xB8000, 0x04000, 0x10000, ChipSet.MONITOR.EGACOLOR];
+Video.cardSpecs[Video.CARD.MDA] = ["MDA", Card.MDA.CRTC.INDX.PORT, 0xB0000, 0x01000, 0, ChipSet.MONITOR.MONO];
+Video.cardSpecs[Video.CARD.CGA] = ["CGA", Card.CGA.CRTC.INDX.PORT, 0xB8000, 0x04000, 0, ChipSet.MONITOR.COLOR];
+Video.cardSpecs[Video.CARD.EGA] = ["EGA", Card.CGA.CRTC.INDX.PORT, 0xB8000, 0x04000, 0x10000, ChipSet.MONITOR.EGACOLOR];
+Video.cardSpecs[Video.CARD.VGA] = ["VGA", Card.CGA.CRTC.INDX.PORT, 0xB8000, 0x04000, 0x10000, ChipSet.MONITOR.VGACOLOR];
/**
* initBus(cmp, bus, cpu, dbg)
@@ -1972,7 +1990,7 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
bus.addPortInputTable(this, Video.aPortInput);
bus.addPortOutputTable(this, Video.aPortOutput);
- if (this.model == "ega") {
+ if (this.nCard >= Video.CARD.EGA) {
bus.addPortInputTable(this, Video.aEGAPortInput);
bus.addPortOutputTable(this, Video.aEGAPortOutput);
}
@@ -1996,10 +2014,10 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
this.kbd.setBinding(this.textareaScreen? "textarea" : "canvas", "kbd", this.inputScreen);
}
- this.bEGASW = 0x09; // our default "switches" setting (see aEGAMonitorSwitches)
+ this.bEGASwitches = 0x09; // our default "switches" setting (see aEGAMonitorSwitches)
this.chipset = cmp.getComponentByType("ChipSet");
- if (this.chipset && this.sEGASW) {
- this.bEGASW = this.chipset.parseSwitches(this.sEGASW, this.bEGASW);
+ if (this.chipset && this.sSwitches) {
+ if (this.nCard == Video.CARD.EGA) this.bEGASwitches = this.chipset.parseSwitches(this.sSwitches, this.bEGASwitches);
}
if (this.kbd && this.fTouchScreen) this.captureTouch();
@@ -2444,28 +2462,27 @@ Video.prototype.reset = function()
nMonitorType = this.chipset.getSWVideoMonitor();
}
- var fEGA = false;
- if (this.model) {
- switch (this.model) {
- case "ega":
- fEGA = true;
- var aMonitors = Video.aEGAMonitorSwitches[this.bEGASW];
- /*
- * TODO: Figure out how to deal with aMonitors[2], the boolean which indicates
- * whether the EGA is driving the primary monitor (true) or the secondary monitor (false).
- */
- if (aMonitors) nMonitorType = aMonitors[0];
- if (!nMonitorType) nMonitorType = ChipSet.MONITOR.EGACOLOR;
- break;
- case "mda":
- nMonitorType = ChipSet.MONITOR.MONO;
- break;
- case "cga":
- /* falls through */
- default:
- nMonitorType = ChipSet.MONITOR.COLOR;
- break;
- }
+ switch (this.nCard) {
+ case Video.CARD.VGA:
+ nMonitorType = ChipSet.MONITOR.VGACOLOR;
+ break;
+ case Video.CARD.EGA:
+ var aMonitors = Video.aEGAMonitorSwitches[this.bEGASwitches];
+ /*
+ * TODO: Figure out how to deal with aMonitors[2], the boolean which indicates
+ * whether the EGA is driving the primary monitor (true) or the secondary monitor (false).
+ */
+ if (aMonitors) nMonitorType = aMonitors[0];
+ if (!nMonitorType) nMonitorType = ChipSet.MONITOR.EGACOLOR;
+ break;
+ case Video.CARD.MDA:
+ nMonitorType = ChipSet.MONITOR.MONO;
+ break;
+ case Video.CARD.CGA:
+ /* falls through */
+ default:
+ nMonitorType = ChipSet.MONITOR.COLOR;
+ break;
}
if (this.nMonitorType !== nMonitorType) {
@@ -2474,16 +2491,14 @@ Video.prototype.reset = function()
}
this.cardActive = null;
- this.cardMono = this.cardMDA = new Card(this, Video.CARDS.MDA);
- this.cardColor = this.cardCGA = new Card(this, Video.CARDS.CGA);
+ this.cardMono = this.cardMDA = new Card(this, Video.CARD.MDA);
+ this.cardColor = this.cardCGA = new Card(this, Video.CARD.CGA);
- if (!fEGA) {
- /*
- * Define a dummy (uninitialized) EGA card for now
- */
- this.cardEGA = new Card();
- } else {
- this.cardEGA = new Card(this, Video.CARDS.EGA, null, this.cbMemory);
+ if (this.nCard < Video.CARD.EGA) {
+ this.cardEGA = new Card(); // define a dummy (uninitialized) EGA card for now
+ }
+ else {
+ this.cardEGA = new Card(this, this.nCard, null, this.cbMemory);
this.enableEGA();
}
@@ -2493,7 +2508,7 @@ Video.prototype.reset = function()
this.buildFonts();
this.nMode = null;
- this.nModeDefault = (nMonitorType == ChipSet.MONITOR.MONO? Video.MODES.MDA_80X25 : Video.MODES.CGA_80X25);
+ this.nModeDefault = (nMonitorType == ChipSet.MONITOR.MONO? Video.MODE.MDA_80X25 : Video.MODE.CGA_80X25);
this.iCellCursor = -1; // initially, there is no visible cursor cell
this.cBlinks = -1; // initially, blinking is not active
@@ -2524,7 +2539,7 @@ Video.prototype.reset = function()
for (var addrScreen = this.cardActive.addrBuffer; addrScreen < addrScreenLimit; addrScreen += 2) {
var dataRandom = Math.floor(Math.random() * 0x10000);
var bChar, bAttr;
- if (this.nMonitorType == ChipSet.MONITOR.EGACOLOR) {
+ if (this.nMonitorType == ChipSet.MONITOR.EGACOLOR || this.nMonitorType == ChipSet.MONITOR.VGACOLOR) {
/*
* For the EGA, we choose sequential characters; for random characters, copy the MDA/CGA code below.
*/
@@ -2596,13 +2611,13 @@ Video.prototype.restore = function(data)
this.nMode = a[2];
this.cardActive = null;
- this.cardMono = this.cardMDA = new Card(this, Video.CARDS.MDA, data[0]);
- this.cardColor = this.cardCGA = new Card(this, Video.CARDS.CGA, data[1]);
+ this.cardMono = this.cardMDA = new Card(this, Video.CARD.MDA, data[0]);
+ this.cardColor = this.cardCGA = new Card(this, Video.CARD.CGA, data[1]);
/*
* If no EGA was originally initialized, then cardEGA will remain uninitialized.
*/
- this.cardEGA = new Card(this, Video.CARDS.EGA, data[3], this.cbMemory);
+ this.cardEGA = new Card(this, this.nCard, data[3], this.cbMemory);
if (this.cardEGA.fActive) this.enableEGA();
/*
@@ -2747,10 +2762,10 @@ Video.prototype.onLoadSetFonts = function(sFontFile, sFontData, nErrorCode)
*
* Called by copyROM() whenever a ROM with a 'notify' attribute set to our component ID has been loaded.
*
- * If model is "ega", then we assume the associated ROM is the original IBM EGA ROM, which stores
- * its 8x14 font data at 0x2230 (and unlike the MDA/CGA character generator ROM, which splits the first
- * 8 rows and remaining 6 rows of each character across separate 2K chunks, the bytes for all the EGA
- * character rows are contiguous); the total size of the 8x14 font is 0xE00 bytes.
+ * For IBM EGA cards, we assume the associated ROM is the original IBM EGA ROM, which stores its 8x14 font
+ * data at 0x2230 (and unlike the MDA/CGA character generator ROM, which splits the first 8 rows and
+ * remaining 6 rows of each character across separate 2K chunks, the bytes for all the EGA character rows
+ * are contiguous); the total size of the 8x14 font is 0xE00 bytes.
*
* At 0x3030, there is an "ALPHA SUPPLEMENT" table, which contains 15 bytes per row instead of 14,
* because each row is preceded by one byte containing the corresponding ASCII code; there are 20 entries
@@ -2765,12 +2780,18 @@ Video.prototype.onLoadSetFonts = function(sFontFile, sFontData, nErrorCode)
* font generation to support it (as opposed to "init-time" generation, which is all we do now). There's
* probably a similar need for user-defined fonts; for now, they're just not supported.
*
+ * IBM VGA NOTES: The IBM VGA "ALPHA SUPPLEMENT" table appears at the same delta (+0x0E00) as the IBM EGA
+ * table (ie, 0x3f8d + 0x0e00 = 0x4d8d). If we assume the same length for the supplement table (20 chars,
+ * 15 bytes per char, for a total of 0x12c bytes, rounded up to 0x130), that would put the 8x8 data at
+ * offset 0x4ebd. I suspect the data ends at around 0x5eb9, meaning it's organized as 16 bytes per char
+ * instead of 8.
+ *
* @this {Video}
* @param {Array.} abROM
*/
Video.prototype.onROMLoad = function(abROM)
{
- if (this.model == "ega") {
+ if (this.nCard == Video.CARD.EGA) {
/*
* TODO: Unlike the MDA/CGA font data, we may want to hang onto this data, so that we can
* regenerate the color font(s) whenever the foreground and/or background colors have changed.
@@ -2778,6 +2799,10 @@ Video.prototype.onROMLoad = function(abROM)
if (DEBUG) this.printMessage("onROMLoad(): EGA fonts loaded");
this.setFontData(abROM, [0x2230, 0x3160], 8);
}
+ else if (this.nCard == Video.CARD.VGA) {
+ if (DEBUG) this.printMessage("onROMLoad(): VGA fonts loaded");
+ this.setFontData(abROM, [0x3f8d, 0x0000], 8);
+ }
this.setReady();
};
@@ -2909,27 +2934,26 @@ Video.prototype.buildFonts = function()
var fChanges = false;
/*
- * There's no point building any fonts if (a) we're in a non-windowed (eg, command-line) environment or
- * (b) no font data was loaded.
+ * There's no point building any fonts if we're in a non-windowed (eg, command-line) environment or no font data was loaded.
*/
if (window && this.abFontData) {
var offSplit = this.cxFontChar? 0 : 0x0800;
var cxChar = this.cxFontChar? this.cxFontChar : 9;
- if (this.buildFont(Video.FONTS.MDA, this.aFontOffsets[0], offSplit, cxChar, 14, this.abFontData, Video.aMDAColors, Video.aMDAColorMap)) {
+ if (this.buildFont(Video.FONT.MDA, this.aFontOffsets[0], offSplit, cxChar, 14, this.abFontData, Video.aMDAColors, Video.aMDAColorMap)) {
fChanges = true;
}
var aRGBColors = this.getCardColors();
offSplit = 0x0000;
cxChar = this.cxFontChar? this.cxFontChar : 8;
- if (this.buildFont(Video.FONTS.CGA, this.aFontOffsets[1], offSplit, cxChar, 8, this.abFontData, aRGBColors)) {
+ if (this.buildFont(Video.FONT.CGA, this.aFontOffsets[1], offSplit, cxChar, 8, this.abFontData, aRGBColors)) {
fChanges = true;
}
if (this.cxFontChar) {
- if (this.buildFont(Video.FONTS.EGA, this.aFontOffsets[0], 0, this.cxFontChar, 14, this.abFontData, aRGBColors)) {
+ if (this.buildFont(this.nCard, this.aFontOffsets[0], 0, this.cxFontChar, 14, this.abFontData, aRGBColors)) {
fChanges = true;
}
}
@@ -2944,7 +2968,7 @@ Video.prototype.buildFonts = function()
*
* @this {Video}
* @param {number} nFont
- * @param {number} offData is the offset of the font data
+ * @param {number} offData is the offset of the font data, zero if none
* @param {number} offSplit is the offset of any split font data, or zero if not split
* @param {number} cxChar is the width of the font characters
* @param {number} cyChar is the height of the font characters
@@ -2957,22 +2981,23 @@ Video.prototype.buildFont = function(nFont, offData, offSplit, cxChar, cyChar, a
{
var fChanges = false;
- if (DEBUG && this.messageEnabled()) {
- this.printMessage("buildFont(" + nFont + "): building " + Video.cardSpecs[nFont][0] + " font");
- }
- if (this.createFont(nFont, offData, offSplit, cxChar, cyChar, abFontData, aRGBColors, aColorMap)) fChanges = true;
-
- /*
- * If font-doubling is enabled, then load a double-size version of the font as well, as it provides
- * sharper rendering, especially when the screen cell size is a multiple of the above font cell size;
- * in the case of the CGA, this may also be useful for 40-column modes.
- */
- if (this.fDoubleFont) {
- nFont <<= 1;
+ if (offData) {
if (DEBUG && this.messageEnabled()) {
- this.printMessage("buildFont(" + nFont + "): building " + Video.cardSpecs[nFont >> 1][0] + " double-size font");
+ this.printMessage("buildFont(" + nFont + "): building " + Video.cardSpecs[nFont][0] + " font");
}
if (this.createFont(nFont, offData, offSplit, cxChar, cyChar, abFontData, aRGBColors, aColorMap)) fChanges = true;
+ /*
+ * If font-doubling is enabled, then load a double-size version of the font as well, as it provides
+ * sharper rendering, especially when the screen cell size is a multiple of the above font cell size;
+ * in the case of the CGA, this may also be useful for 40-column modes.
+ */
+ if (this.fDoubleFont) {
+ nFont <<= 1;
+ if (DEBUG && this.messageEnabled()) {
+ this.printMessage("buildFont(" + nFont + "): building " + Video.cardSpecs[nFont >> 1][0] + " double-size font");
+ }
+ if (this.createFont(nFont, offData, offSplit, cxChar, cyChar, abFontData, aRGBColors, aColorMap)) fChanges = true;
+ }
}
return fChanges;
};
@@ -3226,9 +3251,9 @@ Video.prototype.checkCursor = function()
* HACK: The original EGA BIOS has a cursor emulation bug when 43-line mode is enabled, so we attempt to detect
* that particular combination of bad values and automatically fix them.
*/
- var fEGA = false;
+ var fEGAHack = false;
if (this.cardActive === this.cardEGA) {
- fEGA = true;
+ fEGAHack = true;
if (bCursorMax == 7 && bCursorStart == 4 && !bCursorEnd) bCursorEnd = 7;
}
@@ -3240,7 +3265,7 @@ Video.prototype.checkCursor = function()
* TODO: Verify whether the second test (bCursorStart > bCursorMax) should also result in a hidden cursor;
* ThinkTank sets both start and end values to 0x0f, which doesn't make sense on a CGA, where the max is 0x07.
*/
- if ((bCursorFlags & Card.CRTC.CURSOR_START.BLINKOFF) || bCursorStart > bCursorEnd && !fEGA || bCursorStart > bCursorMax) {
+ if ((bCursorFlags & Card.CRTC.CURSOR_START.BLINKOFF) || bCursorStart > bCursorEnd && !fEGAHack || bCursorStart > bCursorMax) {
this.removeCursor();
return false;
}
@@ -3427,17 +3452,19 @@ Video.prototype.setDimensions = function()
this.nFont = 0;
this.nCols = this.nDefaultCols;
this.nRows = this.nDefaultRows;
- this.nCellsPerWord = Video.aModeParms[Video.MODES.MDA_80X25][2];
+ this.nCellsPerWord = Video.aModeParms[Video.MODE.MDA_80X25][2];
var cbPadding = 0;
var modeParms = Video.aModeParms[this.nMode];
if (modeParms) {
+
this.nCols = modeParms[0];
this.nRows = modeParms[1];
this.nCellsPerWord = modeParms[2];
cbPadding = modeParms[3] || 0;
this.nFont = modeParms[4]; // this will be undefined for graphics modes
- if (this.nMonitorType == ChipSet.MONITOR.EGACOLOR) {
+
+ if (this.nMonitorType == ChipSet.MONITOR.EGACOLOR || this.nMonitorType == ChipSet.MONITOR.VGACOLOR) {
/*
* When an EGA is connected to a CGA monitor, the old aModeParms table is correct: we must
* use the hard-coded 8x8 "CGA_80" font. But when it's connected to an EGA monitor, we want
@@ -3446,7 +3473,7 @@ Video.prototype.setDimensions = function()
* TODO: Can an EGA with a monochrome monitor be programmed for 43-line mode as well? If so,
* then we'll need to load another MDA font variation, because we only load an 9x14 font for MDA.
*/
- if (this.cardActive === this.cardEGA && this.nFont == Video.FONTS.CGA) {
+ if (this.cardActive === this.cardEGA && this.nFont == Video.FONT.CGA) {
if (this.cardEGA.aCRTCRegs[Card.CRTC.MAX_SCAN_LINE] == 7) {
/*
* Vertical resolution of 350 divided by 8 (ie, scan lines 0-7) yields 43 whole rows.
@@ -3460,8 +3487,11 @@ Video.prototype.setDimensions = function()
else /* if (this.cardEGA.aCRTCRegs[Card.CRTC.MAX_SCAN_LINE] == 13) */ {
/*
* Vertical resolution of 350 divided by 14 (ie, scan lines 0-13) yields exactly 25 rows.
+ *
+ * Note that a card's default font matches its card ID (eg, Video.CARD.EGA == Video.FONT.EGA,
+ * and Video.CARD.VGA == Video.FONT.VGA)
*/
- this.nFont = Video.FONTS.EGA;
+ this.nFont = this.nCard;
}
}
}
@@ -3471,7 +3501,7 @@ Video.prototype.setDimensions = function()
this.nCellCache = (this.nCells / this.nCellsPerWord);
this.cbScreen = (this.nCellCache << 1) + cbPadding;
this.cbSplit = (cbPadding? ((this.cbScreen + cbPadding) >> 1) : 0);
- if (this.nMode >= Video.MODES.EGA_320X200) this.nCellCache <<= 1;
+ if (this.nMode >= Video.MODE.EGA_320X200) this.nCellCache <<= 1;
/*
* If no fonts were successfully loaded, there's no point in initializing the remaining drawing parameters.
@@ -3617,10 +3647,10 @@ Video.prototype.checkMode = function(fForce)
if (nMode == null) nMode = this.nModeDefault;
}
else {
- if (card.iCard == Video.CARDS.MDA) {
- nMode = Video.MODES.MDA_80X25;
+ if (card.iCard == Video.CARD.MDA) {
+ nMode = Video.MODE.MDA_80X25;
}
- else if (card.iCard == Video.CARDS.EGA) {
+ else if (card.iCard == Video.CARD.EGA) {
/*
* The sizeBuffer we choose reflects the amount of physical address space that all 4 planes
* of EGA memory normally span, NOT the total amount of EGA memory. So for a 64Kb EGA card,
@@ -3640,22 +3670,22 @@ Video.prototype.checkMode = function(fForce)
case Card.GRC.MISC.MAPA0128:
card.addrBuffer = 0xA0000;
card.sizeBuffer = cbBuffer; // 0x20000
- nMode = Video.MODES.UNKNOWN; // no BIOS mode uses this mapping, but we don't want to leave nMode null if we've come this far
+ nMode = Video.MODE.UNKNOWN; // no BIOS mode uses this mapping, but we don't want to leave nMode null if we've come this far
break;
case Card.GRC.MISC.MAPA064:
card.addrBuffer = 0xA0000;
card.sizeBuffer = cbBuffer; // 0x10000
- nMode = (this.nMonitorType == ChipSet.MONITOR.MONO? Video.MODES.EGA_640X350_MONO : Video.MODES.EGA_640X350);
+ nMode = (this.nMonitorType == ChipSet.MONITOR.MONO? Video.MODE.EGA_640X350_MONO : Video.MODE.EGA_640X350);
break;
case Card.GRC.MISC.MAPB032:
card.addrBuffer = 0xB0000;
card.sizeBuffer = cbBufferText;
- nMode = Video.MODES.MDA_80X25;
+ nMode = Video.MODE.MDA_80X25;
break;
case Card.GRC.MISC.MAPB832:
card.addrBuffer = 0xB8000;
card.sizeBuffer = cbBufferText;
- nMode = (this.nMonitorType == ChipSet.MONITOR.MONO? Video.MODES.CGA_80X25_BW : Video.MODES.CGA_80X25);
+ nMode = (this.nMonitorType == ChipSet.MONITOR.MONO? Video.MODE.CGA_80X25_BW : Video.MODE.CGA_80X25);
break;
default:
break;
@@ -3664,7 +3694,7 @@ Video.prototype.checkMode = function(fForce)
var fSEQDotClock = (card.aSEQRegs[Card.SEQ.CLK.INDX] & Card.SEQ.CLK.DOTCLOCK);
var nCRTCVertTotal = card.aCRTCRegs[Card.CRTC.EGA.VERT_TOTAL] | ((card.aCRTCRegs[Card.CRTC.EGA.OVERFLOW.INDX] & Card.CRTC.EGA.OVERFLOW.VERT_TOTAL) << 8);
- if (nMode != Video.MODES.UNKNOWN) {
+ if (nMode != Video.MODE.UNKNOWN) {
if (!(regGRCMisc & Card.GRC.MISC.GRAPHICS)) {
if (fSEQDotClock) nMode -= 2;
} else {
@@ -3673,14 +3703,14 @@ Video.prototype.checkMode = function(fForce)
// Since nMode will have been assigned a default of either 0x02 or 0x03, convert that to either
// 0x05 or 0x04 if we're in a low-res graphics mode, 0x06 otherwise.
//
- nMode = fSEQDotClock? (7 - nMode) : Video.MODES.CGA_640X200;
+ nMode = fSEQDotClock? (7 - nMode) : Video.MODE.CGA_640X200;
} else {
//
// card.addrBuffer must be 0xA0000, so we need to discriminate between modes 0x0D through 0x10;
// we've already defaulted to 0x0F or 0x10, so determine if it's 0x0D or 0x0E (ie, a 200-row mode)
// and then which one (ie, 320 wide or 640 wide).
//
- if (nCRTCVertTotal < 350) nMode = (fSEQDotClock? Video.MODES.EGA_320X200 : Video.MODES.EGA_640X200);
+ if (nCRTCVertTotal < 350) nMode = (fSEQDotClock? Video.MODE.EGA_320X200 : Video.MODE.EGA_640X200);
}
}
}
@@ -3695,10 +3725,10 @@ Video.prototype.checkMode = function(fForce)
* off, using a hard-coded mode value (0x25) that does NOT necessarily match the the CGA video mode currently in effect.
*/
if (!(card.modeReg & Card.CGA.MODE.GRAPHIC_SEL)) {
- nMode = ((card.modeReg & Card.CGA.MODE._80X25)? Video.MODES.CGA_80X25 : Video.MODES.CGA_40X25);
+ nMode = ((card.modeReg & Card.CGA.MODE._80X25)? Video.MODE.CGA_80X25 : Video.MODE.CGA_40X25);
if (card.modeReg & Card.CGA.MODE.BW_SEL) nMode -= 1;
} else {
- nMode = ((card.modeReg & Card.CGA.MODE.HIRES_BW)? Video.MODES.CGA_640X200 : Video.MODES.CGA_320X200_BW);
+ nMode = ((card.modeReg & Card.CGA.MODE.HIRES_BW)? Video.MODE.CGA_640X200 : Video.MODE.CGA_320X200_BW);
if (!(card.modeReg & Card.CGA.MODE.BW_SEL)) nMode -= 1;
}
}
@@ -3755,7 +3785,7 @@ Video.prototype.setMode = function(nMode, fForce)
* (MDA or CGA) don't allocate/manage their own memory buffer, but even then, it's still a good idea
* to always force this operation (eg, in case a switch setting changed the active video card).
*/
- var card = this.cardActive || (nMode == Video.MODES.MDA_80X25? this.cardMono : this.cardColor);
+ var card = this.cardActive || (nMode == Video.MODE.MDA_80X25? this.cardMono : this.cardColor);
if (card != this.cardActive || card.addrBuffer != this.addrBuffer || card.sizeBuffer != this.sizeBuffer) {
@@ -4527,7 +4557,7 @@ Video.prototype.outATC = function(port, bOut, addrFrom)
Video.prototype.inStatus0 = function(port, addrFrom)
{
var iBit = 3 - ((this.cardEGA.miscReg & Card.MISC.CLK_SELECT) >> 2); // this is the desired SW # (0-3)
- var bSWBit = (this.bEGASW & (1 << iBit)) << (Card.STATUS0.SWSENSE_SHIFT - iBit);
+ var bSWBit = (this.bEGASwitches & (1 << iBit)) << (Card.STATUS0.SWSENSE_SHIFT - iBit);
var b = ((this.cardEGA.status0 & ~Card.STATUS0.SWSENSE) | bSWBit);
/*
* TODO: Figure out where Card.STATUS0.FEAT bits should come from....