CPU function reorg
This commit is contained in:
parent
f50612e7ca
commit
1dc0aeccd6
16 changed files with 9794 additions and 9015 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, we’d 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
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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue