Preliminary groundwork for FPU support

This commit is contained in:
Jeff Parsons 2015-11-11 18:11:30 -08:00
commit 0c6db1df46
18 changed files with 756 additions and 173 deletions

View file

@ -190,7 +190,7 @@ B2 was not an actual 80386 stepping; it is a *pseudo-stepping* that provides a s
passes all 32-bit multiplication tests. passes all 32-bit multiplication tests.
As previously discussed, Windows 95 SETUP will refuse to install on any "A" or "B" stepping, but if it's already been As previously discussed, Windows 95 SETUP will refuse to install on any "A" or "B" stepping, but if it's already been
installed, it *will* start up. installed, it *will* start up on a B1 stepping.
PCjs stepping support is extremely limited at this point. Here's a summary: PCjs stepping support is extremely limited at this point. Here's a summary:

View file

@ -3,16 +3,17 @@
<machine id="ibm5160" class="pc" border="1" width="980px" pos="center" background="#FAEBD7"> <machine id="ibm5160" class="pc" border="1" width="980px" pos="center" background="#FAEBD7">
<name pos="center">IBM PC XT (Model 5160), CGA, 640K, 10Mb Drive</name> <name pos="center">IBM PC XT (Model 5160), CGA, 640K, 10Mb Drive</name>
<computer id="xt-cga-640k" name="IBM PC XT"/> <computer id="xt-cga-640k" name="IBM PC XT"/>
<cpu id="cpu8088" model="8088" autostart="true"/>
<fpu id="fpu8087" model="8087"/>
<ram id="ramLow" addr="0x00000" test="false" size="0xa0000" comment="0xa0000 (640Kb) size overrides SW1|ROM BIOS memory test has been disabled"/> <ram id="ramLow" addr="0x00000" test="false" size="0xa0000" comment="0xa0000 (640Kb) size overrides SW1|ROM BIOS memory test has been disabled"/>
<rom id="romHDC" addr="0xc8000" size="0x2000" file="/devices/pc/hdc/ibm-xebec-1982.json"/> <rom id="romHDC" addr="0xc8000" size="0x2000" file="/devices/pc/hdc/ibm-xebec-1982.json"/>
<rom id="romBASIC" addr="0xf6000" size="0x8000" file="/devices/pc/basic/ibm-basic-1.10.json"/> <rom id="romBASIC" addr="0xf6000" size="0x8000" file="/devices/pc/basic/ibm-basic-1.10.json"/>
<rom id="romBIOS" addr="0xfe000" size="0x2000" file="/devices/pc/bios/5160/1982-11-08.json"/> <rom id="romBIOS" addr="0xfe000" size="0x2000" file="/devices/pc/bios/5160/1982-11-08.json"/>
<video ref="/devices/pc/video/ibm/cga/ibm-cga-960-keygrid.xml"/> <video ref="/devices/pc/video/ibm/cga/ibm-cga-960-keygrid.xml"/>
<cpu id="cpu8088" model="8088" autostart="true"/>
<keyboard ref="/devices/pc/keyboard/keyboard-minimal.xml"/> <keyboard ref="/devices/pc/keyboard/keyboard-minimal.xml"/>
<debugger id="debugger"/> <debugger id="debugger"/>
<panel ref="/devices/pc/panel/wide.xml"/> <panel ref="/devices/pc/panel/wide.xml"/>
<fdc ref="/disks/pc/library.xml" width="320px"/> <fdc ref="/disks/pc/private/disks.xml" width="320px"/>
<chipset id="chipset" model="5160" sw1="01001001" pos="left" padleft="8px" padbottom="8px"> <chipset id="chipset" model="5160" sw1="01001001" pos="left" padleft="8px" padbottom="8px">
<control type="switches" label="SW1" binding="sw1" left="0px"/> <control type="switches" label="SW1" binding="sw1" left="0px"/>
<control type="description" binding="swdesc" left="0px"/> <control type="description" binding="swdesc" left="0px"/>

View file

@ -13,7 +13,7 @@
<fdc ref="/disks/pc/library.xml" automount=''/> <fdc ref="/disks/pc/library.xml" automount=''/>
<debugger id="debugger" messages="" commands=''/> <debugger id="debugger" messages="" commands=''/>
<panel ref="/devices/pc/panel/wide386.xml"/> <panel ref="/devices/pc/panel/wide386.xml"/>
<hdc id="hdcAT" type="at" drives='[{name:"68Mb Hard Disk",type:4,path:"http://static.pcjs.org/devices/pc/machine/compaq/deskpro386/vga/4096kb/WIN95-68Mb.json.gz"}]'/> <hdc id="hdcAT" type="at" drives='[{name:"68Mb Hard Disk",type:4,path:"http://static.pcjs.org/devices/pc/machine/compaq/deskpro386/vga/4096kb/WIN95-68Mb.json"}]'/>
<chipset id="chipset" model="deskpro386" floppies="[1200,1200]" monitor="vga"/> <chipset id="chipset" model="deskpro386" floppies="[1200,1200]" monitor="vga"/>
<serial id="com1" adapter="1"/> <serial id="com1" adapter="1"/>
<serial id="com2" adapter="2" binding="print"/> <serial id="com2" adapter="2" binding="print"/>

View file

@ -3,6 +3,7 @@
<control type="list" binding="listDrives"/> <control type="list" binding="listDrives"/>
<control type="list" binding="listDisks"> <control type="list" binding="listDisks">
<disk path="">None</disk> <disk path="">None</disk>
<manifest ref="/disks/pc/diags/ibm/2.20/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/dos/ibm/1.00/manifest.xml" disk="*"/> <manifest ref="/disks/pc/dos/ibm/1.00/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/dos/ibm/1.10/manifest.xml" disk="*"/> <manifest ref="/disks/pc/dos/ibm/1.10/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/dos/ibm/2.00/manifest.xml" disk="*"/> <manifest ref="/disks/pc/dos/ibm/2.00/manifest.xml" disk="*"/>
@ -47,8 +48,8 @@
<manifest ref="/disks/pc/apps/microsoft/word/3.1/manifest.xml" disk="*"/> <manifest ref="/disks/pc/apps/microsoft/word/3.1/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/apps/microsoft/word/5.0/manifest.xml" disk="*"/> <manifest ref="/disks/pc/apps/microsoft/word/5.0/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/apps/microsoft/winword/2.0c/manifest.xml" disk="*"/> <manifest ref="/disks/pc/apps/microsoft/winword/2.0c/manifest.xml" disk="*"/>
<disk path="/disks/pc/games/id/wolf3d/1WOLF14.json">Wolfenstein 3D (v1.4)</disk> <manifest ref="/disks/pc/tools/borland/pascal/3.00b/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/games/microsoft/flightsim/manifest.xml" disk="*"/> <manifest ref="/disks/pc/tools/borland/pascal/3.01a/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/tools/microsoft/basic/manifest.xml" disk="*"/> <manifest ref="/disks/pc/tools/microsoft/basic/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/tools/microsoft/c/4.00/manifest.xml" disk="*"/> <manifest ref="/disks/pc/tools/microsoft/c/4.00/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/tools/microsoft/masm/4.00/manifest.xml" disk="*"/> <manifest ref="/disks/pc/tools/microsoft/masm/4.00/manifest.xml" disk="*"/>
@ -58,6 +59,8 @@
<manifest ref="/disks/pc/tools/microsoft/windows/sdk/1.04/manifest.xml" disk="*"/> <manifest ref="/disks/pc/tools/microsoft/windows/sdk/1.04/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/tools/microsoft/windows/sdk/2.03/manifest.xml" disk="*"/> <manifest ref="/disks/pc/tools/microsoft/windows/sdk/2.03/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/tools/microsoft/windows/sdk/3.00/manifest.xml" disk="*"/> <manifest ref="/disks/pc/tools/microsoft/windows/sdk/3.00/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/games/microsoft/flightsim/manifest.xml" disk="*"/>
<disk path="/disks/pc/games/id/wolf3d/1WOLF14.json">Wolfenstein 3D (v1.4)</disk>
<manifest ref="/apps/pc/1981/visicalc/manifest.xml" disk="disk01"/> <manifest ref="/apps/pc/1981/visicalc/manifest.xml" disk="disk01"/>
<manifest ref="/apps/pc/1982/esuite/manifest.xml" disk="disk01"/> <manifest ref="/apps/pc/1982/esuite/manifest.xml" disk="disk01"/>
<manifest ref="/apps/pc/1985/rogue/manifest.xml" disk="disk01"/> <manifest ref="/apps/pc/1985/rogue/manifest.xml" disk="disk01"/>

View file

@ -1,8 +0,0 @@
Turbo Pascal 3.0
---
[Manifest](manifest.xml) updated using:
diskdump --disk=private/TURBO30.img --format=json --output=TURBO30.json --manifest
The original diskettes are not part of the repository/website.

File diff suppressed because one or more lines are too long

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/versions/pcjs/1.20.1/manifest.xsl"?>
<manifest type="software">
<title>Turbo Pascal</title>
<disk id="disk01" size="368640" chs="40:2:9" img="static/TURBO30.img" href="/disks/pc/tools/borland/pascal/3.0/TURBO30.json" md5="b1243614e885cb2a16047cff1adcb00b" md5json="672b78ada3c22756676b71fdf83c23af">
<name>Turbo Pascal 3.0</name>
</disk>
</manifest>

View file

@ -30,18 +30,19 @@ At the time of this writing, the recommended order is:
* [shared/weblib.js](../../shared/lib/weblib.js) * [shared/weblib.js](../../shared/lib/weblib.js)
* [shared/component.js](../../shared/lib/component.js) * [shared/component.js](../../shared/lib/component.js)
* [pcjs/defines.js](defines.js) * [pcjs/defines.js](defines.js)
* [pcjs/x86.js](x86.js)
* [pcjs/interrupts.js](interrupts.js) * [pcjs/interrupts.js](interrupts.js)
* [pcjs/messages.js](messages.js) * [pcjs/messages.js](messages.js)
* [pcjs/panel.js](panel.js) * [pcjs/panel.js](panel.js)
* [pcjs/bus.js](bus.js) * [pcjs/bus.js](bus.js)
* [pcjs/memory.js](memory.js) * [pcjs/memory.js](memory.js)
* [pcjs/cpu.js](cpu.js) * [pcjs/cpu.js](cpu.js)
* [pcjs/x86.js](x86.js)
* [pcjs/x86seg.js](x86seg.js) * [pcjs/x86seg.js](x86seg.js)
* [pcjs/x86cpu.js](x86cpu.js) * [pcjs/x86cpu.js](x86cpu.js)
* [pcjs/x86cpu.js](x86fpu.js)
* [pcjs/x86func.js](x86func.js)
* [pcjs/x86ops.js](x86ops.js) * [pcjs/x86ops.js](x86ops.js)
* [pcjs/x86op0f.js](x86op0f.js) * [pcjs/x86op0f.js](x86op0f.js)
* [pcjs/x86func.js](x86func.js)
* [pcjs/x86modb.js](x86modb.js) * [pcjs/x86modb.js](x86modb.js)
* [pcjs/x86modw.js](x86modw.js) * [pcjs/x86modw.js](x86modw.js)
* [pcjs/x86modb16.js](x86modb16.js) * [pcjs/x86modb16.js](x86modb16.js)

View file

@ -168,7 +168,7 @@ function ChipSet(parmsChipSet)
* a separate question. FYI, with only 16Kb, the ROM BIOS will still try to boot, and fail miserably. * a separate question. FYI, with only 16Kb, the ROM BIOS will still try to boot, and fail miserably.
*/ */
this.sw1Init = 0; this.sw1Init = 0;
var sw1 = parmsChipSet['sw1']; var sw1 = parmsChipSet[ChipSet.CONTROLS.SW1];
if (sw1) { if (sw1) {
this.sw1Init = this.parseSwitches(sw1, ChipSet.PPI_SW.MEMORY.X4 | ChipSet.PPI_SW.MONITOR.MONO); this.sw1Init = this.parseSwitches(sw1, ChipSet.PPI_SW.MEMORY.X4 | ChipSet.PPI_SW.MONITOR.MONO);
} else { } else {
@ -201,7 +201,9 @@ function ChipSet(parmsChipSet)
* NOTE: If you use the "size" parameter, you will not be able to dynamically alter the memory configuration; * NOTE: If you use the "size" parameter, you will not be able to dynamically alter the memory configuration;
* the RAM component will ignore any changes to SW1. * the RAM component will ignore any changes to SW1.
*/ */
this.sw2Init = this.parseSwitches(parmsChipSet['sw2'] || "11110000", 0); this.sw2Init = this.parseSwitches(parmsChipSet[ChipSet.CONTROLS.SW2] || "11110000", 0);
this.sCellClass = PCJSCLASS + "-bitCell";
/* /*
* The SW1 memory setting is actually just a multiplier: it's multiplied by 16Kb on a MODEL_5150, 64Kb otherwise. * The SW1 memory setting is actually just a multiplier: it's multiplied by 16Kb on a MODEL_5150, 64Kb otherwise.
@ -288,6 +290,12 @@ ChipSet.MODELS = {
"deskpro386": ChipSet.MODEL_DESKPRO386 "deskpro386": ChipSet.MODEL_DESKPRO386
}; };
ChipSet.CONTROLS = {
SW1: "sw1",
SW2: "sw2",
SWDESC: "swdesc"
};
/* /*
* Values returned by ChipSet.getSWVideoMonitor() * Values returned by ChipSet.getSWVideoMonitor()
*/ */
@ -983,6 +991,8 @@ ChipSet.COPROC = { // TODO: Define a variable for this
}; };
/** /**
* setBinding(sHTMLType, sBinding, control)
*
* @this {ChipSet} * @this {ChipSet}
* @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas") * @param {string|null} sHTMLType is the type of the HTML control (eg, "button", "list", "text", "submit", "textarea", "canvas")
* @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "sw1") * @param {string} sBinding is the value of the 'binding' parameter stored in the HTML control's "data-value" attribute (eg, "sw1")
@ -992,31 +1002,43 @@ ChipSet.COPROC = { // TODO: Define a variable for this
ChipSet.prototype.setBinding = function(sHTMLType, sBinding, control) ChipSet.prototype.setBinding = function(sHTMLType, sBinding, control)
{ {
switch (sBinding) { switch (sBinding) {
case "sw1": case ChipSet.CONTROLS.SW1:
this.bindings[sBinding] = control;
this.addSwitches(sBinding, 8, this.sw1Init, {
0: (this.model == ChipSet.MODEL_5150? "Bootable Floppy Drive" : "Loop on POST"),
/*
* NOTE: Both the Aug 1981 and the Apr 1984 IBM 5150 Technical Reference Manuals list SW1-2 as "RESERVED", but
* contemporary articles discussing 8087 support in early PCs all indicate that switch SW1-2 must be set to the
* OFF position if a coprocessor is installed.
*
* The 1981 5150 TechRef doesn't mention 8087 support at all, whereas the 1984 5150 TechRef discusses it in
* a fair bit of detail, including the fact that 8087 exceptions generate an NMI (despite Intel's warning in their
* "iAPX 86,88 User's Manual", on page S-27, that "[t]he 8087 should not be tied to the CPU's NMI (non-maskable
* interrupt) line.")
*
* In light of this, we have effectively disabled the "Reserved" option below, since model will never be < 5150.
*/
1: (this.model < ChipSet.MODEL_5150? "Reserved" : "Coprocessor"),
2: "Base Memory Size", // up to 64Kb on a MODEL_5150, 256Kb on a MODEL_5160
4: "Monitor Type",
6: "Number of Floppy Drives"
});
return true;
case ChipSet.CONTROLS.SW2:
if (this.model == ChipSet.MODEL_5150) {
this.bindings[sBinding] = control; this.bindings[sBinding] = control;
this.addSwitches(sBinding, control, 8, this.sw1Init, { this.addSwitches(sBinding, 8, this.sw2Init, {
0: (this.model == ChipSet.MODEL_5150? "Bootable Floppy Drive" : "Loop on POST"), 0: "Expansion Memory Size", // up to 480Kb, which, when combined with 64Kb of MODEL_5150 base memory, gives a maximum of 544Kb
1: (this.model == ChipSet.MODEL_5150? "Reserved" : "Coprocessor"), 4: "Reserved"
2: "Base Memory Size", // up to 64Kb on a MODEL_5150, 256Kb on a MODEL_5160
4: "Monitor Type",
6: "Number of Floppy Drives"
}); });
return true; return true;
case "sw2": }
if (this.model == ChipSet.MODEL_5150) { break;
this.bindings[sBinding] = control; case ChipSet.CONTROLS.SWDESC:
this.addSwitches(sBinding, control, 8, this.sw2Init, { this.bindings[sBinding] = control;
0: "Expansion Memory Size", // up to 480Kb, which, when combined with 64Kb of MODEL_5150 base memory, gives a maximum of 544Kb return true;
4: "Reserved" default:
}); break;
return true;
}
break;
case "swdesc":
this.bindings[sBinding] = control;
return true;
default:
break;
} }
return false; return false;
}; };
@ -1069,6 +1091,9 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg)
} }
cpu.addIntNotify(Interrupts.RTC, this.intBIOSRTC.bind(this)); cpu.addIntNotify(Interrupts.RTC, this.intBIOSRTC.bind(this));
} }
if (cpu.fpu) {
this.sw1Init |= ChipSet.PPI_SW.COPROC;
}
}; };
/** /**
@ -1119,7 +1144,9 @@ ChipSet.prototype.reset = function(fHard)
var i; var i;
this.sw1 = this.sw1Init; this.sw1 = this.sw1Init;
this.sw2 = this.sw2Init; this.sw2 = this.sw2Init;
this.updateSwitchDesc(); this.updateSwitches(ChipSet.CONTROLS.SW1, this.sw1);
this.updateSwitches(ChipSet.CONTROLS.SW2, this.sw2);
this.updateSwitchDescriptions();
/* /*
* DMA (Direct Memory Access) Controller initialization * DMA (Direct Memory Access) Controller initialization
@ -2196,45 +2223,91 @@ ChipSet.prototype.getSWVideoMonitor = function(fInit)
}; };
/** /**
* addSwitches(s, control, n, v, oTips) * addSwitches(sBinding, n, v, oTips)
* *
* @this {ChipSet} * @this {ChipSet}
* @param {string} s is the name of the control * @param {string} sBinding is the name of the control
* @param {Object} control is the HTML control DOM object
* @param {number} n is the number of switches to add * @param {number} n is the number of switches to add
* @param {number} v contains the current value(s) of the switches * @param {number} v contains the current value(s) of the switches
* @param {Object} oTips contains tooltips for the various cells * @param {Object} oTips contains tooltips for the various cells
*/ */
ChipSet.prototype.addSwitches = function(s, control, n, v, oTips) ChipSet.prototype.addSwitches = function(sBinding, n, v, oTips)
{ {
var sHTML = ""; var sHTML = "";
var sCellClass = PCJSCLASS + "-bitCell"; var control = this.bindings[sBinding];
for (var i = 1; i <= n; i++) { for (var i = 1; i <= n; i++) {
var sCellClasses = sCellClass; var sCellClasses = this.sCellClass;
if (!i) sCellClasses += " " + PCJSCLASS + "-bitCellLeft"; if (!i) sCellClasses += " " + this.sCellClass + "Left";
var sCellID = s + "-" + i; var sCellID = sBinding + "-" + i;
sHTML += "<div id=\"" + sCellID + "\" class=\"" + sCellClasses + "\" data-value=\"0\">" + i + "</div>\n"; sHTML += "<div id=\"" + sCellID + "\" class=\"" + sCellClasses + "\" data-value=\"0\">" + i + "</div>\n";
} }
control.innerHTML = sHTML; control.innerHTML = sHTML;
var aeCells = Component.getElementsByClass(control, sCellClass); this.updateSwitches(sBinding, v, oTips);
var sTip = null; };
for (i = 0; i < aeCells.length; i++) {
if (oTips != null && oTips[i] != null) { /**
sTip = oTips[i]; * updateSwitches(sBinding, v, oTips)
*
* @this {ChipSet}
* @param {string} sBinding is the name of the control
* @param {number} v contains the current value(s) of the switches
* @param {Object} [oTips] contains tooltips for the various cells
*/
ChipSet.prototype.updateSwitches = function(sBinding, v, oTips)
{
var control = this.bindings[sBinding];
if (control) {
var aeCells = Component.getElementsByClass(control, this.sCellClass);
for (var i = 0; i < aeCells.length; i++) {
var sTip = null;
if (oTips != null && oTips[i] != null) {
sTip = oTips[i];
}
if (sTip) aeCells[i].setAttribute("title", sTip);
this.setSwitch(aeCells[i], (v & (0x1 << i))? false : true);
aeCells[i].onclick = function(chipset, eSwitch) {
/*
* If we defined the onclick handler below as "function(e)" instead of simply "function()", then we could
* also receive an event object (e); however, IE reportedly requires that we examine a global (window.event)
* instead. If that's true, and if we ever care to get more details about the click event, then we might
* have to worry about that (eg, define a local var: "var event = window.event || e").
*/
return function onClickSwitch() {
chipset.toggleSwitch(eSwitch);
};
}(this, aeCells[i]);
} }
if (sTip) aeCells[i].setAttribute("title", sTip); }
this.setSwitch(aeCells[i], (v & (0x1 << i))? false : true); };
aeCells[i].onclick = function(chipset, eSwitch) {
/* /**
* If we defined the onclick handler below as "function(e)" instead of simply "function()", then we could * updateSwitchDescriptions()
* also receive an event object (e); however, IE reportedly requires that we examine a global (window.event) *
* instead. If that's true, and if we ever care to get more details about the click event, then we might * @this {ChipSet}
* have to worry about that (eg, define a local var: "var event = window.event || e"). */
*/ ChipSet.prototype.updateSwitchDescriptions = function()
return function onClickSwitch() { {
chipset.toggleSwitch(eSwitch); var controlDesc = this.bindings[ChipSet.CONTROLS.SWDESC];
}; if (controlDesc != null) {
}(this, aeCells[i]); var sText = "";
/*
* TODO: Monitor type 0 used to be "None" (ie, "No Monitor"), which was correct in a pre-EGA world,
* but in the post-EGA world, it depends. We should ask the Video component for a definitive answer.
*/
var asMonitorTypes = {
0: "Enhanced Color",
1: "TV",
2: "Color",
3: "Monochrome"
};
sText += this.getSWMemorySize(true) + "Kb";
sText += ", " + ((this.sw1 & ChipSet.PPI_SW.COPROC)? "" : "No ") + "Coprocessor";
sText += ", " + asMonitorTypes[this.getSWVideoMonitor(true)] + " Monitor";
sText += ", " + this.getSWFloppyDrives(true) + " Floppy Drives";
if (this.sw1 != null && this.sw1 != this.sw1Init || this.sw2 != null && this.sw2 != this.sw2Init) {
sText += " (Reset required)";
}
controlDesc.textContent = sText;
} }
}; };
@ -2278,48 +2351,16 @@ ChipSet.prototype.toggleSwitch = function(control)
var asParts = sID.split("-"); var asParts = sID.split("-");
var b = (0x1 << (+asParts[1] - 1)); var b = (0x1 << (+asParts[1] - 1));
switch (asParts[0]) { switch (asParts[0]) {
case "sw1": case ChipSet.CONTROLS.SW1:
this.sw1Init = (this.sw1Init & ~b) | (f? 0 : b); this.sw1Init = (this.sw1Init & ~b) | (f? 0 : b);
break; break;
case "sw2": case ChipSet.CONTROLS.SW2:
this.sw2Init = (this.sw2Init & ~b) | (f? 0 : b); this.sw2Init = (this.sw2Init & ~b) | (f? 0 : b);
break; break;
default: default:
break; break;
} }
this.updateSwitchDesc(); this.updateSwitchDescriptions();
};
/**
* updateSwitchDesc()
*
* @this {ChipSet}
*/
ChipSet.prototype.updateSwitchDesc = function()
{
var controlDesc = this.bindings["swdesc"];
/*
* TODO: Monitor type 0 used to be "No" (as in "No Monitor"), which was correct in the pre-EGA world,
* but in the post-EGA world, it depends. We could ask the Video component for a definitive answer, but
* but what we print here isn't that critical, because most people won't bother with a Control Panel,
* which is really the only beneficiary of this code.
*/
var asMonitorTypes = {
0: "Enhanced Color",
1: "TV",
2: "Color",
3: "Monochrome"
};
if (controlDesc != null) {
var sText = "";
sText += this.getSWMemorySize(true) + "Kb";
sText += ", " + asMonitorTypes[this.getSWVideoMonitor(true)] + " Monitor";
sText += ", " + this.getSWFloppyDrives(true) + " Floppy Drives";
if (this.sw1 != null && this.sw1 != this.sw1Init || this.sw2 != null && this.sw2 != this.sw2Init) {
sText += " (Reset required)";
}
controlDesc.textContent = sText;
}
}; };
/** /**
@ -5284,7 +5325,7 @@ ChipSet.init = function()
var parmsChipSet = Component.getComponentParms(eChipSet); var parmsChipSet = Component.getComponentParms(eChipSet);
var chipset = new ChipSet(parmsChipSet); var chipset = new ChipSet(parmsChipSet);
Component.bindComponentControls(chipset, eChipSet, PCJSCLASS); Component.bindComponentControls(chipset, eChipSet, PCJSCLASS);
chipset.updateSwitchDesc(); chipset.updateSwitchDescriptions();
} }
}; };

View file

@ -138,8 +138,7 @@ function CPU(parmsCPU, nCyclesDefault)
*/ */
this.aVideo = []; this.aVideo = [];
var cpu = this; this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); };
this.onRunTimeout = function onRunTimeout() { cpu.runCPU(); };
this.setReady(); this.setReady();
} }
@ -179,9 +178,10 @@ CPU.STATUS_UPDATES_PER_SECOND = 2;
*/ */
CPU.prototype.initBus = function(cmp, bus, cpu, dbg) CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
{ {
this.cmp = cmp;
this.bus = bus; this.bus = bus;
this.dbg = dbg; this.dbg = dbg;
this.cmp = cmp;
/* /*
* Attach the Video component to the CPU, so that the CPU can periodically update * Attach the Video component to the CPU, so that the CPU can periodically update
* the video display via updateVideo(), as cycles permit. * the video display via updateVideo(), as cycles permit.
@ -189,13 +189,17 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
for (var video = null; (video = cmp.getMachineComponent("Video", video));) { for (var video = null; (video = cmp.getMachineComponent("Video", video));) {
this.aVideo.push(video); this.aVideo.push(video);
} }
/* /*
* Attach the ChipSet component to the CPU, so that it can obtain the IDT vector number of * Attach the ChipSet component to the CPU so that it can obtain the IDT vector number
* pending hardware interrupts, in response to ChipSet's updateINTR() notifications. * of pending hardware interrupts in response to the ChipSet's updateINTR() notifications.
* *
* We must also call chipset.updateAllTimers() periodically; stepCPU() takes care of that. * We must also call chipset.updateAllTimers() periodically; stepCPU() takes care of that.
*/ */
this.chipset = cmp.getMachineComponent("ChipSet"); this.chipset = cmp.getMachineComponent("ChipSet");
this.fpu = cmp.getMachineComponent("FPU");
this.setReady(); this.setReady();
}; };

View file

@ -275,7 +275,7 @@ if (DEBUGGER) {
Debugger.COMMANDS = { Debugger.COMMANDS = {
'?': "help/print", '?': "help/print",
'a [#]': "assemble", 'a [#]': "assemble", // TODO: Implement this command someday
'b [#]': "breakpoint", // multiple variations (use b? to list them) 'b [#]': "breakpoint", // multiple variations (use b? to list them)
'c': "clear output", 'c': "clear output",
'd [#]': "dump memory", // additional syntax: d [#] [l#], where l# is a number of bytes to dump 'd [#]': "dump memory", // additional syntax: d [#] [l#], where l# is a number of bytes to dump
@ -320,67 +320,113 @@ if (DEBUGGER) {
}; };
/* /*
* Instruction ordinals * CPU instruction ordinals
*
* Note that individual instructions end with ordinal 162 and instruction groups begin with ordinal 163;
* the disassembler knows it's dealing with a group whenever the ordinal is not a valid index into INS_NAMES.
*/ */
Debugger.INS = { Debugger.INS = {
NONE: 0, AAA: 1, AAD: 2, AAM: 3, AAS: 4, ADC: 5, ADD: 6, AND: 7, NONE: 0, AAA: 1, AAD: 2, AAM: 3, AAS: 4, ADC: 5, ADD: 6, AND: 7,
ARPL: 8, AS: 9, BOUND: 10, BSF: 11, BSR: 12, BT: 13, BTC: 14, BTR: 15, ARPL: 8, AS: 9, BOUND: 10, BSF: 11, BSR: 12, BT: 13, BTC: 14, BTR: 15,
BTS: 16, CALL: 17, CBW: 18, CLC: 19, CLD: 20, CLI: 21, CLTS: 22, CMC: 23, BTS: 16, CALL: 17, CBW: 18, CLC: 19, CLD: 20, CLI: 21, CLTS: 22, CMC: 23,
CMP: 24, CMPSB: 25, CMPSW: 26, CS: 27, CWD: 28, DAA: 29, DAS: 30, DEC: 31, CMP: 24, CMPSB: 25, CMPSW: 26, CS: 27, CWD: 28, DAA: 29, DAS: 30, DEC: 31,
DIV: 32, DS: 33, ENTER: 34, ES: 35, ESC: 36, FADD: 37, FBLD: 38, FBSTP: 39, DIV: 32, DS: 33, ENTER: 34, ES: 35, ESC: 36, FS: 37, GS: 38, HLT: 39,
FCOM: 40, FCOMP: 41, FDIV: 42, FDIVR: 43, FIADD: 44, FICOM: 45, FICOMP: 46, FIDIV: 47, IBTS: 40, IDIV: 41, IMUL: 42, IN: 43, INC: 44, INS: 45, INT: 46, INT3: 47,
FIDIVR: 48, FILD: 49, FIMUL: 50, FIST: 51, FISTP: 52, FISUB: 53, FISUBR: 54, FLD: 55, INTO: 48, IRET: 49, JBE: 50, JC: 51, JCXZ: 52, JG: 53, JGE: 54, JL: 55,
FLDCW: 56, FLDENV: 57, FMUL: 58, FNSAVE: 59, FNSTCW: 60, FNSTENV:61, FNSTSW: 62, FRSTOR: 63, JLE: 56, JMP: 57, JA: 58, JNC: 59, JNO: 60, JNP: 61, JNS: 62, JNZ: 63,
FS: 64, FST: 65, FSTP: 66, FSUB: 67, FSUBR: 68, GS: 69, HLT: 70, IBTS: 71, JO: 64, JP: 65, JS: 66, JZ: 67, LAHF: 68, LAR: 69, LDS: 70, LEA: 71,
IDIV: 72, IMUL: 73, IN: 74, INC: 75, INS: 76, INT: 77, INT3: 78, INTO: 79, LEAVE: 72, LES: 73, LFS: 74, LGDT: 75, LGS: 76, LIDT: 77, LLDT: 78, LMSW: 79,
IRET: 80, JBE: 81, JC: 82, JCXZ: 83, JG: 84, JGE: 85, JL: 86, JLE: 87, LOADALL:80, LOCK: 81, LODSB: 82, LODSW: 83, LOOP: 84, LOOPNZ: 85, LOOPZ: 86, LSL: 87,
JMP: 88, JA: 89, JNC: 90, JNO: 91, JNP: 92, JNS: 93, JNZ: 94, JO: 95, LSS: 88, LTR: 89, MOV: 90, MOVSB: 91, MOVSW: 92, MOVSX: 93, MOVZX: 94, MUL: 95,
JP: 96, JS: 97, JZ: 98, LAHF: 99, LAR: 100, LDS: 101, LEA: 102, LEAVE: 103, NEG: 96, NOP: 97, NOT: 98, OR: 99, OS: 100, OUT: 101, OUTS: 102, POP: 103,
LES: 104, LFS: 105, LGDT: 106, LGS: 107, LIDT: 108, LLDT: 109, LMSW: 110, LOADALL:111, POPA: 104, POPF: 105, PUSHF: 106, PUSHA: 107, PUSH: 108, RCL: 109, RCR: 110, REPNZ: 111,
LOCK: 112, LODSB: 113, LODSW: 114, LOOP: 115, LOOPNZ: 116, LOOPZ: 117, LSL: 118, LSS: 119, REPZ: 112, RET: 113, RETF: 114, ROL: 115, ROR: 116, SAHF: 117, SALC: 118, SAR: 119,
LTR: 120, MOV: 121, MOVSB: 122, MOVSW: 123, MOVSX: 124, MOVZX: 125, MUL: 126, NEG: 127, SBB: 120, SCASB: 121, SCASW: 122, SETBE: 123, SETC: 124, SETG: 125, SETGE: 126, SETL: 127,
NOP: 128, NOT: 129, OR: 130, OS: 131, OUT: 132, OUTS: 133, POP: 134, POPA: 135, SETLE: 128, SETNBE: 129, SETNC: 130, SETNO: 131, SETNP: 132, SETNS: 133, SETNZ: 134, SETO: 135,
POPF: 136, PUSHF: 137, PUSHA: 138, PUSH: 139, RCL: 140, RCR: 141, REPNZ: 142, REPZ: 143, SETP: 136, SETS: 137, SETZ: 138, SGDT: 139, SHL: 140, SHLD: 141, SHR: 142, SHRD: 143,
RET: 144, RETF: 145, ROL: 146, ROR: 147, SAHF: 148, SALC: 149, SAR: 150, SBB: 151, SIDT: 144, SLDT: 145, SMSW: 146, SS: 147, STC: 148, STD: 149, STI: 150, STOSB: 151,
SCASB: 152, SCASW: 153, SETBE: 154, SETC: 155, SETG: 156, SETGE: 157, SETL: 158, SETLE: 159, STOSW: 152, STR: 153, SUB: 154, TEST: 155, VERR: 156, VERW: 157, WAIT: 158, XBTS: 159,
SETNBE: 160, SETNC: 161, SETNO: 162, SETNP: 163, SETNS: 164, SETNZ: 165, SETO: 166, SETP: 167, XCHG: 160, XLAT: 161, XOR: 162, GRP1B: 163, GRP1W: 164, GRP1SW: 165, GRP2B: 166, GRP2W: 167,
SETS: 168, SETZ: 169, SGDT: 170, SHL: 171, SHLD: 172, SHR: 173, SHRD: 174, SIDT: 175, GRP2B1: 168, GRP2W1: 169, GRP2BC: 170, GRP2WC: 171, GRP3B: 172, GRP3W: 173, GRP4B: 174, GRP4W: 175,
SLDT: 176, SMSW: 177, SS: 178, STC: 179, STD: 180, STI: 181, STOSB: 182, STOSW: 183, OP0F: 176, GRP6: 177, GRP7: 178, GRP8: 179
STR: 184, SUB: 185, TEST: 186, VERR: 187, VERW: 188, WAIT: 189, XBTS: 190, XCHG: 191,
XLAT: 192, XOR: 193, GRP1B: 194, GRP1W: 195, GRP1SW: 196, GRP2B: 197, GRP2W: 198, GRP2B1: 199,
GRP2W1: 200, GRP2BC: 201, GRP2WC: 202, GRP3B: 203, GRP3W: 204, GRP4B: 205, GRP4W: 206, OP0F: 207,
GRP6: 208, GRP7: 209, GRP8: 210
}; };
/* /*
* Instruction names (mnemonics), indexed by instruction ordinal (above) * CPU instruction names (mnemonics), indexed by CPU instruction ordinal (above)
*/ */
Debugger.INS_NAMES = [ Debugger.INS_NAMES = [
"INVALID","AAA", "AAD", "AAM", "AAS", "ADC", "ADD", "AND", "INVALID","AAA", "AAD", "AAM", "AAS", "ADC", "ADD", "AND",
"ARPL", "AS:", "BOUND", "BSF", "BSR", "BT", "BTC", "BTR", "ARPL", "AS:", "BOUND", "BSF", "BSR", "BT", "BTC", "BTR",
"BTS", "CALL", "CBW", "CLC", "CLD", "CLI", "CLTS", "CMC", "BTS", "CALL", "CBW", "CLC", "CLD", "CLI", "CLTS", "CMC",
"CMP", "CMPSB", "CMPSW", "CS:", "CWD", "DAA", "DAS", "DEC", "CMP", "CMPSB", "CMPSW", "CS:", "CWD", "DAA", "DAS", "DEC",
"DIV", "DS:", "ENTER", "ES:", "ESC", "FADD", "FBLD", "FBSTP", "DIV", "DS:", "ENTER", "ES:", "ESC", "FS:", "GS:", "HLT",
"FCOM", "FCOMP", "FDIV", "FDIVR", "FIADD", "FICOM", "FICOMP", "FIDIV", "IBTS", "IDIV", "IMUL", "IN", "INC", "INS", "INT", "INT3",
"FIDIVR", "FILD", "FIMUL", "FIST", "FISTP", "FISUB", "FISUBR", "FLD", "INTO", "IRET", "JBE", "JC", "JCXZ", "JG", "JGE", "JL",
"FLDCW", "FLDENV", "FMUL", "FNSAVE", "FNSTCW", "FNSTENV","FNSTSW", "FRSTOR", "JLE", "JMP", "JA", "JNC", "JNO", "JNP", "JNS", "JNZ",
"FS:", "FST", "FSTP", "FSUB", "FSUBR", "GS:", "HLT", "IBTS", "JO", "JP", "JS", "JZ", "LAHF", "LAR", "LDS", "LEA",
"IDIV", "IMUL", "IN", "INC", "INS", "INT", "INT3", "INTO", "LEAVE", "LES", "LFS", "LGDT", "LGS", "LIDT", "LLDT", "LMSW",
"IRET", "JBE", "JC", "JCXZ", "JG", "JGE", "JL", "JLE", "LOADALL","LOCK", "LODSB", "LODSW", "LOOP", "LOOPNZ", "LOOPZ", "LSL",
"JMP", "JA", "JNC", "JNO", "JNP", "JNS", "JNZ", "JO", "LSS", "LTR", "MOV", "MOVSB", "MOVSW", "MOVSX", "MOVZX", "MUL",
"JP", "JS", "JZ", "LAHF", "LAR", "LDS", "LEA", "LEAVE", "NEG", "NOP", "NOT", "OR", "OS:", "OUT", "OUTS", "POP",
"LES", "LFS", "LGDT", "LGS", "LIDT", "LLDT", "LMSW", "LOADALL", "POPA", "POPF", "PUSHF", "PUSHA", "PUSH", "RCL", "RCR", "REPNZ",
"LOCK", "LODSB", "LODSW", "LOOP", "LOOPNZ", "LOOPZ", "LSL", "LSS", "REPZ", "RET", "RETF", "ROL", "ROR", "SAHF", "SALC", "SAR",
"LTR", "MOV", "MOVSB", "MOVSW", "MOVSX", "MOVZX", "MUL", "NEG", "SBB", "SCASB", "SCASW", "SETBE", "SETC", "SETG", "SETGE", "SETL",
"NOP", "NOT", "OR", "OS:", "OUT", "OUTS", "POP", "POPA", "SETLE", "SETNBE", "SETNC", "SETNO", "SETNP", "SETNS", "SETNZ", "SETO",
"POPF", "PUSHF", "PUSHA", "PUSH", "RCL", "RCR", "REPNZ", "REPZ", "SETP", "SETS", "SETZ", "SGDT", "SHL", "SHLD", "SHR", "SHRD",
"RET", "RETF", "ROL", "ROR", "SAHF", "SALC", "SAR", "SBB", "SIDT", "SLDT", "SMSW", "SS:", "STC", "STD", "STI", "STOSB",
"SCASB", "SCASW", "SETBE", "SETC", "SETG", "SETGE", "SETL", "SETLE", "STOSW", "STR", "SUB", "TEST", "VERR", "VERW", "WAIT", "XBTS",
"SETNBE", "SETNC", "SETNO", "SETNP", "SETNS", "SETNZ", "SETO", "SETP", "XCHG", "XLAT", "XOR"
"SETS", "SETZ", "SGDT", "SHL", "SHLD", "SHR", "SHRD", "SIDT", ];
"SLDT", "SMSW", "SS:", "STC", "STD", "STI", "STOSB", "STOSW",
"STR", "SUB", "TEST", "VERR", "VERW", "WAIT", "XBTS", "XCHG", /*
"XLAT", "XOR" * FPU instruction ordinals
*
* Unlike CPU instruction ordinals, these are not organized alphabetically (which I did only for the
* sake of tidiness), but rather by functionality; ie:
*
* 0-3: real transfers
* 4-6: integer transfers
* 7-8: packed decimal transfers
* 9-11: addition
* 12-17: subtraction
* 18-20: multiplication
* 21-26: division
* 27-33: other
* 34-40: comparisons
* 41-45: transcendental
* 46-52: constants
* 53-76: coprocessor control
*
* Also, unlike the CPU instructions, there is no NONE ("INVALID") instruction; if an ESC instruction
* can't be decoded as a valid FPU instruction, then it should remain an ESC instruction.
*/
Debugger.FINS = {
FLD: 0, FST: 1, FSTP: 2, FXCH: 3, FILD: 4, FIST: 5, FISTP: 6, FBLD: 7,
FBSTP: 8, FADD: 9, FADDP: 10, FIADD: 11, FSUB: 12, FSUBP: 13, FISUB: 14, FSUBR: 15,
FSUBRP: 16, FISUBR: 17, FMUL: 18, FMULP: 19, FIMUL: 20, FDIV: 21, FDIVP: 22, FIDIV: 23,
FDIVR: 24, FDIVRP: 25, FIDIVR: 26, FSQRT: 27, FSCALE: 28, FPREM: 29, FRNDINT:30, FXTRACT:31,
FABS: 32, FCHS: 33, FCOM: 34, FCOMP: 35, FCOMPP: 36, FICOM: 37, FICOMP: 38, FTST: 39,
FXAM: 40, FPTAN: 41, FPATAN: 42, F2XM1: 43, FYL2X: 44, FYL2XP1:45, FLDZ: 46, FLD1: 47,
FLDPI: 48, FLDL2T: 49, FLDL2E: 50, FLDLG2: 51, FLDLN2: 52, FINIT: 53, FNINIT: 54, FDISI: 55,
FNDISI: 56, FENI: 57, FNENI: 58, FLDCW: 59, FSTCW: 60, FNSTCW: 61, FSTSW: 62, FNSTSW: 63,
FCLEX: 64, FNCLEX: 65, FSTENV: 66, FNSTENV:67, FLDENV: 68, FSAVE: 69, FNSAVE: 70, FRSTOR: 71,
FINCSTP:72, FDECSTP:73, FFREE: 74, FNOP: 75, FWAIT: 76
};
/*
* FPU instruction names (mnemonics), indexed by FPU instruction ordinal (above)
*/
Debugger.FINS_NAMES = [
"FLD", "FST", "FSTP", "FXCH", "FILD", "FIST", "FISTP", "FBLD",
"FBSTP", "FADD", "FADDP", "FIADD", "FSUB", "FSUBP", "FISUB", "FSUBR",
"FSUBRP", "FISUBR", "FMUL", "FMULP", "FIMUL", "FDIV", "FDIVP", "FIDIV",
"FDIVR", "FDIVRP", "FIDIVR", "FSQRT", "FSCALE", "FPREM", "FRNDINT","FXTRACT",
"FABS", "FCHS", "FCOM", "FCOMP", "FCOMPP", "FICOM", "FICOMP", "FTST",
"FXAM", "FPTAN", "FPATAN", "F2XM1", "FYL2X", "FYL2XP1","FLDZ", "FLD1",
"FLDPI", "FLDL2T", "FLDL2E", "FLDLG2", "FLDLN2", "FINIT", "FNINIT", "FDISI",
"FNDISI", "FENI", "FNENI", "FLDCW", "FSTCW", "FNSTCW", "FSTSW", "FNSTSW",
"FCLEX", "FNCLEX", "FSTENV", "FNSTENV","FLDENV", "FSAVE", "FNSAVE", "FRSTOR",
"FINCSTP","FDECSTP","FFREE", "FNOP", "FWAIT"
]; ];
Debugger.CPU_8086 = 0; Debugger.CPU_8086 = 0;

View file

@ -494,6 +494,14 @@ var X86 = {
INTN: 0xCD, // opINTn() INTN: 0xCD, // opINTn()
INTO: 0xCE, // opINTO() INTO: 0xCE, // opINTO()
IRET: 0xCF, // opIRET() IRET: 0xCF, // opIRET()
ESC0: 0xD8, // opESC0()
ESC1: 0xD9, // opESC1()
ESC2: 0xDA, // opESC2()
ESC3: 0xDB, // opESC3()
ESC4: 0xDC, // opESC4()
ESC5: 0xDD, // opESC5()
ESC6: 0xDE, // opESC6()
ESC7: 0xDF, // opESC7()
LOOPNZ: 0xE0, // opLOOPNZ() LOOPNZ: 0xE0, // opLOOPNZ()
LOOPZ: 0xE1, // opLOOPZ() LOOPZ: 0xE1, // opLOOPZ()
LOOP: 0xE2, // opLOOP() LOOP: 0xE2, // opLOOP()
@ -510,6 +518,80 @@ var X86 = {
CALLMASK: 0x38FF, // mask 2-byte GRP4W opcodes with this before comparing to CALLW or CALLFDW CALLMASK: 0x38FF, // mask 2-byte GRP4W opcodes with this before comparing to CALLW or CALLFDW
UD2: 0x0B0F // UD2 (invalid opcode "guaranteed" to generate UD_FAULT on all post-8086 processors) UD2: 0x0B0F // UD2 (invalid opcode "guaranteed" to generate UD_FAULT on all post-8086 processors)
}, },
/*
* Floating Point Unit (FPU), aka Numeric Data Processor (NDP), aka Numeric Processor Extension (NPX) definitions
*/
FPU: {
MODEL_8087: 8087,
MODEL_80287: 80287,
MODEL_80387: 80387,
CONTROL: { // FPU Control Word
IM: 0x0001, // bit 0: Invalid Operation Mask
DM: 0x0002, // bit 1: Denormalized Operand Mask
ZM: 0x0004, // bit 2: Zero Divide Mask
OM: 0x0008, // bit 3: Overflow Mask
UM: 0x0010, // bit 4: Underflow Mask
PM: 0x0020, // bit 5: Precision Mask
IEM: 0x0080, // bit 7: Interrupt Enable Mask (0 enables interrupts, 1 masks them; 8087 only)
PC: 0x0300, // bits 8-9: Precision Control
RC: 0x0C00, // bits 10-11: Rounding Control
IC: 0x1000 // bit 12: Infinity Control (0 for Projective, 1 for Affine)
},
STATUS: { // FPU Status Word
IE: 0x0001, // bit 0: Invalid Operation
DE: 0x0002, // bit 1: Denormalized Operand
ZE: 0x0004, // bit 2: Zero Divide
OE: 0x0008, // bit 3: Overflow
UE: 0x0010, // bit 4: Underflow
PE: 0x0020, // bit 5: Precision
SF: 0x0040, // bit 6: Stack Fault (80387 and later)
ES: 0x0080, // bit 7: Exception Summary (Interrupt Request on 8087)
C0: 0x0100, // bit 8: Condition Code 0
C1: 0x0200, // bit 9: Condition Code 1
C2: 0x0400, // bit 10: Condition Code 2
ST: 0x3800, // bits 11-13: Stack Top
C3: 0x4000, // bit 14: Condition Code 3
BUSY: 0x8000 // bit 15: Busy
},
TAG: {
VALID: 0x0,
ZERO: 0x1,
SPECIAL: 0x2,
EMPTY: 0x3
}
/*
C3 C2 C1 C0 Condition Code (CC) values following an Examine
0 0 0 0 Valid, positive unnormalized (+Unnormal)
0 0 0 1 Invalid, positive, exponent=0 (+NaN)
0 0 1 0 Valid, negative, unnormalized (-Unnormal)
0 0 1 1 Invalid, negative, exponent=0 (-NaN)
0 1 0 0 Valid, positive, normalized (+Normal)
0 1 0 1 Infinity, positive (+Infinity)
0 1 1 0 Valid, negative, normalized (-Normal)
0 1 1 1 Infinity, negative (-Infinity)
1 0 0 0 Zero, positive (+0)
1 0 0 1 Empty
1 0 1 0 Zero, negative (-0)
1 0 1 1 Empty
1 1 0 0 Invalid, positive, exponent=0 (+Denormal)
1 1 0 1 Empty
1 1 1 0 Invalid, negative, exponent=0 (-Denormal)
1 1 1 1 Empty
Condition Code (CC) values following an FCOM or FTST
0 0 ? 0 ST > source operand (FCOM); ST > 0 (FTST)
0 0 ? 1 ST < source operand (FCOM); ST < 0 (FTST)
1 0 ? 0 ST = source operand (FCOM); ST = 0 (FTST)
1 1 ? 1 ST is not comparable
Condition Code (CC) values following a Remainder
Q1 0 Q0 Q2 Complete reduction (he three low bits of the quotient stored in C0, C3, and C1)
? 1 ? ? Incomplete reduction
*/
},
CYCLES_8088: { CYCLES_8088: {
nWordCyclePenalty: 4, // NOTE: accurate for the 8088/80188 only (on the 8086/80186, it applies to odd addresses only) nWordCyclePenalty: 4, // NOTE: accurate for the 8088/80188 only (on the 8086/80186, it applies to odd addresses only)
nEACyclesBase: 5, // base or index only (BX, BP, SI or DI) nEACyclesBase: 5, // base or index only (BX, BP, SI or DI)

View file

@ -75,8 +75,8 @@ if (!I386) {
* *
* The X86CPU class uses the following (parmsCPU) properties: * The X86CPU class uses the following (parmsCPU) properties:
* *
* model: a number (eg, 8088) that should match one of the X86.MODEL values * model: a number (eg, 8088) that should match one of the X86.MODEL values (default is 8088)
* stepping: a string (eg, "B1") that should match one of the X86.STEPPING values * stepping: a string (eg, "B1") that should match one of the X86.STEPPING values (default is "")
* *
* This extends the CPU class and passes any remaining parmsCPU properties to the CPU class * This extends the CPU class and passes any remaining parmsCPU properties to the CPU class
* constructor, along with a default speed (cycles per second) based on the specified (or default) * constructor, along with a default speed (cycles per second) based on the specified (or default)
@ -1070,6 +1070,12 @@ X86CPU.prototype.resetRegs = function()
this.regMDLo = this.regMDHi = 0; this.regMDLo = this.regMDHi = 0;
this.regXX = 0; // internal register for segment register and control register moves this.regXX = 0; // internal register for segment register and control register moves
/*
* This internal "register" is set in selected opcode handlers to record the original opcode; ordinarily,
* we dispatch on the opcode but never save it, because it's rarely needed.
*/
this.bOpcode = 0;
/* /*
* Another internal "register" we occasionally need is an interim copy of bModRM, set inside selected opcode * Another internal "register" we occasionally need is an interim copy of bModRM, set inside selected opcode
* handlers so that the helper function can have access to the instruction's bModRM without resorting to a * handlers so that the helper function can have access to the instruction's bModRM without resorting to a

296
modules/pcjs/lib/x86fpu.js Normal file
View file

@ -0,0 +1,296 @@
/**
* @fileoverview Implements PCjs 8087 FPU logic.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* Created 2015-Nov-09
*
* Copyright © 2012-2015 Jeff Parsons <Jeff@pcjs.org>
*
* This file is part of PCjs, which is part of the JavaScript Machines Project (aka JSMachines)
* at <http://jsmachines.net/> and <http://pcjs.org/>.
*
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCjs. If not,
* see <http://www.gnu.org/licenses/gpl.html>.
*
* You are required to include the above copyright notice in every source code file of every
* copy or modified version of this work, and to display that copyright notice on every screen
* that loads or runs any version of this software (see Computer.sCopyright).
*
* Some PCjs files also attempt to load external resource files, such as character-image files,
* ROM files, and disk image files. Those external resource files are not considered part of the
* PCjs program for purposes of the GNU General Public License, and the author does not claim
* any copyright as to their contents.
*/
"use strict";
if (NODE) {
var str = require("../../shared/lib/strlib");
var web = require("../../shared/lib/weblib");
var Component = require("../../shared/lib/component");
var X86 = require("./x86");
}
/**
* X86FPU(parmsFPU)
*
* The X86FPU class uses the following (parmsFPU) properties:
*
* model: a number (eg, 8087) that should match one of the X86.FPU.MODEL values (default is 8087)
* stepping: a string (eg, "B1") that should match one of the X86.FPU.STEPPING values (default is "")
*
* @constructor
* @extends Component
* @param {Object} parmsFPU
*/
function X86FPU(parmsFPU)
{
Component.call(this, "FPU", parmsFPU, X86FPU);
this.model = parmsFPU['model'] || X86.FPU.MODEL_8087;
/*
* We take the 'stepping' value, convert it to a hex value, and then add that to the model to provide
* a single value that's unique for any given CPU stepping. If no stepping is provided, then stepping
* is equal to model.
*/
var stepping = parmsFPU['stepping'];
this.stepping = this.model + (stepping? str.parseInt(stepping, 16) : 0);
/*
* Initialize the coprocessor to match the requested model.
*
* NOTE: Technically, the FPU's internal registers are 80-bit, but all JavaScript gives us are 64-bit floats.
*/
this.aFPUEmpty = new Array(8);
this.aFPURegs = new Float64Array(8);
this.regFPUTmpSR = new Float32Array(1);
this.intFPUTmpSR = new Int32Array(this.regFPUTmpSR.buffer);
this.regFPUTmpLR = new Float64Array(1);
this.intFPUTmpLR = new Int32Array(this.regFPUTmpLR.buffer);
this.regFPUInsAddr = this.regFPUDataAddr = this.regFPUOpcode = 0;
this.resetFPU();
}
Component.subclass(X86FPU);
/*
* Operand types:
*
* ST stack top; the register currently at the top of the stack
* ST(i) register in the stack i (0<=i<=7) stack elements from the top; ST(1) is the next-on-stack register, ST(2) is below ST(1), etc
* SR (short-real) short real (32 bits) number in memory
* LR (long-real) long real (64 bits) number in memory
* TR (temp-real) temporary real (80 bits) number in memory
* PD (packed-decimal) packed decimal integer (18 digits, 10 bytes) in memory
* WI (word-integer) word binary integer (16 bits) in memory
* SI (short-integer) short binary integer (32 bits) in memory
* LI (long-integer) long binary integer (64 bits) in memory
* NN (nn-bytes) memory area nn bytes long
*
*/
/**
* initBus(cmp, bus, cpu, dbg)
*
* @this {X86FPU}
* @param {Computer} cmp
* @param {Bus} bus
* @param {X86CPU} cpu
* @param {Debugger} dbg
*/
X86FPU.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.cpu = cpu;
this.setReady();
};
/**
* resetFPU()
*
* @this {X86FPU}
*/
X86FPU.prototype.resetFPU = function()
{
for (var i = 0; i < this.aFPUEmpty.length; i++) this.aFPUEmpty[i] = true;
this.regFPUControl = X86.FPU.CONTROL.IM | X86.FPU.CONTROL.DM | X86.FPU.CONTROL.ZM | X86.FPU.CONTROL.OM | X86.FPU.CONTROL.UM | X86.FPU.CONTROL.PM | X86.FPU.CONTROL.IEM | X86.FPU.CONTROL.PC;
this.regFPUStatus = 0;
this.iFPUReg = 0; // copy of the ST bits in regFPUStatus
};
/**
* setFPUTmpSRFromEA()
*
* Sets the internal regFPUTmpSR register to the (32-bit) short-real value located at regEA.
*
* @this {X86FPU}
*/
X86FPU.prototype.setFPUTmpSRFromEA = function()
{
this.intFPUTmpSR[0] = this.cpu.getLong(this.cpu.regEA);
};
/**
* fnFPU(bOpcode, bModRM, dst, src)
*
* @this {X86FPU}
* @param {number} bOpcode (0xD8-0xDF)
* @param {number} bModRM
* @param {number} dst
* @param {number} src
*/
X86FPU.prototype.fnFPU = function(bOpcode, bModRM, dst, src)
{
this.regFPUOpcode = bOpcode;
this.regFPUInsAddr = this.cpu.regLIP;
this.regFPUDataAddr = this.cpu.regEA;
this.println("FPU(" + str.toHexByte(bOpcode) + "," + str.toHexByte(bModRM) + ")");
var mod = (bModRM >> 6) & 0x3;
var reg = (bModRM >> 3) & 0x7;
var r_m = (bModRM & 0x7);
/*
* Combine mod and reg into one decodable value: put mod in the high nibble
* and reg in the low nibble, after first collapsing all mod values < 3 to zero.
*/
var modReg = (mod < 3? 0 : 0x30) + reg;
switch(bOpcode) {
case X86.OPCODE.ESC0:
switch(modReg) {
case 0x00:
this.fnFADDsr(); // ST=ST+[short-real]
break;
case 0x30:
this.fnFADDi(r_m); // ST=ST+ST(i)
break;
case 0x31:
this.fnFMULi(r_m); // ST=ST*ST(i)
break;
case 0x32:
// this.fnFCOMi(r_m);
break;
case 0x33:
// this.fnFCOMPi(r_m);
break;
case 0x34:
// this.fnFSUBi(r_m); // ST=ST-ST(i)
break;
case 0x35:
// this.fnFSUBRi(r_m); // ST=ST-ST(i)
break;
case 0x36:
// this.fnFDIVi(r_m); // ST=ST/ST(i)
break;
case 0x37:
// this.fnFDIVRi(r_m); // ST=ST/ST(i)
break;
}
break;
case X86.OPCODE.ESC3:
switch(modReg) {
case 0x34:
switch(r_m) {
case 0:
// this.fnFENI();
break;
case 1:
// this.fnFDISI;
break;
case 2:
// this.fnFCLEX();
break;
case 3:
this.fnFINIT();
break;
}
break;
}
break;
}
};
/**
* fnFINIT()
*
* @this {X86FPU}
*/
X86FPU.prototype.fnFINIT = function()
{
this.resetFPU();
};
/**
* fnFADDi()
*
* @this {X86FPU}
* @param {number} i
*/
X86FPU.prototype.fnFADDi = function(i)
{
this.aFPURegs[this.iFPUReg] += this.aFPURegs[i];
};
/**
* fnFMULi()
*
* @this {X86FPU}
* @param {number} i
*/
X86FPU.prototype.fnFMULi = function(i)
{
this.aFPURegs[this.iFPUReg] *= this.aFPURegs[i];
};
/**
* fnFADDsr()
*
* @this {X86FPU}
*/
X86FPU.prototype.fnFADDsr = function()
{
this.setFPUTmpSRFromEA();
this.aFPURegs[this.iFPUReg] += this.regFPUTmpSR[0];
};
/**
* X86FPU.init()
*
* This function operates on every HTML element of class "fpu", extracting the
* JSON-encoded parameters for the X86FPU constructor from the element's "data-value"
* attribute, invoking the constructor to create an X86FPU component, and then binding
* any associated HTML controls to the new component.
*/
X86FPU.init = function()
{
var aeFPUs = Component.getElementsByClass(window.document, PCJSCLASS, "fpu");
for (var iFPU = 0; iFPU < aeFPUs.length; iFPU++) {
var eFPU = aeFPUs[iFPU];
var parmsFPU = Component.getComponentParms(eFPU);
var fpu = new X86FPU(parmsFPU);
Component.bindComponentControls(fpu, eFPU, PCJSCLASS);
}
};
/*
* Initialize every FPU module on the page
*/
web.onInit(X86FPU.init);
if (NODE) module.exports = X86FPU;

View file

@ -945,6 +945,11 @@ X86.fnDIVw = function(dst, src)
*/ */
X86.fnESC = function(dst, src) X86.fnESC = function(dst, src)
{ {
if (this.fpu) {
this.fpu.fnFPU(this.bOpcode, this.bModRM, dst, src);
this.stopCPU();
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 2 : 8);
return dst; return dst;
}; };

View file

@ -3771,14 +3771,95 @@ X86.opXLAT = function()
}; };
/** /**
* op=0xD8-0xDF (ESC) * opESC()
*
* @this {X86CPU}
* @param {number} bOpcode
*/
X86.opESC = function(bOpcode)
{
this.bOpcode = bOpcode;
this.aOpModRegWord[this.bModRM = this.getIPByte()].call(this, X86.fnESC);
};
/**
* op=0xD8 (ESC0)
* *
* @this {X86CPU} * @this {X86CPU}
*/ */
X86.opESC = function() X86.opESC0 = function()
{ {
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnESC); X86.opESC.call(this, X86.OPCODE.ESC0);
this.nStepCycles -= 8; // TODO: Fix };
/**
* op=0xD9 (ESC1)
*
* @this {X86CPU}
*/
X86.opESC1 = function()
{
X86.opESC.call(this, X86.OPCODE.ESC1);
};
/**
* op=0xDA (ESC2)
*
* @this {X86CPU}
*/
X86.opESC2 = function()
{
X86.opESC.call(this, X86.OPCODE.ESC2);
};
/**
* op=0xDB (ESC3)
*
* @this {X86CPU}
*/
X86.opESC3 = function()
{
X86.opESC.call(this, X86.OPCODE.ESC3);
};
/**
* op=0xDC (ESC4)
*
* @this {X86CPU}
*/
X86.opESC4 = function()
{
X86.opESC.call(this, X86.OPCODE.ESC4);
};
/**
* op=0xDD (ESC5)
*
* @this {X86CPU}
*/
X86.opESC5 = function()
{
X86.opESC.call(this, X86.OPCODE.ESC5);
};
/**
* op=0xDE (ESC6)
*
* @this {X86CPU}
*/
X86.opESC6 = function()
{
X86.opESC.call(this, X86.OPCODE.ESC6);
};
/**
* op=0xDF (ESC7)
*
* @this {X86CPU}
*/
X86.opESC7 = function()
{
X86.opESC.call(this, X86.OPCODE.ESC7);
}; };
/** /**
@ -4421,8 +4502,8 @@ X86.aOps = [
* Even as of the Pentium, opcode 0xD6 is still marked as "reserved", but it's always been SALC (aka SETALC). * Even as of the Pentium, opcode 0xD6 is still marked as "reserved", but it's always been SALC (aka SETALC).
*/ */
X86.opAAM, X86.opAAD, X86.opSALC, X86.opXLAT, // 0xD4-0xD7 X86.opAAM, X86.opAAD, X86.opSALC, X86.opXLAT, // 0xD4-0xD7
X86.opESC, X86.opESC, X86.opESC, X86.opESC, // 0xD8-0xDB X86.opESC0, X86.opESC1, X86.opESC2, X86.opESC3, // 0xD8-0xDB
X86.opESC, X86.opESC, X86.opESC, X86.opESC, // 0xDC-0xDF X86.opESC4, X86.opESC5, X86.opESC6, X86.opESC7, // 0xDC-0xDF
X86.opLOOPNZ, X86.opLOOPZ, X86.opLOOP, X86.opJCXZ, // 0xE0-0xE3 X86.opLOOPNZ, X86.opLOOPZ, X86.opLOOP, X86.opJCXZ, // 0xE0-0xE3
X86.opINb, X86.opINw, X86.opOUTb, X86.opOUTw, // 0xE4-0xE7 X86.opINb, X86.opINw, X86.opOUTb, X86.opOUTw, // 0xE4-0xE7
X86.opCALL, X86.opJMP, X86.opJMPF, X86.opJMPs, // 0xE8-0xEB X86.opCALL, X86.opJMP, X86.opJMPF, X86.opJMPs, // 0xE8-0xEB

View file

@ -528,6 +528,12 @@
<xsl:otherwise></xsl:otherwise> <xsl:otherwise></xsl:otherwise>
</xsl:choose> </xsl:choose>
</xsl:variable> </xsl:variable>
<xsl:variable name="fpu">
<xsl:choose>
<xsl:when test="@fpu"><xsl:value-of select="@fpu"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="cycles"> <xsl:variable name="cycles">
<xsl:choose> <xsl:choose>
<xsl:when test="@cycles"><xsl:value-of select="@cycles"/></xsl:when> <xsl:when test="@cycles"><xsl:value-of select="@cycles"/></xsl:when>
@ -567,7 +573,34 @@
<xsl:call-template name="component"> <xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/> <xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class" select="'cpu'"/> <xsl:with-param name="class" select="'cpu'"/>
<xsl:with-param name="parms">,model:<xsl:value-of select="$model"/>,stepping:'<xsl:value-of select="$stepping"/>',cycles:<xsl:value-of select="$cycles"/>,multiplier:<xsl:value-of select="$multiplier"/>,autoStart:<xsl:value-of select="$autoStart"/>,csStart:<xsl:value-of select="$csStart"/>,csInterval:<xsl:value-of select="$csInterval"/>,csStop:<xsl:value-of select="$csStop"/></xsl:with-param> <xsl:with-param name="parms">,model:<xsl:value-of select="$model"/>,stepping:'<xsl:value-of select="$stepping"/>',fpu:<xsl:value-of select="$fpu"/>,cycles:<xsl:value-of select="$cycles"/>,multiplier:<xsl:value-of select="$multiplier"/>,autoStart:<xsl:value-of select="$autoStart"/>,csStart:<xsl:value-of select="$csStart"/>,csInterval:<xsl:value-of select="$csInterval"/>,csStop:<xsl:value-of select="$csStop"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="fpu[@ref]">
<xsl:param name="machine" select="''"/>
<xsl:variable name="componentFile"><xsl:value-of select="$rootDir"/><xsl:value-of select="@ref"/></xsl:variable>
<xsl:apply-templates select="document($componentFile)/fpu"><xsl:with-param name="machine" select="$machine"/></xsl:apply-templates>
</xsl:template>
<xsl:template match="fpu[not(@ref)]">
<xsl:param name="machine" select="''"/>
<xsl:variable name="model">
<xsl:choose>
<xsl:when test="@model"><xsl:value-of select="@model"/></xsl:when>
<xsl:otherwise>8087</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="stepping">
<xsl:choose>
<xsl:when test="@stepping"><xsl:value-of select="@stepping"/></xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class" select="'fpu'"/>
<xsl:with-param name="parms">,model:<xsl:value-of select="$model"/>,stepping:'<xsl:value-of select="$stepping"/>'</xsl:with-param>
</xsl:call-template> </xsl:call-template>
</xsl:template> </xsl:template>

View file

@ -107,6 +107,7 @@
"./modules/pcjs/lib/cpu.js", "./modules/pcjs/lib/cpu.js",
"./modules/pcjs/lib/x86seg.js", "./modules/pcjs/lib/x86seg.js",
"./modules/pcjs/lib/x86cpu.js", "./modules/pcjs/lib/x86cpu.js",
"./modules/pcjs/lib/x86fpu.js",
"./modules/pcjs/lib/x86func.js", "./modules/pcjs/lib/x86func.js",
"./modules/pcjs/lib/x86ops.js", "./modules/pcjs/lib/x86ops.js",
"./modules/pcjs/lib/x86op0f.js", "./modules/pcjs/lib/x86op0f.js",