CPU function reorg

This commit is contained in:
Jeff Parsons 2015-03-05 14:41:34 -08:00 committed by jeffpar
commit 1dc0aeccd6
16 changed files with 9794 additions and 9015 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -43,15 +43,14 @@
"X86": true,
"X86Seg": true,
"X86CPU": true,
"X86Grps": true,
"X86Help": true,
"X86Func": true,
"X86OpXX": true,
"X86Op0F": true,
"X86ModB": true,
"X86ModW": true,
"X86ModB32": true,
"X86ModW32": true,
"X86ModSIB": true,
"X86OpXX": true,
"X86Op0F": true,
"str": true,
"usr": true,
"web": true,

View file

@ -11,7 +11,7 @@ So it's best to refer to these files generically as "modules", and more specific
whenever they implement a specific device (or set of devices, in the case of [*ChipSet*](/docs/pcjs/chipset/)).
Examples of non-device modules include UI modules like [panel.js](panel.js) and [debugger.js](debugger.js),
and sub-modules like [x86opxx.js](x86opxx.js) and [x86help.js](x86help.js) that separate the CPU functionality
and sub-modules like [x86opxx.js](x86opxx.js) and [x86func.js](x86func.js) that separate the CPU functionality
of [x86.js](x86.js) into more manageable pieces.
These modules should always be loaded or compiled in the order listed by the *pcJSFiles* property in
@ -37,8 +37,7 @@ At the time of this writing, the order is:
* [pcjs/x86.js](x86.js)
* [pcjs/x86seg.js](x86seg.js)
* [pcjs/x86cpu.js](x86cpu.js)
* [pcjs/x86grps.js](x86grps.js)
* [pcjs/x86help.js](x86help.js)
* [pcjs/x86func.js](x86func.js)
* [pcjs/x86modb.js](x86modb.js)
* [pcjs/x86modw.js](x86modw.js)
* [pcjs/x86modb16.js](x86modb16.js)

View file

@ -2541,9 +2541,7 @@ if (DEBUGGER) {
* getByte(aAddr, inc)
*
* getByte() should be used for all Debugger memory reads (eg, doDump, doUnassemble), to ensure
* all notification handlers are bypassed for physical addresses; for segmented addresses, we must
* use the CPU's X86Seg load() logic, but we don't call the CPU's getSOByte() or getByte() functions,
* to avoid triggering any memory read notifications.
* all notification handlers are bypassed for physical addresses.
*
* @this {Debugger}
* @param {Array} aAddr
@ -2689,7 +2687,7 @@ if (DEBUGGER) {
* aAddr[2] = this.getAddr(aAddr);
*
* The way to create a real-mode breakpoint that will break regardless of mode is to
* use the physical address of the real-mode memory location.
* use the physical address of the real-mode memory location instead.
*/
aAddr[3] = fTemp;
aBreak.push(aAddr);
@ -4670,7 +4668,7 @@ if (DEBUGGER) {
fUnknown = false;
switch(sRegMatch){
case "MS":
X86Help.opHelpLMSW.call(this.cpu, w);
this.cpu.setMSW(w);
break;
case "TR":
this.cpu.segTSS.load(w);

View file

@ -240,8 +240,8 @@ var X86 = {
MASK: 0xfff8 // index of corresponding entry in GDT, LDT or IDT
},
RESULT: {
SIZE_BYTE: 0x00100, // mask for byte arithmetic instructions (after subtracting 1)
SIZE_WORD: 0x10000, // mask for word arithmetic instructions (after subtracting 1)
SIZE_BYTE: 0x00100,
SIZE_WORD: 0x10000,
AUXOVF_AF: 0x00010,
AUXOVF_OF: 0x08080,
AUXOVF_CF: 0x10100

View file

@ -42,12 +42,11 @@ if (typeof module !== 'undefined') {
var CPU = require("./cpu");
var X86 = require("./x86");
var X86Seg = require("./x86seg");
var X86Grps = require("./x86grps");
var X86Help = require("./x86help");
var X86ModB = require("./x86modb");
var X86ModW = require("./x86modw");
var X86Func = require("./x86func");
var X86OpXX = require("./x86opxx");
var X86Op0F = require("./x86op0f");
var X86ModB = require("./x86modb");
var X86ModW = require("./x86modw");
}
if (I386) {
@ -482,7 +481,7 @@ X86CPU.PREFETCH = {
* regLIP has been set, so flushPrefetch() expects to receive that address.
*
* If the prefetch queue does not contain any (or enough) bytes to satisfy a getBytePrefetch()
* or getWordPrefetch() request, we force the queue to be filled with the necessary number
* or getShortPrefetch() request, we force the queue to be filled with the necessary number
* of bytes first.
*
* @this {X86CPU}
@ -706,10 +705,10 @@ X86CPU.prototype.initProcessor = function()
*/
this.CYCLES = (this.model >= X86.MODEL_80286? X86CPU.CYCLES_80286 : X86CPU.CYCLES_8088);
this.aOps = X86OpXX.aOps;
this.aOpGrp4b = X86Grps.aOpGrp4b;
this.aOpGrp4w = X86Grps.aOpGrp4w;
this.aOpGrp6 = X86Op0F.aOpGrp6Real; // setProtMode() will ensure that aOpGrp6 is switched
this.aOps = X86.aOps;
this.aOpGrp4b = X86.aOpGrp4b;
this.aOpGrp4w = X86.aOpGrp4w;
this.aOpGrp6 = X86.aOpGrp6Real; // setProtMode() will ensure that aOpGrp6 is switched
if (this.model >= X86.MODEL_80186) {
/*
@ -720,34 +719,34 @@ X86CPU.prototype.initProcessor = function()
* Instruction handlers that contain "hard-coded" 80286 cycle times include: opINSb, opINSw, opOUTSb,
* opOUTSw, opENTER, and opLEAVE.
*/
this.aOps = X86OpXX.aOps.slice(); // make copies of aOps and others before modifying them
this.aOpGrp4b = X86Grps.aOpGrp4b.slice();
this.aOpGrp4w = X86Grps.aOpGrp4w.slice();
this.aOps = X86.aOps.slice(); // make copies of aOps and others before modifying them
this.aOpGrp4b = X86.aOpGrp4b.slice();
this.aOpGrp4w = X86.aOpGrp4w.slice();
this.nShiftCountMask = 0x1f; // on newer processors, all shift counts are MOD 32
this.aOps[0x0F] = X86Help.opHelpInvalid;
this.aOps[X86.OPCODE.PUSHA] = X86OpXX.opPUSHA;
this.aOps[X86.OPCODE.POPA] = X86OpXX.opPOPA;
this.aOps[X86.OPCODE.BOUND] = X86OpXX.opBOUND;
this.aOps[X86.OPCODE.ARPL] = X86Help.opHelpInvalid;
this.aOps[X86.OPCODE.FS] = X86Help.opHelpInvalid;
this.aOps[X86.OPCODE.GS] = X86Help.opHelpInvalid;
this.aOps[X86.OPCODE.OS] = X86Help.opHelpInvalid;
this.aOps[X86.OPCODE.AS] = X86Help.opHelpInvalid;
this.aOps[X86.OPCODE.PUSH16] = X86OpXX.opPUSH16;
this.aOps[X86.OPCODE.IMUL16] = X86OpXX.opIMUL16;
this.aOps[X86.OPCODE.PUSH8] = X86OpXX.opPUSH8;
this.aOps[X86.OPCODE.IMUL8] = X86OpXX.opIMUL8;
this.aOps[X86.OPCODE.INSB] = X86OpXX.opINSb;
this.aOps[X86.OPCODE.INSW] = X86OpXX.opINSw;
this.aOps[X86.OPCODE.OUTSB] = X86OpXX.opOUTSb;
this.aOps[X86.OPCODE.OUTSW] = X86OpXX.opOUTSw;
this.aOps[0xC0] = X86OpXX.opGrp2bi;
this.aOps[0xC1] = X86OpXX.opGrp2wi;
this.aOps[X86.OPCODE.ENTER] = X86OpXX.opENTER;
this.aOps[X86.OPCODE.LEAVE] = X86OpXX.opLEAVE;
this.aOps[0xF1] = X86OpXX.opINT1;
this.aOpGrp4b[0x07] = X86Grps.opGrpInvalid;
this.aOpGrp4w[0x07] = X86Grps.opGrpInvalid;
this.aOps[0x0F] = X86.opInvalid;
this.aOps[X86.OPCODE.PUSHA] = X86.opPUSHA;
this.aOps[X86.OPCODE.POPA] = X86.opPOPA;
this.aOps[X86.OPCODE.BOUND] = X86.opBOUND;
this.aOps[X86.OPCODE.ARPL] = X86.opInvalid;
this.aOps[X86.OPCODE.FS] = X86.opInvalid;
this.aOps[X86.OPCODE.GS] = X86.opInvalid;
this.aOps[X86.OPCODE.OS] = X86.opInvalid;
this.aOps[X86.OPCODE.AS] = X86.opInvalid;
this.aOps[X86.OPCODE.PUSH16] = X86.opPUSH16;
this.aOps[X86.OPCODE.IMUL16] = X86.opIMUL16;
this.aOps[X86.OPCODE.PUSH8] = X86.opPUSH8;
this.aOps[X86.OPCODE.IMUL8] = X86.opIMUL8;
this.aOps[X86.OPCODE.INSB] = X86.opINSb;
this.aOps[X86.OPCODE.INSW] = X86.opINSw;
this.aOps[X86.OPCODE.OUTSB] = X86.opOUTSb;
this.aOps[X86.OPCODE.OUTSW] = X86.opOUTSw;
this.aOps[0xC0] = X86.opGrp2bi;
this.aOps[0xC1] = X86.opGrp2wi;
this.aOps[X86.OPCODE.ENTER] = X86.opENTER;
this.aOps[X86.OPCODE.LEAVE] = X86.opLEAVE;
this.aOps[0xF1] = X86.opINT1;
this.aOpGrp4b[0x07] = X86.fnGRPInvalid;
this.aOpGrp4w[0x07] = X86.fnGRPInvalid;
if (this.model >= X86.MODEL_80286) {
@ -756,19 +755,23 @@ X86CPU.prototype.initProcessor = function()
this.OPFLAG_NOINTR8086 = 0; // used with instructions that should *not* set NOINTR on an 80286 (eg, non-SS segment loads)
this.aOps0F = X86Op0F.aOps0F;
this.aOps[0x0F] = X86OpXX.op0F;
this.aOps[X86.OPCODE.ARPL] = X86OpXX.opARPL;
this.aOps[X86.OPCODE.PUSHSP] = X86OpXX.opPUSHSP;
this.aOps0F = X86.aOps0F;
this.aOps[0x0F] = X86.op0F;
this.aOps[X86.OPCODE.ARPL] = X86.opARPL;
this.aOps[X86.OPCODE.PUSHSP] = X86.opPUSHSP;
if (I386 && this.model >= X86.MODEL_80386) {
this.aOps[X86.OPCODE.FS] = X86OpXX.opFS;
this.aOps[X86.OPCODE.GS] = X86OpXX.opGS;
this.aOps[X86.OPCODE.OS] = X86OpXX.opOS;
this.aOps[X86.OPCODE.AS] = X86OpXX.opAS;
this.aOps0F = X86Op0F.aOps0F.slice();
this.aOps0F[0x20] = X86Op0F.opMOVrcr;
this.aOps0F[0x22] = X86Op0F.opMOVcrr;
this.aOps[X86.OPCODE.FS] = X86.opFS;
this.aOps[X86.OPCODE.GS] = X86.opGS;
this.aOps[X86.OPCODE.OS] = X86.opOS;
this.aOps[X86.OPCODE.AS] = X86.opAS;
this.aOps0F = X86.aOps0F.slice();
this.aOps0F[0x20] = X86.opMOVrcr;
this.aOps0F[0x22] = X86.opMOVcrr;
this.aOps = this.aOps.concat(this.aOps);
for (var bOpcode in X86.aOpsD) {
this.aOps[parseInt(bOpcode, 10) + 256] = X86.aOpsD[bOpcode];
}
}
}
}
@ -896,7 +899,7 @@ X86CPU.prototype.resetRegs = function()
/*
* NOTE: Even though the 8086 doesn't have CR0 (aka MSW) and IDTR, we initialize them for ALL CPUs, so
* that functions like X86Help.opHelpINT() can use the same code for both. The 8086/8088 have no direct
* that functions like X86.fnINT() can use the same code for both. The 8086/8088 have no direct
* way of accessing or changing them, so this internal change should be perfectly safe for those processors.
*/
this.regCR0 = X86.CR0.MSW.ON;
@ -1075,6 +1078,7 @@ X86CPU.prototype.setAddrSize = function()
X86CPU.prototype.setDataSize = function()
{
this.opMem = this.aaOpMem[this.dataSize];
this.bOpcodeBias = (this.dataSize == 4? 256 : 0);
};
/**
@ -1091,6 +1095,7 @@ X86CPU.prototype.setSizes = function()
*/
this.addrSize = this.segCS.addrSize;
this.addrMask = this.segCS.addrMask;
/*
* It's also worth noting that instructions that implicitly use the stack also rely on STACK size,
* which is based on the BIG bit of the last descriptor loaded into SS; use the following segSS properties:
@ -1255,7 +1260,7 @@ X86CPU.prototype.setProtMode = function(fProt)
if (!fProt) {
this.printMessage("returning to real-mode");
}
this.aOpGrp6 = (fProt? X86Op0F.aOpGrp6Prot : X86Op0F.aOpGrp6Real);
this.aOpGrp6 = (fProt? X86.aOpGrp6Prot : X86.aOpGrp6Real);
this.segCS.updateMode(fProt);
this.segDS.updateMode(fProt);
this.segSS.updateMode(fProt);
@ -1718,6 +1723,38 @@ X86CPU.prototype.setSP = function(off)
/**
* getCF()
*
* Notes regarding carry following a 32-bit addition:
*
* The following table summarizes bit 31 of dst, src, and result, along with the expected carry bit:
*
* dst src res carry
* --- --- --- -----
* 0 0 0 0 no
* 0 0 1 0 no (there must have been a carry out of bit 30, but it was "absorbed")
* 0 1 0 1 yes (there must have been a carry out of bit 30, but it was NOT "absorbed")
* 0 1 1 0 no
* 1 0 0 1 yes (same as the preceding "yes" case)
* 1 0 1 0 no
* 1 1 0 1 yes (since the addition of two ones must always produce a carry)
* 1 1 1 1 yes (since the addition of two ones must always produce a carry)
*
* So, we could use (dst ^ ((dst ^ src) & (src ^ res))) >>> 15 to shift the calculated carry bit (bit 31)
* into the conventional SIZE_WORD position (bit 16); eg:
*
* resultZeroCarry = ((resultZeroCarry >>> 16) | (resultZeroCarry & 0xffff)) | (((dst ^ ((dst ^ src) & (src ^ resultZeroCarry))) >>> 15) & SIZE_WORD);
*
* Essentially, wed be cramming all 32 result bits into the low 16 bits (which would effectively represent the
* zero flag), and then setting bit 16 to the effective carry flag. This transforms the zero and carry conditions
* for a DWORD computation into the corresponding conditions for a WORD computation. This would slow down 32-bit
* addition, but it would allow 8-bit and 16-bit addition to remain fast. Languages that support 64-bit values in
* conjunction with bit-wise operators can omit that one-line transformation, allowing us to set SIZE_WORD to a
* 33-bit value, but sadly, we cannot do that in JavaScript.
*
* Alternatively, we could store the src and dst operands into their own result variables (eg, resultSrc and resultDst)
* and compute carry lazily, but that would affect MUCH more existing code (eg, all code that currently inspects carry
* with a single bit test). I think the DWORD-to-WORD flag conversion for 32-bit instructions that modify zero
* and/or carry) is a more reasonable first step.
*
* @this {X86CPU}
* @return {number}
*/
@ -2013,6 +2050,32 @@ X86CPU.prototype.getPS = function()
return (this.regPS & ~X86.PS.INDIRECT) | (this.getCF() | this.getPF() | this.getAF() | this.getZF() | this.getSF() | this.getOF());
};
/**
* setMSW(w)
*
* Factored out of x86op0f.js, since both opLMSW and opLOADALL are capable of setting a new MSW.
* The caller is responsible for assessing the appropriate cycle cost.
*
* @this {X86CPU}
* @param {number} w
*/
X86CPU.prototype.setMSW = function(w)
{
/*
* This instruction is always allowed to set MSW.PE, but it cannot clear MSW.PE once set;
* therefore, we always OR the previous value of MSW.PE into the new value before loading.
*/
w |= (this.regCR0 & X86.CR0.MSW.PE) | X86.CR0.MSW.ON;
this.regCR0 = (this.regCR0 & ~X86.CR0.MSW.MASK) | (w & X86.CR0.MSW.MASK);
/*
* Since the 80286 cannot return to real-mode via this instruction, the only transition we
* must worry about is to protected-mode. And don't worry, there's no harm calling setProtMode()
* if the CPU is already in protected-mode (we could certainly optimize the call out in that
* case, but this instruction isn't used frequently enough to warrant it).
*/
if (this.regCR0 & X86.CR0.MSW.PE) this.setProtMode(true);
};
/**
* setPS(regPS)
*
@ -2598,9 +2661,9 @@ X86CPU.prototype.getBytePrefetch = function(addr)
};
/**
* getWordPrefetch(addr)
* getShortPrefetch(addr)
*
* Return the next word from the prefetch queue. There are 3 cases to consider:
* Return the next short from the prefetch queue. There are 3 cases to consider:
*
* 1) Both bytes have been prefetched; no bytes need be fetched from memory
* 2) Only the low byte has been prefetched; the high byte must be fetched from memory
@ -2611,11 +2674,38 @@ X86CPU.prototype.getBytePrefetch = function(addr)
*
* @this {X86CPU}
* @param {number} addr is a physical (non-segmented) address
* @return {number} word (16-bit) value at that address
* @return {number} short (16-bit) value at that address
*/
X86CPU.prototype.getShortPrefetch = function(addr)
{
return this.getBytePrefetch(addr) | (this.getBytePrefetch(addr + 1) << 8);
};
/**
* getLongPrefetch(addr)
*
* Return the next long from the prefetch queue. Similar to getShortPrefetch(), we take the
* easy way out and call getShortPrefetch() twice.
*
* @this {X86CPU}
* @param {number} addr is a physical (non-segmented) address
* @return {number} long (32-bit) value at that address
*/
X86CPU.prototype.getLongPrefetch = function(addr)
{
return this.getShortPrefetch(addr) | (this.getShortPrefetch(addr + 2) << 16);
};
/**
* getWordPrefetch(addr)
*
* @this {X86CPU}
* @param {number} addr is a physical (non-segmented) address
* @return {number} short (16-bit) or long (32-bit value as appropriate
*/
X86CPU.prototype.getWordPrefetch = function(addr)
{
return this.getBytePrefetch(addr) | (this.getBytePrefetch(addr + 1) << 8);
return (I386 && this.addrSize == 4? this.getLongPrefetch(addr) : this.getShortPrefetch(addr));
};
/**
@ -2716,6 +2806,46 @@ X86CPU.prototype.getIPDisp = function()
return w & (I386? this.addrMask : 0xffff);
};
/**
* getIPShort()
*
* @this {X86CPU}
* @return {number} short at the current IP; IP advanced by 2
*/
X86CPU.prototype.getIPShort = function()
{
var w = (PREFETCH? this.getShortPrefetch(this.regLIP) : this.getShort(this.regLIP));
if (BACKTRACK) {
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMemHi);
}
this.regLIP += 2;
if (this.regLIP > this.regLIPLimit) {
this.setIP(this.regLIP - this.segCS.base);
}
return w;
};
/**
* getIPLong()
*
* @this {X86CPU}
* @return {number} long at the current IP; IP advanced by 4
*/
X86CPU.prototype.getIPLong = function()
{
var l = (PREFETCH? this.getLongPrefetch(this.regLIP) : this.getLong(this.regLIP));
if (BACKTRACK) {
this.bus.updateBackTrackCode(this.regLIP, this.backTrack.btiMemLo);
this.bus.updateBackTrackCode(this.regLIP + 1, this.backTrack.btiMemHi);
}
this.regLIP += 4;
if (this.regLIP > this.regLIPLimit) {
this.setIP(this.regLIP - this.segCS.base);
}
return l;
};
/**
* getIPWord()
*
@ -2868,14 +2998,14 @@ X86CPU.prototype.checkINTR = function()
this.intFlags &= ~X86.INTFLAG.INTR;
if (nIDT >= 0) {
this.intFlags &= ~X86.INTFLAG.HALT;
X86Help.opHelpINT.call(this, nIDT, null, 11);
X86.fnINT.call(this, nIDT, null, 11);
return true;
}
}
}
else if ((this.intFlags & X86.INTFLAG.TRAP)) {
this.intFlags &= ~X86.INTFLAG.TRAP;
X86Help.opHelpINT.call(this, X86.EXCEPTION.TRAP, null, 11);
X86.fnINT.call(this, X86.EXCEPTION.TRAP, null, 11);
return true;
}
}
@ -3162,7 +3292,7 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
this.nSnapCycles = this.nStepCycles;
}
this.aOps[this.getIPByte()].call(this);
this.aOps[this.getIPByte() + (I386? this.bOpcodeBias : 0)].call(this);
if (PREFETCH) {
var nSpareCycles = (this.nSnapCycles - this.nStepCycles) - this.nBusCycles;
@ -3217,10 +3347,6 @@ if (typeof APP_PCJS !== 'undefined') {
APP_PCJS.X86 = X86;
APP_PCJS.X86.X86CPU = X86CPU;
APP_PCJS.X86.X86Seg = X86Seg;
APP_PCJS.X86.X86Grps = X86Grps;
APP_PCJS.X86.X86Help = X86Help;
APP_PCJS.X86.X86Op0F = X86Op0F;
APP_PCJS.X86.X86OpXX = X86OpXX;
}
if (typeof module !== 'undefined') module.exports = X86CPU;

2444
modules/pcjs/lib/x86func.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,789 +0,0 @@
/**
* @fileoverview Implements PCjs 8086 opcode helpers.
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
* @version 1.0
* Created 2012-Sep-05
*
* 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 (typeof module !== 'undefined') {
var X86 = require("./x86");
var Messages = require("./messages");
}
var X86Help = {
/**
* @this {X86CPU}
* @param {number} dst (current value, ignored)
* @param {number} src (new value)
* @return {number} dst (updated value, from src)
*/
opHelpMOV: function(dst, src) {
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesMovRR : this.CYCLES.nOpCyclesMovRM) : this.CYCLES.nOpCyclesMovMR);
return src;
},
/**
* @this {X86CPU}
* @param {number} dst (current value, ignored)
* @param {number} src (new value)
* @return {number} dst (src is overridden, replaced with regMD16, as specified by opMOVwsr())
*/
opHelpMOVMD16: function(dst, src) {
return X86Help.opHelpMOV.call(this, dst, this.regMD16);
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpTESTb: function(dst, src) {
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesTestRR : this.CYCLES.nOpCyclesTestRM) : this.CYCLES.nOpCyclesTestRM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpTESTw: function(dst, src) {
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = dst & src;
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= (this.regEAWrite < 0? (this.regEA < 0? this.CYCLES.nOpCyclesTestRR : this.CYCLES.nOpCyclesTestRM) : this.CYCLES.nOpCyclesTestRM);
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*
* 80286_and_80287_Programmers_Reference_Manual_1987.pdf, p.B-44 (p.254) notes that:
*
* "The low 16 bits of the product of a 16-bit signed multiply are the same as those of an
* unsigned multiply. The three operand IMUL instruction can be used for unsigned operands as well."
*
* However, we still sign-extend the operands before multiplying, making it easier to range-check the result.
*
* (80186/80188 and up)
*/
opHelpIMUL8: function(dst, src) {
var result = ((src << 16) >> 16) * ((this.getIPByte() << 24) >> 24);
this.resultZeroCarry = this.resultAuxOverflow = this.resultParitySign = result;
this.resultSize = X86.RESULT.SIZE_BYTE;
/*
* TODO: Look into a more efficient way of setting/synchronizing CF and OF; this code works,
* but it somewhat defeats the purpose of the indirect result variables that we've set above.
*/
if (result > 32767 || result < -32768) {
this.setCF(); this.setOF();
} else {
this.clearCF(); this.clearOF();
}
result &= 0xffff;
if (DEBUG && DEBUGGER) this.traceLog('IMUL8', dst, src, null, this.getPS(), result);
/*
* NOTE: These are the cycle counts for the 80286; the 80186/80188 have slightly different values (ranges):
* 22-25 and 29-32 instead of 21 and 24, respectively. However, accurate cycle counts for the 80186/80188 is
* not super-critical. TODO: Fix this someday.
*/
this.nStepCycles -= (this.regEA < 0? 21 : 24);
return result;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*
* 80286_and_80287_Programmers_Reference_Manual_1987.pdf, p.B-44 (p.254) notes that:
*
* "The low 16 bits of the product of a 16-bit signed multiply are the same as those of an
* unsigned multiply. The three operand IMUL instruction can be used for unsigned operands as well."
*
* However, we still sign-extend the operands before multiplying, making it easier to range-check the result.
*
* (80186/80188 and up)
*/
opHelpIMUL16: function(dst, src) {
var result = ((src << 16) >> 16) * ((this.getIPWord() << 16) >> 16);
this.resultZeroCarry = this.resultAuxOverflow = this.resultParitySign = result;
this.resultSize = X86.RESULT.SIZE_WORD;
/*
* TODO: Look into a more efficient way of setting/synchronizing CF and OF; this code works,
* but it somewhat defeats the purpose of the indirect result variables that we've set above.
*/
if (result > 32767 || result < -32768) {
this.setCF(); this.setOF();
} else {
this.clearCF(); this.clearOF();
}
result &= 0xffff;
if (DEBUG && DEBUGGER) this.traceLog('IMUL16', dst, src, null, this.getPS(), result);
/*
* NOTE: These are the cycle counts for the 80286; the 80186/80188 have slightly different values (ranges):
* 22-25 and 29-32 instead of 21 and 24, respectively. However, accurate cycle counts for the 80186/80188 is
* not super-critical. TODO: Fix this someday.
*/
this.nStepCycles -= (this.regEA < 0? 21 : 24);
return result;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number} dst unchanged
*/
opHelpESC: function(dst, src) {
return dst;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpLEA: function(dst, src) {
if (this.regEA < 0) {
/*
* TODO: After reading http://www.os2museum.com/wp/undocumented-8086-opcodes/, it seems that this
* form of LEA (eg, "LEA AX,DX") simply returns the last calculated EA. Since we always reset regEA
* at the start of a new instruction, we would need to preserve the previous EA if we want to mimic
* that (undocumented) behavior.
*
* And for completeness, we would have to extend EA tracking beyond the usual ModRM instructions
* (eg, XLAT, instructions that modify the stack pointer, and string instructions). Anything else?
*/
X86Help.opHelpUndefined.call(this);
return dst;
}
this.nStepCycles -= this.CYCLES.nOpCyclesLEA;
return this.regEA;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpLDS: function(dst, src) {
if (this.regEA < 0) {
X86Help.opHelpUndefined.call(this);
return dst;
}
this.setDS(this.getShort(this.regEA + 2));
this.nStepCycles -= this.CYCLES.nOpCyclesLS;
return src;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpLES: function(dst, src) {
if (this.regEA < 0) {
X86Help.opHelpUndefined.call(this);
return dst;
}
this.setES(this.getShort(this.regEA + 2));
this.nStepCycles -= this.CYCLES.nOpCyclesLS;
return src;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpBOUND: function(dst, src) {
if (this.regEA < 0) {
/*
* Generate UD_FAULT (INT 0x06: Invalid Opcode) if src is not a memory operand.
*/
X86Help.opHelpInvalid.call(this);
return dst;
}
/*
* Note that BOUND performs signed comparisons, so we must transform all arguments into signed values.
*/
var wIndex = (dst << 16) >> 16;
var wLower = (this.getShort(this.regEA) << 16) >> 16;
var wUpper = (this.getShort(this.regEA + 2) << 16) >> 16;
this.nStepCycles -= this.CYCLES.nOpCyclesBound;
if (wIndex < wLower || wIndex > wUpper) {
/*
* The INT 0x05 handler must be called with CS:IP pointing to the BOUND instruction.
*
* TODO: Determine the cycle cost when a BOUND exception is triggered, over and above nOpCyclesBound.
*/
this.setIP(this.opLIP - this.segCS.base);
X86Help.opHelpINT.call(this, X86.EXCEPTION.BOUND_ERR, null, 0);
}
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpARPL: function(dst, src) {
this.nStepCycles -= (10 + (this.regEA < 0? 0 : 1));
if ((dst & X86.SEL.RPL) < (src & X86.SEL.RPL)) {
dst = (dst & ~X86.SEL.RPL) | (src & X86.SEL.RPL);
this.setZF();
return dst;
}
this.clearZF();
return dst;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpLAR: function(dst, src) {
this.nStepCycles -= (14 + (this.regEA < 0? 0 : 2));
/*
* Currently, segVER.load() will return an error only if the selector is beyond the bounds of the
* descriptor table or the descriptor is not for a segment.
*
* TODO: This instruction's 80286 documentation does not discuss conforming code segments; determine
* if we need a special check for them.
*/
if (this.segVER.load(src, true) != X86.ADDR_INVALID) {
if (this.segVER.dpl >= this.segCS.cpl && this.segVER.dpl >= (src & X86.SEL.RPL)) {
this.setZF();
return this.segVER.acc & X86.DESC.ACC.MASK;
}
}
this.clearZF();
return dst;
},
/**
* @this {X86CPU}
* @param {number} dst
* @param {number} src (the selector)
* @return {number}
*/
opHelpLSL: function(dst, src) {
/*
* TODO: Is this an invalid operation if regEAWrite is set? dst is required to be a register.
*/
this.nStepCycles -= (14 + (this.regEA < 0? 0 : 2));
/*
* Currently, segVER.load() will return an error only if the selector is beyond the bounds of the
* descriptor table or the descriptor is not for a segment.
*
* TODO: LSL is explicitly documented as ALSO requiring a non-null selector, so we check X86.SEL.MASK;
* are there any other instructions that were, um, less explicit but also require a non-null selector?
*/
if ((src & X86.SEL.MASK) && this.segVER.load(src, true) != X86.ADDR_INVALID) {
var fConforming = ((this.segVER.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) == X86.DESC.ACC.TYPE.CODE_CONFORMING);
if ((fConforming || this.segVER.dpl >= this.segCS.cpl) && this.segVER.dpl >= (src & X86.SEL.RPL)) {
this.setZF();
return this.segVER.limit;
}
}
this.clearZF();
return dst;
},
/**
* opHelpXCHGrb(dst, src)
*
* If an instruction like "XCHG AL,AH" was a traditional "op dst,src" instruction, dst would contain AL,
* src would contain AH, and we would return src, which the caller would then store in AL, and we'd be done.
*
* However, that's only half of what XCHG does, so THIS function must perform the other half; in the previous
* example, that entails storing AL (dst) into AH (src).
*
* BACKTRACK support is incomplete without also passing bti values as parameters, because the caller will
* store btiAH in btiAL, but the original btiAL will be lost. Similarly, if src is a memory operand, the
* caller will store btiEALo in btiAL, but again, the original btiAL will be lost.
*
* BACKTRACK support for memory operands could be fixed by decoding the dst register in order to determine the
* corresponding bti and then temporarily storing it in btiEALo around the setEAByte() call below. Register-only
* XCHGs would require a more extensive hack. For now, I'm going to live with one-way BACKTRACK support here.
*
* TODO: Implement full BACKTRACK support for XCHG instructions.
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpXCHGrb: function(dst, src) {
if (this.regEA < 0) {
/*
* Decode which register was src
*/
switch (this.bModRM & 0x7) {
case 0x0: // AL
this.regEAX = (this.regEAX & ~0xff) | dst;
break;
case 0x1: // CL
this.regECX = (this.regECX & ~0xff) | dst;
break;
case 0x2: // DL
this.regEDX = (this.regEDX & ~0xff) | dst;
break;
case 0x3: // BL
this.regEBX = (this.regEBX & ~0xff) | dst;
break;
case 0x4: // AH
this.regEAX = (this.regEAX & 0xff) | (dst << 8);
break;
case 0x5: // CH
this.regECX = (this.regECX & 0xff) | (dst << 8);
break;
case 0x6: // DH
this.regEDX = (this.regEDX & 0xff) | (dst << 8);
break;
case 0x7: // BH
this.regEBX = (this.regEBX & 0xff) | (dst << 8);
break;
default:
break; // there IS no other case, but JavaScript inspections don't know that
}
this.nStepCycles -= this.CYCLES.nOpCyclesXchgRR;
} else {
/*
* This is a case where the ModRM decoder that's calling us didn't know it should have called modEAByte()
* instead of getEAByte(), so we compensate by updating regEAWrite. However, setEAByte() has since been
* changed to revalidate the write using segEA:offEA, so updating regEAWrite here isn't strictly necessary.
*/
this.regEAWrite = this.regEA;
this.setEAByte(dst);
this.nStepCycles -= this.CYCLES.nOpCyclesXchgRM;
}
return src;
},
/**
* opHelpXCHGrw(dst, src)
*
* If an instruction like "XCHG AX,DX" was a traditional "op dst,src" instruction, dst would contain AX,
* src would contain DX, and we would return src, which the caller would then store in AX, and we'd be done.
*
* However, that's only half of what XCHG does, so THIS function must perform the other half; in the previous
* example, that entails storing AX (dst) into DX (src).
*
* TODO: Implement full BACKTRACK support for XCHG instructions (see opHelpXCHGrb comments).
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
opHelpXCHGrw: function(dst, src) {
if (this.regEA < 0) {
/*
* Decode which register was src
*/
switch (this.bModRM & 0x7) {
case 0x0: // AX
this.regEAX = dst;
break;
case 0x1: // CX
this.regECX = dst;
break;
case 0x2: // DX
this.regEDX = dst;
break;
case 0x3: // BX
this.regEBX = dst;
break;
case 0x4: // SP
this.setSP(dst);
break;
case 0x5: // BP
this.regEBP = dst;
break;
case 0x6: // SI
this.regESI = dst;
break;
case 0x7: // DI
this.regEDI = dst;
break;
default:
break; // there IS no other case, but JavaScript inspections don't know that
}
this.nStepCycles -= this.CYCLES.nOpCyclesXchgRR;
} else {
/*
* This is a case where the ModRM decoder that's calling us didn't know it should have called modEAWord()
* instead of getEAWord(), so we compensate by updating regEAWrite. However, setEAWord() has since been
* changed to revalidate the write using segEA:offEA, so updating regEAWrite here isn't strictly necessary.
*/
this.regEAWrite = this.regEA;
this.setEAWord(dst);
this.nStepCycles -= this.CYCLES.nOpCyclesXchgRM;
}
return src;
},
/**
* opHelpLMSW(w)
*
* Factored out of x86op0f.js, since both opLMSW and opLOADALL are capable of loading a new MSW.
* The caller is responsible for assessing the appropriate cycle cost.
*
* @this {X86CPU}
* @param {number} w
*/
opHelpLMSW: function(w) {
/*
* This instruction is always allowed to set MSW.PE, but it cannot clear MSW.PE once set;
* therefore, we always OR the previous value of MSW.PE into the new value before loading.
*/
w |= (this.regCR0 & X86.CR0.MSW.PE) | X86.CR0.MSW.ON;
this.regCR0 = (this.regCR0 & ~X86.CR0.MSW.MASK) | (w & X86.CR0.MSW.MASK);
/*
* Since the 80286 cannot return to real-mode via this instruction, the only transition we
* must worry about is to protected-mode. And don't worry, there's no harm calling setProtMode()
* if the CPU is already in protected-mode (we could certainly optimize the call out in that
* case, but this instruction isn't used frequently enough to warrant it).
*/
if (this.regCR0 & X86.CR0.MSW.PE) this.setProtMode(true);
},
/**
* opHelpLCR0(l)
*
* This called on behalf of 80386 opcodes only (ie, MOV CR0,reg).
*
* TODO: Determine which CR0 bits, if any, cannot be modified by MOV CR0,reg.
*
* @this {X86CPU}
* @param {number} l
*/
opHelpLCR0: function(l) {
this.regCR0 = l;
this.setProtMode(!!(this.regCR0 & X86.CR0.MSW.PE));
},
/**
* opHelpCALLF(off, sel)
*
* For protected-mode, this function must attempt to load the new code segment first, because if the new segment
* requires a change in privilege level, the return address must be pushed on the NEW stack, not the current stack.
*
* @this {X86CPU}
* @param {number} off
* @param {number} sel
*/
opHelpCALLF: function(off, sel) {
var regCS = this.getCS();
var regEIP = this.getIP();
if (this.setCSIP(off, sel, true) != null) {
this.pushWord(regCS);
this.pushWord(regEIP);
}
},
/**
* opHelpRETF(n)
*
* For protected-mode, this function must be prepared to pop any arguments off the current stack AND
* whatever stack we may have switched to (setCSIP() returns true only when a stack switch has occurred).
*
* @this {X86CPU}
* @param {number} n
*/
opHelpRETF: function(n) {
var regEIP = this.popWord();
var regCS = this.popWord();
n <<= (this.dataSize >> 2);
if (n) this.setSP(this.getSP() + n); // TODO: optimize
if (this.setCSIP(regEIP, regCS, false)) {
if (n) this.setSP(this.getSP() + n); // TODO: optimize
/*
* As per Intel documentation: "If any of [the DS or ES] registers refer to segments whose DPL is
* less than the new CPL (excluding conforming code segments), the segment register is loaded with
* the null selector."
*
* TODO: I'm not clear on whether a conforming code segment must also be marked readable, so I'm playing
* it safe and using CODE_CONFORMING instead of CODE_CONFORMING_READABLE. Also, for the record, I've not
* seen this situation occur in OS/2 1.0 yet.
*/
if ((this.segDS.sel & X86.SEL.MASK) && this.segDS.dpl < this.segCS.cpl && (this.segDS.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
this.assert(false); // I'm not asserting this is bad, I just want to see it in action
this.segDS.load(0);
}
if ((this.segES.sel & X86.SEL.MASK) && this.segES.dpl < this.segCS.cpl && (this.segES.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
this.assert(false); // I'm not asserting this is bad, I just want to see it in action
this.segES.load(0);
}
}
if (n == 2 && this.cIntReturn) this.checkIntReturn(this.regLIP);
},
/**
* opHelpINT(nIDT, nError, nCycles)
*
* NOTE: We no longer use setCSIP(), because it always loads the new CS using segCS.load(), which
* only knows how to load GDT and LDT descriptors, whereas interrupts must use setCS.loadIDT(), which
* deals exclusively with IDT descriptors.
*
* This means we must take care to replicate critical features of setCSIP(); eg, setting segCS.fCall before
* calling loadIDT(), updating LIP, and flushing the prefetch queue.
*
* @this {X86CPU}
* @param {number} nIDT
* @param {number|null|undefined} nError
* @param {number} nCycles (in addition to the default of nOpCyclesInt)
*/
opHelpINT: function(nIDT, nError, nCycles) {
/*
* TODO: We assess the cycle cost up front, because otherwise, if loadIDT() fails, no cost may be assessed.
*/
this.nStepCycles -= this.CYCLES.nOpCyclesInt + nCycles;
this.segCS.fCall = true;
var regPS = this.getPS();
var regCS = this.getCS();
var regEIP = this.getIP();
var addr = this.segCS.loadIDT(nIDT);
if (addr != X86.ADDR_INVALID) {
this.regLIP = addr;
if (PREFETCH) this.flushPrefetch(this.regLIP);
this.pushWord(regPS);
this.pushWord(regCS);
this.pushWord(regEIP);
if (nError != null) this.pushWord(nError);
this.nFault = -1;
}
},
/**
* opHelpIRET()
*
* @this {X86CPU}
*/
opHelpIRET: function() {
/*
* TODO: We assess a fixed cycle cost up front, because at the moment, switchTSS() doesn't assess anything.
*/
this.nStepCycles -= this.CYCLES.nOpCyclesIRet;
if (this.regCR0 & X86.CR0.MSW.PE) {
if (this.regPS & X86.PS.NT) {
var addrNew = this.segTSS.base;
var sel = this.getShort(addrNew + X86.TSS.PREV_TSS);
X86Seg.switchTSS.call(this.segCS, sel, false);
return;
}
}
var cpl = this.segCS.cpl;
var regEIP = this.popWord();
var regCS = this.popWord();
var regPS = this.popWord();
if (this.setCSIP(regEIP, regCS, false) != null) {
this.setPS(regPS, cpl);
if (this.cIntReturn) this.checkIntReturn(this.regLIP);
}
},
/**
* opHelpDIVOverflow()
*
* @this {X86CPU}
*/
opHelpDIVOverflow: function() {
this.setIP(this.opLIP - this.segCS.base);
/*
* TODO: Determine the proper cycle cost.
*/
X86Help.opHelpINT.call(this, X86.EXCEPTION.DIV_ERR, null, 2);
},
/**
* opHelpFault(nFault, nError, fHalt)
*
* Helper to dispatch faults.
*
* @this {X86CPU}
* @param {number} nFault
* @param {number} [nError]
* @param {boolean} [fHalt] will halt the CPU if true *and* a Debugger is loaded
*/
opHelpFault: function(nFault, nError, fHalt)
{
if (!this.aFlags.fComplete) {
this.printMessage("Fault " + str.toHexByte(nFault) + " blocked by Debugger", Messages.WARN);
this.setIP(this.opLIP - this.segCS.base);
return;
}
var fDispatch = false;
if (this.model >= X86.MODEL_80186) {
if (this.nFault < 0) {
/*
* Single-fault (error code is passed through, and the responsible instruction is restartable)
*/
this.setIP(this.opLIP - this.segCS.base);
fDispatch = true;
} else if (this.nFault != X86.EXCEPTION.DF_FAULT) {
/*
* Double-fault (error code is always zero, and the responsible instruction is not restartable)
*/
nError = 0;
nFault = X86.EXCEPTION.DF_FAULT;
fDispatch = true;
} else {
/*
* Triple-fault (usually referred to in Intel literature as a "shutdown", but at least on the 80286,
* it's actually a "reset")
*/
X86Help.opHelpFaultMessage.call(this, -1, 0, fHalt);
this.resetRegs();
return;
}
}
if (X86Help.opHelpFaultMessage.call(this, nFault, nError, fHalt)) {
fDispatch = false;
}
if (fDispatch) X86Help.opHelpINT.call(this, this.nFault = nFault, nError, 0);
/*
* Since this fault is likely being issued in the context of an instruction that hasn't finished
* executing, and since we currently don't do anything to interrupt that execution (eg, throw a
* JavaScript exception), we should shut off all further reads/writes for the current instruction.
*
* That's easy for any EA-based memory accesses: simply set both the NOREAD and NOWRITE flags.
* However, there are also direct, non-EA-based memory accesses to consider. A perfect example is
* opPUSHA(): if a GP fault occurs on any PUSH other than the last, a subsequent PUSH is likely to
* cause another fault, which we will misinterpret as a double-fault.
*
* TODO: Throw a special JavaScript exception that cpu.js must intercept and quietly ignore.
*/
this.opFlags &= ~(X86.OPFLAG.NOREAD | X86.OPFLAG.NOWRITE);
},
/**
* opHelpFaultMessage()
*
* Aside from giving the Debugger an opportunity to report every fault, this also gives us the ability to
* halt exception processing in tracks: return true to prevent the fault handler from being dispatched.
*
* At the moment, the only Debugger control you have over fault interception is setting MESSAGE.FAULT, which
* will display faults as they occur, and MESSAGE.HALT, which will halt after any Debugger message, including
* MESSAGE.FAULT. If you want execution to continue after halting, clear MESSAGE.FAULT and/or MESSAGE.HALT,
* or single-step over the offending instruction, which will allow the fault to be dispatched.
*
* @this {X86CPU}
* @param {number} nFault
* @param {number} [nError]
* @param {boolean} [fHalt] true if the CPU should always be halted, false if "it depends"
* @return {boolean|undefined} true to block the fault (often desirable when fHalt is true), otherwise dispatch it
*/
opHelpFaultMessage: function(nFault, nError, fHalt)
{
var bitsMessage = Messages.FAULT;
var bOpcode = this.bus.getByteDirect(this.regLIP);
/*
* OS/2 1.0 uses an INT3 (0xCC) opcode in conjunction with an invalid IDT to trigger a triple-fault
* reset and return to real-mode, and these resets happen quite frequently during boot; for example,
* OS/2 startup messages are displayed using a series of INT 0x10 BIOS calls for each character, and
* each series of BIOS calls requires a round-trip mode switch.
*
* Since we really only want to halt on "bad" faults, not "good" (ie, intentional) faults, we take
* advantage of the fact that all 3 faults comprising the triple-fault point to an INT3 (0xCC) opcode,
* and so whenever we see that opcode, we ignore the caller's fHalt flag, and suppress FAULT messages
* unless CPU messages are also enabled.
*
* When a triple fault shows up, nFault is -1; it displays as "ff" because we display nFault as a byte.
*/
if (bOpcode == X86.OPCODE.INT3) {
fHalt = false;
bitsMessage |= Messages.CPU;
}
/*
* Similarly, the PC AT ROM BIOS deliberately generates a couple of GP faults as part of the POST
* (Power-On Self Test); we don't want to ignore those, but we don't want to halt on them either. We
* detect those faults by virtue of the LIP being in the range %0F0000 to %0FFFFF.
*/
if (this.regLIP >= 0x0F0000 && this.regLIP <= 0x0FFFFF) {
fHalt = false;
}
/*
* However, the foregoing notwithstanding, if MESSAGE.HALT is enabled along with all the other required
* MESSAGE bits, then we want to halt regardless.
*/
if (this.messageEnabled(bitsMessage | Messages.HALT)) {
fHalt = true;
}
if (this.messageEnabled(bitsMessage) || fHalt) {
var sMessage = (fHalt? '\n' : '') + "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode 0x" + str.toHexByte(bOpcode) + " at " + this.dbg.hexOffset(this.getIP(), this.getCS()) + " (%" + str.toHex(this.regLIP, 6) + ")";
var fRunning = this.aFlags.fRunning;
if (this.printMessage(sMessage, bitsMessage)) {
if (fHalt) {
/*
* By setting fHalt to fRunning (which is true while running but false while single-stepping),
* this allows a fault to be dispatched when you single-step over a faulting instruction; you can
* then continue single-stepping into the fault handler, or start running again.
*
* Note that we had to capture fRunning before calling printMessage(), because if MESSAGE.HALT
* is set, printMessage() will have already halted the CPU.
*/
fHalt = fRunning;
this.dbg.stopCPU();
}
} else {
/*
* If printMessage() returned false, then messageEnabled() must have returned false as well, which
* means that fHalt must be true. Which means we should shut the machine down.
*/
this.assert(fHalt);
this.notice(sMessage);
this.stopCPU();
}
}
return fHalt;
},
/**
* @this {X86CPU}
*/
opHelpInvalid: function() {
X86Help.opHelpFault.call(this, X86.EXCEPTION.UD_FAULT);
this.stopCPU();
},
/**
* @this {X86CPU}
*/
opHelpUndefined: function() {
this.setIP(this.opLIP - this.segCS.base);
this.setError("Undefined opcode 0x" + str.toHexByte(this.bus.getByteDirect(this.regLIP)) + " at 0x" + str.toHex(this.regLIP));
this.stopCPU();
}
};
if (typeof module !== 'undefined') module.exports = X86Help;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,6 @@ if (typeof module !== 'undefined') {
var str = require("../../shared/lib/strlib");
var Messages = require("./messages");
var X86 = require("./x86");
var X86Help = require("./x86help");
}
/**
@ -190,7 +189,7 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
return this.loadDesc8(addrDesc, sel, fSuppress);
}
if (!fSuppress) {
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
}
}
return X86.ADDR_INVALID;
@ -237,14 +236,14 @@ X86Seg.loadIDTProt = function loadIDTProt(nIDT)
if (addrDesc + 7 <= cpu.addrIDTLimit) {
return this.loadDesc8(addrDesc, nIDT) + cpu.regEIP;
}
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
return X86.ADDR_INVALID;
};
/**
* checkReadReal(off, cb, fSuppress)
*
* TODO: Invoke X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off is 0xffff and cb is 1;
* TODO: Invoke X86.fnFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off is 0xffff and cb is 1;
* also, whether or not the opHelpFault() call should include an error code, since this is happening in real-mode.
*
* @this {X86Seg}
@ -261,7 +260,7 @@ X86Seg.checkReadReal = function checkReadReal(off, cb, fSuppress)
/**
* checkWriteReal(off, cb, fSuppress)
*
* TODO: Invoke X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off is 0xffff and cb is 1;
* TODO: Invoke X86.fnFault.call(this.cpu, X86.EXCEPTION.GP_FAULT) if off is 0xffff and cb is 1;
* also, whether or not the opHelpFault() call should include an error code, since this is happening in real-mode.
*
* @this {X86Seg}
@ -321,7 +320,7 @@ X86Seg.checkReadProtDown = function checkReadProtDown(off, cb, fSuppress)
X86Seg.checkReadProtDisallowed = function checkReadProtDisallowed(off, cb, fSuppress)
{
if (!fSuppress) {
X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, 0);
X86.fnFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, 0);
}
return X86.ADDR_INVALID;
};
@ -372,7 +371,7 @@ X86Seg.checkWriteProtDown = function checkWriteProtDown(off, cb, fSuppress)
X86Seg.checkWriteProtDisallowed = function checkWriteProtDisallowed(off, cb, fSuppress)
{
if (!fSuppress) {
X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, 0);
X86.fnFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, 0);
}
return X86.ADDR_INVALID;
};
@ -411,7 +410,7 @@ X86Seg.switchTSS = function switchTSS(selNew, fNest)
var selOld = cpu.segTSS.sel;
if (!fNest) {
if (cpu.segTSS.type != X86.DESC.ACC.TYPE.TSS_BUSY) {
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.TS_FAULT, selNew, true);
X86.fnFault.call(cpu, X86.EXCEPTION.TS_FAULT, selNew, true);
return false;
}
cpu.setShort(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, (cpu.segTSS.acc & ~X86.DESC.ACC.TYPE.TSS_BUSY) | X86.DESC.ACC.TYPE.TSS);
@ -425,7 +424,7 @@ X86Seg.switchTSS = function switchTSS(selNew, fNest)
}
if (fNest) {
if (cpu.segTSS.type == X86.DESC.ACC.TYPE.TSS_BUSY) {
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew, true);
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew, true);
return false;
}
cpu.setShort(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, cpu.segTSS.acc |= X86.DESC.ACC.TYPE.TSS_BUSY);
@ -502,7 +501,7 @@ X86Seg.prototype.loadAcc = function(sel, fGDT)
return cpu.getShort(addrDesc + X86.DESC.ACC.OFFSET);
}
}
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
return X86.DESC.ACC.INVALID;
};
@ -684,13 +683,13 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
return this.base;
}
cpu.assert(false);
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, nFaultError, true);
if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nFaultError, true);
base = X86.ADDR_INVALID;
break;
}
else if (fGate !== false) {
cpu.assert(false);
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
base = X86.ADDR_INVALID;
break;
}
@ -719,7 +718,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
*
* Anyway, because of this, if acc is zero, we won't set fHalt on this GP_FAULT.
*/
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, !!acc);
if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, !!acc);
base = X86.ADDR_INVALID;
break;
}
@ -727,14 +726,14 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
}
else if (this.id == X86Seg.ID.STACK) {
if (!selMasked || type < X86.DESC.ACC.TYPE.DATA_READONLY || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.READABLE)) == X86.DESC.ACC.TYPE.CODE) {
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
base = X86.ADDR_INVALID;
break;
}
}
else if (this.id == X86Seg.ID.TSS) {
if (!selMasked || type != X86.DESC.ACC.TYPE.TSS && type != X86.DESC.ACC.TYPE.TSS_BUSY) {
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.TS_FAULT, sel, true);
if (!fSuppress) X86.fnFault.call(cpu, X86.EXCEPTION.TS_FAULT, sel, true);
base = X86.ADDR_INVALID;
break;
}

View file

@ -106,8 +106,9 @@
"./modules/pcjs/lib/x86.js",
"./modules/pcjs/lib/x86seg.js",
"./modules/pcjs/lib/x86cpu.js",
"./modules/pcjs/lib/x86grps.js",
"./modules/pcjs/lib/x86help.js",
"./modules/pcjs/lib/x86func.js",
"./modules/pcjs/lib/x86opxx.js",
"./modules/pcjs/lib/x86op0f.js",
"./modules/pcjs/lib/x86modb.js",
"./modules/pcjs/lib/x86modw.js",
"./modules/pcjs/lib/x86modb16.js",
@ -115,8 +116,6 @@
"./modules/pcjs/lib/x86modb32.js",
"./modules/pcjs/lib/x86modw32.js",
"./modules/pcjs/lib/x86modsib.js",
"./modules/pcjs/lib/x86opxx.js",
"./modules/pcjs/lib/x86op0f.js",
"./modules/pcjs/lib/chipset.js",
"./modules/pcjs/lib/rom.js",
"./modules/pcjs/lib/ram.js",

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff