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.
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:

View file

@ -3,16 +3,17 @@
<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>
<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"/>
<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="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"/>
<cpu id="cpu8088" model="8088" autostart="true"/>
<keyboard ref="/devices/pc/keyboard/keyboard-minimal.xml"/>
<debugger id="debugger"/>
<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">
<control type="switches" label="SW1" binding="sw1" left="0px"/>
<control type="description" binding="swdesc" left="0px"/>

View file

@ -13,7 +13,7 @@
<fdc ref="/disks/pc/library.xml" automount=''/>
<debugger id="debugger" messages="" commands=''/>
<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"/>
<serial id="com1" adapter="1"/>
<serial id="com2" adapter="2" binding="print"/>

View file

@ -3,6 +3,7 @@
<control type="list" binding="listDrives"/>
<control type="list" binding="listDisks">
<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.10/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/5.0/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/games/microsoft/flightsim/manifest.xml" disk="*"/>
<manifest ref="/disks/pc/tools/borland/pascal/3.00b/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/c/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/2.03/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/1982/esuite/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/component.js](../../shared/lib/component.js)
* [pcjs/defines.js](defines.js)
* [pcjs/x86.js](x86.js)
* [pcjs/interrupts.js](interrupts.js)
* [pcjs/messages.js](messages.js)
* [pcjs/panel.js](panel.js)
* [pcjs/bus.js](bus.js)
* [pcjs/memory.js](memory.js)
* [pcjs/cpu.js](cpu.js)
* [pcjs/x86.js](x86.js)
* [pcjs/x86seg.js](x86seg.js)
* [pcjs/x86cpu.js](x86cpu.js)
* [pcjs/x86cpu.js](x86fpu.js)
* [pcjs/x86func.js](x86func.js)
* [pcjs/x86ops.js](x86ops.js)
* [pcjs/x86op0f.js](x86op0f.js)
* [pcjs/x86func.js](x86func.js)
* [pcjs/x86modb.js](x86modb.js)
* [pcjs/x86modw.js](x86modw.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.
*/
this.sw1Init = 0;
var sw1 = parmsChipSet['sw1'];
var sw1 = parmsChipSet[ChipSet.CONTROLS.SW1];
if (sw1) {
this.sw1Init = this.parseSwitches(sw1, ChipSet.PPI_SW.MEMORY.X4 | ChipSet.PPI_SW.MONITOR.MONO);
} 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;
* 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.
@ -288,6 +290,12 @@ ChipSet.MODELS = {
"deskpro386": ChipSet.MODEL_DESKPRO386
};
ChipSet.CONTROLS = {
SW1: "sw1",
SW2: "sw2",
SWDESC: "swdesc"
};
/*
* Values returned by ChipSet.getSWVideoMonitor()
*/
@ -983,6 +991,8 @@ ChipSet.COPROC = { // TODO: Define a variable for this
};
/**
* setBinding(sHTMLType, sBinding, control)
*
* @this {ChipSet}
* @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")
@ -992,31 +1002,43 @@ ChipSet.COPROC = { // TODO: Define a variable for this
ChipSet.prototype.setBinding = function(sHTMLType, sBinding, control)
{
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.addSwitches(sBinding, control, 8, this.sw1Init, {
0: (this.model == ChipSet.MODEL_5150? "Bootable Floppy Drive" : "Loop on POST"),
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"
this.addSwitches(sBinding, 8, this.sw2Init, {
0: "Expansion Memory Size", // up to 480Kb, which, when combined with 64Kb of MODEL_5150 base memory, gives a maximum of 544Kb
4: "Reserved"
});
return true;
case "sw2":
if (this.model == ChipSet.MODEL_5150) {
this.bindings[sBinding] = control;
this.addSwitches(sBinding, control, 8, this.sw2Init, {
0: "Expansion Memory Size", // up to 480Kb, which, when combined with 64Kb of MODEL_5150 base memory, gives a maximum of 544Kb
4: "Reserved"
});
return true;
}
break;
case "swdesc":
this.bindings[sBinding] = control;
return true;
default:
break;
}
break;
case ChipSet.CONTROLS.SWDESC:
this.bindings[sBinding] = control;
return true;
default:
break;
}
return false;
};
@ -1069,6 +1091,9 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg)
}
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;
this.sw1 = this.sw1Init;
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
@ -2196,45 +2223,91 @@ ChipSet.prototype.getSWVideoMonitor = function(fInit)
};
/**
* addSwitches(s, control, n, v, oTips)
* addSwitches(sBinding, n, v, oTips)
*
* @this {ChipSet}
* @param {string} s is the name of the control
* @param {Object} control is the HTML control DOM object
* @param {string} sBinding is the name of the control
* @param {number} n is the number of switches to add
* @param {number} v contains the current value(s) of the switches
* @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 sCellClass = PCJSCLASS + "-bitCell";
var control = this.bindings[sBinding];
for (var i = 1; i <= n; i++) {
var sCellClasses = sCellClass;
if (!i) sCellClasses += " " + PCJSCLASS + "-bitCellLeft";
var sCellID = s + "-" + i;
var sCellClasses = this.sCellClass;
if (!i) sCellClasses += " " + this.sCellClass + "Left";
var sCellID = sBinding + "-" + i;
sHTML += "<div id=\"" + sCellID + "\" class=\"" + sCellClasses + "\" data-value=\"0\">" + i + "</div>\n";
}
control.innerHTML = sHTML;
var aeCells = Component.getElementsByClass(control, sCellClass);
var sTip = null;
for (i = 0; i < aeCells.length; i++) {
if (oTips != null && oTips[i] != null) {
sTip = oTips[i];
this.updateSwitches(sBinding, v, oTips);
};
/**
* 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
* 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]);
}
};
/**
* updateSwitchDescriptions()
*
* @this {ChipSet}
*/
ChipSet.prototype.updateSwitchDescriptions = function()
{
var controlDesc = this.bindings[ChipSet.CONTROLS.SWDESC];
if (controlDesc != null) {
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 b = (0x1 << (+asParts[1] - 1));
switch (asParts[0]) {
case "sw1":
case ChipSet.CONTROLS.SW1:
this.sw1Init = (this.sw1Init & ~b) | (f? 0 : b);
break;
case "sw2":
case ChipSet.CONTROLS.SW2:
this.sw2Init = (this.sw2Init & ~b) | (f? 0 : b);
break;
default:
break;
}
this.updateSwitchDesc();
};
/**
* 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;
}
this.updateSwitchDescriptions();
};
/**
@ -5284,7 +5325,7 @@ ChipSet.init = function()
var parmsChipSet = Component.getComponentParms(eChipSet);
var chipset = new ChipSet(parmsChipSet);
Component.bindComponentControls(chipset, eChipSet, PCJSCLASS);
chipset.updateSwitchDesc();
chipset.updateSwitchDescriptions();
}
};

View file

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

View file

@ -275,7 +275,7 @@ if (DEBUGGER) {
Debugger.COMMANDS = {
'?': "help/print",
'a [#]': "assemble",
'a [#]': "assemble", // TODO: Implement this command someday
'b [#]': "breakpoint", // multiple variations (use b? to list them)
'c': "clear output",
'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 = {
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,
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,
DIV: 32, DS: 33, ENTER: 34, ES: 35, ESC: 36, FADD: 37, FBLD: 38, FBSTP: 39,
FCOM: 40, FCOMP: 41, FDIV: 42, FDIVR: 43, FIADD: 44, FICOM: 45, FICOMP: 46, FIDIV: 47,
FIDIVR: 48, FILD: 49, FIMUL: 50, FIST: 51, FISTP: 52, FISUB: 53, FISUBR: 54, FLD: 55,
FLDCW: 56, FLDENV: 57, FMUL: 58, FNSAVE: 59, FNSTCW: 60, FNSTENV:61, FNSTSW: 62, FRSTOR: 63,
FS: 64, FST: 65, FSTP: 66, FSUB: 67, FSUBR: 68, GS: 69, HLT: 70, IBTS: 71,
IDIV: 72, IMUL: 73, IN: 74, INC: 75, INS: 76, INT: 77, INT3: 78, INTO: 79,
IRET: 80, JBE: 81, JC: 82, JCXZ: 83, JG: 84, JGE: 85, JL: 86, JLE: 87,
JMP: 88, JA: 89, JNC: 90, JNO: 91, JNP: 92, JNS: 93, JNZ: 94, JO: 95,
JP: 96, JS: 97, JZ: 98, LAHF: 99, LAR: 100, LDS: 101, LEA: 102, LEAVE: 103,
LES: 104, LFS: 105, LGDT: 106, LGS: 107, LIDT: 108, LLDT: 109, LMSW: 110, LOADALL:111,
LOCK: 112, LODSB: 113, LODSW: 114, LOOP: 115, LOOPNZ: 116, LOOPZ: 117, LSL: 118, LSS: 119,
LTR: 120, MOV: 121, MOVSB: 122, MOVSW: 123, MOVSX: 124, MOVZX: 125, MUL: 126, NEG: 127,
NOP: 128, NOT: 129, OR: 130, OS: 131, OUT: 132, OUTS: 133, POP: 134, POPA: 135,
POPF: 136, PUSHF: 137, PUSHA: 138, PUSH: 139, RCL: 140, RCR: 141, REPNZ: 142, REPZ: 143,
RET: 144, RETF: 145, ROL: 146, ROR: 147, SAHF: 148, SALC: 149, SAR: 150, SBB: 151,
SCASB: 152, SCASW: 153, SETBE: 154, SETC: 155, SETG: 156, SETGE: 157, SETL: 158, SETLE: 159,
SETNBE: 160, SETNC: 161, SETNO: 162, SETNP: 163, SETNS: 164, SETNZ: 165, SETO: 166, SETP: 167,
SETS: 168, SETZ: 169, SGDT: 170, SHL: 171, SHLD: 172, SHR: 173, SHRD: 174, SIDT: 175,
SLDT: 176, SMSW: 177, SS: 178, STC: 179, STD: 180, STI: 181, STOSB: 182, STOSW: 183,
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
DIV: 32, DS: 33, ENTER: 34, ES: 35, ESC: 36, FS: 37, GS: 38, HLT: 39,
IBTS: 40, IDIV: 41, IMUL: 42, IN: 43, INC: 44, INS: 45, INT: 46, INT3: 47,
INTO: 48, IRET: 49, JBE: 50, JC: 51, JCXZ: 52, JG: 53, JGE: 54, JL: 55,
JLE: 56, JMP: 57, JA: 58, JNC: 59, JNO: 60, JNP: 61, JNS: 62, JNZ: 63,
JO: 64, JP: 65, JS: 66, JZ: 67, LAHF: 68, LAR: 69, LDS: 70, LEA: 71,
LEAVE: 72, LES: 73, LFS: 74, LGDT: 75, LGS: 76, LIDT: 77, LLDT: 78, LMSW: 79,
LOADALL:80, LOCK: 81, LODSB: 82, LODSW: 83, LOOP: 84, LOOPNZ: 85, LOOPZ: 86, LSL: 87,
LSS: 88, LTR: 89, MOV: 90, MOVSB: 91, MOVSW: 92, MOVSX: 93, MOVZX: 94, MUL: 95,
NEG: 96, NOP: 97, NOT: 98, OR: 99, OS: 100, OUT: 101, OUTS: 102, POP: 103,
POPA: 104, POPF: 105, PUSHF: 106, PUSHA: 107, PUSH: 108, RCL: 109, RCR: 110, REPNZ: 111,
REPZ: 112, RET: 113, RETF: 114, ROL: 115, ROR: 116, SAHF: 117, SALC: 118, SAR: 119,
SBB: 120, SCASB: 121, SCASW: 122, SETBE: 123, SETC: 124, SETG: 125, SETGE: 126, SETL: 127,
SETLE: 128, SETNBE: 129, SETNC: 130, SETNO: 131, SETNP: 132, SETNS: 133, SETNZ: 134, SETO: 135,
SETP: 136, SETS: 137, SETZ: 138, SGDT: 139, SHL: 140, SHLD: 141, SHR: 142, SHRD: 143,
SIDT: 144, SLDT: 145, SMSW: 146, SS: 147, STC: 148, STD: 149, STI: 150, STOSB: 151,
STOSW: 152, STR: 153, SUB: 154, TEST: 155, VERR: 156, VERW: 157, WAIT: 158, XBTS: 159,
XCHG: 160, XLAT: 161, XOR: 162, GRP1B: 163, GRP1W: 164, GRP1SW: 165, GRP2B: 166, GRP2W: 167,
GRP2B1: 168, GRP2W1: 169, GRP2BC: 170, GRP2WC: 171, GRP3B: 172, GRP3W: 173, GRP4B: 174, GRP4W: 175,
OP0F: 176, GRP6: 177, GRP7: 178, GRP8: 179
};
/*
* Instruction names (mnemonics), indexed by instruction ordinal (above)
* CPU instruction names (mnemonics), indexed by CPU instruction ordinal (above)
*/
Debugger.INS_NAMES = [
"INVALID","AAA", "AAD", "AAM", "AAS", "ADC", "ADD", "AND",
"ARPL", "AS:", "BOUND", "BSF", "BSR", "BT", "BTC", "BTR",
"BTS", "CALL", "CBW", "CLC", "CLD", "CLI", "CLTS", "CMC",
"CMP", "CMPSB", "CMPSW", "CS:", "CWD", "DAA", "DAS", "DEC",
"DIV", "DS:", "ENTER", "ES:", "ESC", "FADD", "FBLD", "FBSTP",
"FCOM", "FCOMP", "FDIV", "FDIVR", "FIADD", "FICOM", "FICOMP", "FIDIV",
"FIDIVR", "FILD", "FIMUL", "FIST", "FISTP", "FISUB", "FISUBR", "FLD",
"FLDCW", "FLDENV", "FMUL", "FNSAVE", "FNSTCW", "FNSTENV","FNSTSW", "FRSTOR",
"FS:", "FST", "FSTP", "FSUB", "FSUBR", "GS:", "HLT", "IBTS",
"IDIV", "IMUL", "IN", "INC", "INS", "INT", "INT3", "INTO",
"IRET", "JBE", "JC", "JCXZ", "JG", "JGE", "JL", "JLE",
"JMP", "JA", "JNC", "JNO", "JNP", "JNS", "JNZ", "JO",
"JP", "JS", "JZ", "LAHF", "LAR", "LDS", "LEA", "LEAVE",
"LES", "LFS", "LGDT", "LGS", "LIDT", "LLDT", "LMSW", "LOADALL",
"LOCK", "LODSB", "LODSW", "LOOP", "LOOPNZ", "LOOPZ", "LSL", "LSS",
"LTR", "MOV", "MOVSB", "MOVSW", "MOVSX", "MOVZX", "MUL", "NEG",
"NOP", "NOT", "OR", "OS:", "OUT", "OUTS", "POP", "POPA",
"POPF", "PUSHF", "PUSHA", "PUSH", "RCL", "RCR", "REPNZ", "REPZ",
"RET", "RETF", "ROL", "ROR", "SAHF", "SALC", "SAR", "SBB",
"SCASB", "SCASW", "SETBE", "SETC", "SETG", "SETGE", "SETL", "SETLE",
"SETNBE", "SETNC", "SETNO", "SETNP", "SETNS", "SETNZ", "SETO", "SETP",
"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"
"DIV", "DS:", "ENTER", "ES:", "ESC", "FS:", "GS:", "HLT",
"IBTS", "IDIV", "IMUL", "IN", "INC", "INS", "INT", "INT3",
"INTO", "IRET", "JBE", "JC", "JCXZ", "JG", "JGE", "JL",
"JLE", "JMP", "JA", "JNC", "JNO", "JNP", "JNS", "JNZ",
"JO", "JP", "JS", "JZ", "LAHF", "LAR", "LDS", "LEA",
"LEAVE", "LES", "LFS", "LGDT", "LGS", "LIDT", "LLDT", "LMSW",
"LOADALL","LOCK", "LODSB", "LODSW", "LOOP", "LOOPNZ", "LOOPZ", "LSL",
"LSS", "LTR", "MOV", "MOVSB", "MOVSW", "MOVSX", "MOVZX", "MUL",
"NEG", "NOP", "NOT", "OR", "OS:", "OUT", "OUTS", "POP",
"POPA", "POPF", "PUSHF", "PUSHA", "PUSH", "RCL", "RCR", "REPNZ",
"REPZ", "RET", "RETF", "ROL", "ROR", "SAHF", "SALC", "SAR",
"SBB", "SCASB", "SCASW", "SETBE", "SETC", "SETG", "SETGE", "SETL",
"SETLE", "SETNBE", "SETNC", "SETNO", "SETNP", "SETNS", "SETNZ", "SETO",
"SETP", "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;

View file

@ -494,6 +494,14 @@ var X86 = {
INTN: 0xCD, // opINTn()
INTO: 0xCE, // opINTO()
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()
LOOPZ: 0xE1, // opLOOPZ()
LOOP: 0xE2, // opLOOP()
@ -510,6 +518,80 @@ var X86 = {
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)
},
/*
* 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: {
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)

View file

@ -75,8 +75,8 @@ if (!I386) {
*
* The X86CPU class uses the following (parmsCPU) properties:
*
* model: a number (eg, 8088) that should match one of the X86.MODEL values
* stepping: a string (eg, "B1") that should match one of the X86.STEPPING 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 (default is "")
*
* 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)
@ -1070,6 +1070,12 @@ X86CPU.prototype.resetRegs = function()
this.regMDLo = this.regMDHi = 0;
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
* 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)
{
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;
};

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}
*/
X86.opESC = function()
X86.opESC0 = function()
{
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnESC);
this.nStepCycles -= 8; // TODO: Fix
X86.opESC.call(this, X86.OPCODE.ESC0);
};
/**
* 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).
*/
X86.opAAM, X86.opAAD, X86.opSALC, X86.opXLAT, // 0xD4-0xD7
X86.opESC, X86.opESC, X86.opESC, X86.opESC, // 0xD8-0xDB
X86.opESC, X86.opESC, X86.opESC, X86.opESC, // 0xDC-0xDF
X86.opESC0, X86.opESC1, X86.opESC2, X86.opESC3, // 0xD8-0xDB
X86.opESC4, X86.opESC5, X86.opESC6, X86.opESC7, // 0xDC-0xDF
X86.opLOOPNZ, X86.opLOOPZ, X86.opLOOP, X86.opJCXZ, // 0xE0-0xE3
X86.opINb, X86.opINw, X86.opOUTb, X86.opOUTw, // 0xE4-0xE7
X86.opCALL, X86.opJMP, X86.opJMPF, X86.opJMPs, // 0xE8-0xEB

View file

@ -528,6 +528,12 @@
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</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:choose>
<xsl:when test="@cycles"><xsl:value-of select="@cycles"/></xsl:when>
@ -567,7 +573,34 @@
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<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:template>

View file

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