Getting ready to retire old 16-bit flags code

This commit is contained in:
Jeff Parsons 2015-03-16 14:39:03 -07:00 committed by jeffpar
commit 72cf8d1f4d
12 changed files with 4078 additions and 3318 deletions

View file

@ -124,8 +124,8 @@ memory.
> The B0 stepping became available around the middle of 1986. As is the case with earlier steppings, the > The B0 stepping became available around the middle of 1986. As is the case with earlier steppings, the
"B0" marking can sometimes be found on the outside of the device. If not, the code "S40336" or "S40337" may "B0" marking can sometimes be found on the outside of the device. If not, the code "S40336" or "S40337" may
appear. Starting with the B0 stepping, the 80386 would leave the component identifier, 3, in DH and the appear. Starting with the B0 stepping, the 80386 would leave the component identifier, 3, in DH and the
revision identifier, also 3, in DL [Surprisingly, these are the same values that the B1 stepping reports revision identifier, also 3, in DL [These B0 values need to be independently confirmed, because they are
as well. -JP] the same values reported for the B1 stepping. -JP]
> + *Interrupts and Privilege Violations*: If a hardware interrupt occurs immediately before an IOPL-sensitive > + *Interrupts and Privilege Violations*: If a hardware interrupt occurs immediately before an IOPL-sensitive
instruction when CPL is greater than IOPL, the 80386 behaves erratically. Generally, it will produce spurious instruction when CPL is greater than IOPL, the 80386 behaves erratically. Generally, it will produce spurious
@ -164,7 +164,7 @@ either the loop counter or the string index.
new ones were either introduced or discovered. The best known of these was the widely publicized multiply new ones were either introduced or discovered. The best known of these was the widely publicized multiply
failure discovered in mid-1987. The B1 stepping is identifiable either by the "B1" mark or by the code failure discovered in mid-1987. The B1 stepping is identifiable either by the "B1" mark or by the code
"S40343," "S40344," or "S40362." As is the case with the B0 stepping, the B1 revision leaves a binary three "S40343," "S40344," or "S40362." As is the case with the B0 stepping, the B1 revision leaves a binary three
in DL after reset. [Surprisingly, these are the same values that the B0 stepping reports as well. -JP] in DL after reset. [Again, I would like to see the B0 values independently confirmed. -JP]
> + *IBTS and XBTS Instructions Removed*: The Insert Bit String (IBTS) and Extract Bit String (XBTS) > + *IBTS and XBTS Instructions Removed*: The Insert Bit String (IBTS) and Extract Bit String (XBTS)
instructions were removed from the 80386's instruction set. It was determined that they took up too much instructions were removed from the 80386's instruction set. It was determined that they took up too much
@ -213,13 +213,12 @@ in the first byte of a page that will cause a page fault (either because it is n
impending privilege violation), the 80386 hangs. The processor will remain hung until it receives an interrupt. impending privilege violation), the 80386 hangs. The processor will remain hung until it receives an interrupt.
[See Intel Errata #17 below] [See Intel Errata #17 below]
> The following sample program has been calculated to produce the [multiplication] error. An 80386 that > The following sample program has been calculated to produce the [aforementioned multiplication] error.
fails one or more of these multiply instructions is obviously faulty. However, passing does not guarantee An 80386 that fails one or more of these multiply instructions is obviously faulty. However, passing does not
a perfect part. To their credit, Intel agreed to test all 80386s for a limited time and report on their guarantee a perfect part. To their credit, Intel agreed to test all 80386s for a limited time and report on their
success or failure. Since then, all 80386s have been tested before shipping. Those that fail have been marked success or failure. Since then, all 80386s have been tested before shipping. Those that fail have been marked
"For Sixteen-Bit Software Only." [I believe the exact marking was "16 BIT S/W ONLY" -JP] Those that passed "For Sixteen-Bit Software Only." [To be exact: "16 BIT S/W ONLY" -JP] Those that passed have been marked with
have been marked with a double sigma sign. All 80386s produced after the B1 stepping should be free of this a double sigma sign. All 80386s produced after the B1 stepping should be free of this defect.
defect.
; Perform various 16-bit and 32-bit multiply operations... ; Perform various 16-bit and 32-bit multiply operations...
@ -324,8 +323,9 @@ Most of the information I have obtained about the 80386 begins with the B1 stepp
ii m c i '85 '86 | ii m c i '85 '86 |
| |
So, the B1 stepping set DL to 0x03 on reset. It also seems a safe bet that the revision number So, the B1 stepping set DL to 0x03 on reset. This would lead one to believe that the revision number
for a B0 stepping was 0x02. Does that mean the revision number for the A0 was 0x01? I can only guess. for a B0 stepping was 0x02, but according to Turley (see above), the B0 and B1 steppings report the *same*
revision; as I noted above, it would be nice to see some independent confirmation.
The 80386 CPU on my Compaq DeskPro 386 "Version 2" System Board is labeled as: The 80386 CPU on my Compaq DeskPro 386 "Version 2" System Board is labeled as:
@ -589,7 +589,7 @@ Here's more information on the opcodes (IBTS and XBTS) that were removed from th
+++++++++++++++++++++++ +++++++++++++++++++++++
Physical Form: IBTS r/m16,AX,CL,r16 Physical Form: IBTS r/m16,AX,CL,r16
IBTS r/m32,EAX,CL,r32 IBTS r/m32,EAX,CL,r32
COP (Code of Operation) : 0FH A7H Postbyte COP (Code of Operation) : 0FH A7H
Clocks: IBTS Clocks: IBTS
80386: 12/19 80386: 12/19
@ -623,7 +623,7 @@ Here's more information on the opcodes (IBTS and XBTS) that were removed from th
+++++++++++++++++++++++ +++++++++++++++++++++++
Physical Form: XBTS r16,r/m16,AX,CL Physical Form: XBTS r16,r/m16,AX,CL
XBTS r32,r/m32,EAX,CL XBTS r32,r/m32,EAX,CL
COP (Code of Operation) : 0FH A6H Postbyte COP (Code of Operation) : 0FH A6H
Clocks: XBTS Clocks: XBTS
80386: 6/13 80386: 6/13

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -424,26 +424,28 @@ CPU.prototype.displayChecksum = function()
}; };
/** /**
* displayReg(sReg, nVal, cch) * displayValue(sLabel, nValue, cch)
*
* This is principally for displaying register values, but in reality, it can be used to display any
* numeric (hex) value bound to the given label.
* *
* @this {CPU} * @this {CPU}
* @param {string} sReg * @param {string} sLabel
* @param {number} nVal * @param {number} nValue
* @param {number} [cch] default is 4 * @param {number} cch
*/ */
CPU.prototype.displayReg = function(sReg, nVal, cch) CPU.prototype.displayValue = function(sLabel, nValue, cch)
{ {
if (this.bindings[sReg]) { if (this.bindings[sLabel]) {
if (cch === undefined) cch = 4; if (nValue === undefined) {
if (nVal === undefined) { this.setError("Value for " + sLabel + " is invalid");
this.setError("Register " + sReg + " is invalid");
this.stopCPU(); this.stopCPU();
} }
var sVal; var sVal;
if (!this.aFlags.fRunning || this.aFlags.fDisplayLiveRegs) { if (!this.aFlags.fRunning || this.aFlags.fDisplayLiveRegs) {
sVal = str.toHex(nVal, cch); sVal = str.toHex(nValue, cch);
} else { } else {
sVal = "----".substr(0, cch); sVal = "--------".substr(0, cch);
} }
/* /*
* TODO: Determine if this test actually avoids any redrawing when a register hasn't changed, and/or if * TODO: Determine if this test actually avoids any redrawing when a register hasn't changed, and/or if
@ -451,7 +453,7 @@ CPU.prototype.displayReg = function(sReg, nVal, cch)
* string values that will have to garbage-collected), and/or if this is actually slower, and/or if I'm being * string values that will have to garbage-collected), and/or if this is actually slower, and/or if I'm being
* too obsessive. * too obsessive.
*/ */
if (this.bindings[sReg].textContent != sVal) this.bindings[sReg].textContent = sVal; if (this.bindings[sLabel].textContent != sVal) this.bindings[sLabel].textContent = sVal;
} }
}; };

View file

@ -117,6 +117,13 @@ var BUGS_8086 = false;
*/ */
var I386 = true; var I386 = true;
/**
* @define {boolean}
*
* Retain support for old flags.
*/
var OLDFLAGS = true;
/** /**
* @define {boolean} * @define {boolean}
* *

View file

@ -244,8 +244,8 @@ RAM.init = function()
* *
* DeskPro 386 machines came with a minimum of 1Mb of RAM, which could be configured (via jumpers) * DeskPro 386 machines came with a minimum of 1Mb of RAM, which could be configured (via jumpers)
* for 256Kb, 512Kb or 640Kb of conventional memory, starting at address 0x00000000, with the * for 256Kb, 512Kb or 640Kb of conventional memory, starting at address 0x00000000, with the
* remainder (768Kb, 512Kb, or 384Kb) accessible only at addresses just below 0x01000000. This * remainder (768Kb, 512Kb, or 384Kb) accessible only at addresses just below 0x01000000. In PCjs,
* second chunk of RAM must have an ID of "ramCPQ". * this second chunk of RAM must be separately allocated, with an ID of "ramCPQ".
* *
* The typical configuration was 640Kb of conventional memory, leaving 384Kb accessible at 0x00FA0000. * The typical configuration was 640Kb of conventional memory, leaving 384Kb accessible at 0x00FA0000.
* Presumably, the other configurations (256Kb and 512Kb) would leave 768Kb and 512Kb accessible at * Presumably, the other configurations (256Kb and 512Kb) would leave 768Kb and 512Kb accessible at
@ -253,8 +253,11 @@ RAM.init = function()
* *
* The DeskPro 386 also contained two memory-mapped registers at 0x80C00000. The first is a write-only * The DeskPro 386 also contained two memory-mapped registers at 0x80C00000. The first is a write-only
* mapping register that provides the ability to map the 128Kb at 0x00FE0000 to 0x000E0000, replacing * mapping register that provides the ability to map the 128Kb at 0x00FE0000 to 0x000E0000, replacing
* any ROMs in the range 0x000E0000-0x000FFFFF, and optionally write-protecting that 128Kb. The second * any ROMs in the range 0x000E0000-0x000FFFFF, and optionally write-protecting that 128Kb; internally,
* register is a read-only diagnostics register that indicates jumper configuration and parity errors. * this register corresponds to bMapping.
*
* The second register is a read-only diagnostics register that indicates jumper configuration and
* parity errors; internally, this register corresponds to bSettings.
* *
* To emulate the memory-mapped registers at 0x80C00000, the RAM component allocates a block at that * To emulate the memory-mapped registers at 0x80C00000, the RAM component allocates a block at that
* address using this custom controller once it sees an allocation for "ramCPQ". * address using this custom controller once it sees an allocation for "ramCPQ".

View file

@ -241,11 +241,57 @@ var X86 = {
MASK: 0xfff8 // index of corresponding entry in GDT, LDT or IDT MASK: 0xfff8 // index of corresponding entry in GDT, LDT or IDT
}, },
RESULT: { RESULT: {
/*
* Flags were originally computed based on the following:
*
* CF: resultZeroCarry & resultSize
* PF: resultParitySign & 0xff
* AF: (resultParitySign ^ resultAuxOverflow) & 0x0010 (AUXOVF_AF)
* ZF: resultZeroCarry & (resultSize - 1)
* SF: resultParitySign & (resultSize >> 1)
* OF: (resultParitySign ^ resultAuxOverflow ^ (resultParitySign >> 1)) & (resultSize >> 1)
*
* I386 builds now rely on the following result variables:
*
* resultDst, resultSrc, resultArith, resultLogic, resultType, and resultFlags
*
* and the flags are computed as follows:
*
* CF: ((resultDst ^ ((resultDst ^ resultSrc) & (resultSrc ^ resultArith))) & resultType)
* PF: (resultLogic & 0xff)
* AF: ((resultArith ^ (resultDst ^ resultSrc)) & AUXOVF_AF)
* ZF: (resultLogic & ((resultType - 1) | resultType))
* SF: (resultLogic & resultType)
* OF: (((resultDst ^ resultArith) & (resultSrc ^ resultArith)) & resultType)
*
* Arithmetic operations should call:
*
* setArithResult(dst, src, value, type)
* eg:
* setArithResult(dst, src, dst+src, X86.RESULT.BYTE | X86.RESULT.ALL)
*
* The 4th parameter, type, indicates both the size of the result (BYTE, WORD or DWORD) and which of
* the flags should now be considered "cached" by the new result variables. If the previous resultType
* specifies any flags not contained in the new type parameter, then those flags must be immediately
* calculated and written to the appropriate bit(s) in resultFlags.
*/
BYTE: 0x80,
WORD: 0x8000,
DWORD: 0x80000000|0,
TYPE: 0x80008080|0,
CF: 0x01,
PF: 0x02,
AF: 0x04,
ZF: 0x08,
SF: 0x10,
OF: 0x20,
ALL: 0x3F,
LOGIC: 0x1A,
NOTCF: 0x3E,
SIZE_BYTE: 0x00100, SIZE_BYTE: 0x00100,
SIZE_WORD: 0x10000, SIZE_WORD: 0x10000,
AUXOVF_AF: 0x00010, AUXOVF_AF: 0x00010,
AUXOVF_OF: 0x08080, AUXOVF_OF: 0x08080
AUXOVF_CF: 0x10100
}, },
/* /*
* Bit values for opFlags, which are all reset to zero prior to each instruction * Bit values for opFlags, which are all reset to zero prior to each instruction

View file

@ -768,6 +768,12 @@ X86CPU.prototype.initProcessor = function()
this.aOps0F = X86.aOps0F.slice(); this.aOps0F = X86.aOps0F.slice();
this.aOps0F[0x20] = X86.opMOVrcr; this.aOps0F[0x20] = X86.opMOVrcr;
this.aOps0F[0x22] = X86.opMOVcrr; this.aOps0F[0x22] = X86.opMOVcrr;
/*
* Extend the opcode table by creating a mirror of the first 256 opcodes, but with dword-based
* opcode handlers (as defined in aOpsD) instead word-based opcode handlers. Whenever dataSize
* is changed from 2 bytes to 4, we trigger the appropriate set of opcode handlers by changing
* bOpcodeBias from 0 to 256.
*/
this.aOps = this.aOps.concat(this.aOps); this.aOps = this.aOps.concat(this.aOps);
for (var bOpcode in X86.aOpsD) { for (var bOpcode in X86.aOpsD) {
this.aOps[parseInt(bOpcode, 10) + 256] = X86.aOpsD[bOpcode]; this.aOps[parseInt(bOpcode, 10) + 256] = X86.aOpsD[bOpcode];
@ -890,6 +896,11 @@ X86CPU.prototype.resetRegs = function()
this.addrIDT = 0; this.addrIDTLimit = 0x03FF; this.addrIDT = 0; this.addrIDTLimit = 0x03FF;
this.nIOPL = 0; // this should be set before the first setPS() call this.nIOPL = 0; // this should be set before the first setPS() call
/*
* Define the result variables that setPS() relies on for arithmetic and logical flags
*/
this.resultDst = this.resultSrc = this.resultArith = this.resultLogic = 0;
/* /*
* This is set by opHelpFault() and reset (to -1) by resetRegs() and opIRET(); its initial purpose is to * This is set by opHelpFault() and reset (to -1) by resetRegs() and opIRET(); its initial purpose is to
* "help" opHelpFault() determine when a nested fault should be converted into either a double-fault (DF_FAULT) * "help" opHelpFault() determine when a nested fault should be converted into either a double-fault (DF_FAULT)
@ -1713,12 +1724,123 @@ X86CPU.prototype.setSP = function(off)
} }
}; };
/**
* setArithResult(dst, src, value, type, fSubtract)
*
* Updates the flags for arithmetic instructions; use setLogicResult() for logical instructions.
*
* The type parameter indicates both the size of the result (BYTE, WORD or DWORD) and which of the
* flags should now be considered "cached" by the new result variables. If the previous resultType
* specifies any flags not contained in the new type parameter, then those flags must be immediately
* calculated and written to the appropriate bit(s) in resultFlags.
*
* The fSubtract parameter is used to indicate a "subtracted" result (eg, CMP, DEC, SUB, SBB); the
* default assumes an "added" result (eg, ADD, ADC, INC).
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @param {number} value
* @param {number} type
* @param {boolean} [fSubtract]
*/
X86CPU.prototype.setArithResult = function(dst, src, value, type, fSubtract)
{
if ((type & X86.RESULT.ALL) != X86.RESULT.ALL && type != this.resultType) {
var diff = ((type ^ this.resultType) & this.resultType);
if (diff) {
if (diff & X86.RESULT.CF) this.getCF();
if (diff & X86.RESULT.PF) this.getPF();
if (diff & X86.RESULT.AF) this.getAF();
if (diff & X86.RESULT.ZF) this.getZF();
if (diff & X86.RESULT.SF) this.getSF();
if (diff & X86.RESULT.OF) this.getOF();
}
}
if (!fSubtract) {
this.resultDst = dst;
this.resultArith = value;
} else {
this.resultDst = value;
this.resultArith = dst;
}
this.resultSrc = src;
this.resultLogic = value;
this.resultType = type;
if (DEBUG) this.verifyFlags(type);
};
/**
* setLogicResult(value, type, carry, overflow)
*
* Updates the flags for logical instructions (eg, AND, OR, TEST, XOR); ie, instructions
* that update PF, ZF, and SF, while clearing CF and OF. AF is considered undefined. CF and OF
* are automatically cleared unless explicitly set.
*
* @this {X86CPU}
* @param {number} value
* @param {number} type
* @param {number} [carry]
* @param {number} [overflow]
* @return {number} value
*/
X86CPU.prototype.setLogicResult = function(value, type, carry, overflow)
{
this.resultType = type | X86.RESULT.LOGIC;
this.resultLogic = value;
if (carry) this.setCF(); else this.clearCF();
if (overflow) this.setOF(); else this.clearOF();
if (DEBUG) this.verifyFlags(X86.RESULT.LOGIC | X86.RESULT.CF | X86.RESULT.OF);
return value;
};
/**
* verifyFlags(flags)
*
* @this {X86CPU}
* @param {number} flags
*/
X86CPU.prototype.verifyFlags = function(flags)
{
if (DEBUG) {
if (flags & X86.RESULT.CF) {
this.assert(!this.getCF() == !(this.resultFlags & X86.PS.CF));
}
if (flags & X86.RESULT.PF) {
this.assert(!this.getPF() == !(this.resultFlags & X86.PS.PF));
}
if (flags & X86.RESULT.AF) {
this.assert(!this.getAF() == !(this.resultFlags & X86.PS.AF));
}
if (flags & X86.RESULT.ZF) {
this.assert(!this.getZF() == !(this.resultFlags & X86.PS.ZF));
}
if (flags & X86.RESULT.SF) {
this.assert(!this.getSF() == !(this.resultFlags & X86.PS.SF));
}
if (flags & X86.RESULT.OF) {
this.assert(!this.getOF() == !(this.resultFlags & X86.PS.OF));
}
}
};
/**
* getCarry()
*
* @this {X86CPU}
* @return {number} 0 or 1, depending on whether CF is clear or set
*/
X86CPU.prototype.getCarry = function()
{
return this.getCF()? 1 : 0;
};
/** /**
* getCF() * getCF()
* *
* Notes regarding carry following a 32-bit addition: * Notes regarding carry following an I386 addition:
* *
* The following table summarizes bit 31 of dst, src, and result, along with the expected carry bit: * The following table summarizes bit 31 of dst, src, and result, along with the expected carry:
* *
* dst src res carry * dst src res carry
* --- --- --- ----- * --- --- --- -----
@ -1731,29 +1853,27 @@ X86CPU.prototype.setSP = function(off)
* 1 1 0 1 yes (since the addition of two ones must always produce a carry) * 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) * 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) * So, we use the following calculation:
* into the conventional SIZE_WORD position (bit 16); eg:
* *
* resultZeroCarry = ((resultZeroCarry >>> 16) | (resultZeroCarry & 0xffff)) | (((dst ^ ((dst ^ src) & (src ^ resultZeroCarry))) >>> 15) & SIZE_WORD); * (resultDst ^ ((resultDst ^ resultSrc) & (resultSrc ^ resultArith))) & resultType
*
* 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} * @this {X86CPU}
* @return {number} * @return {number} 0 or X86.PS.CF
*/ */
X86CPU.prototype.getCF = function() X86CPU.prototype.getCF = function()
{ {
return (this.resultZeroCarry & this.resultSize)? X86.PS.CF : 0; var flag = (this.resultZeroCarry & this.resultSize)? X86.PS.CF : 0;
if (I386) {
if (this.resultType & X86.RESULT.CF) {
this.resultFlags &= ~X86.PS.CF;
if ((this.resultDst ^ ((this.resultDst ^ this.resultSrc) & (this.resultSrc ^ this.resultArith))) & (this.resultType & X86.RESULT.TYPE)) {
this.resultFlags |= X86.PS.CF;
}
this.resultType &= ~X86.RESULT.CF;
}
if (!OLDFLAGS) return this.resultFlags & X86.PS.CF;
}
return flag;
}; };
/** /**
@ -1779,63 +1899,162 @@ X86CPU.prototype.getCF = function()
* has EVEN parity; the above calculation yields ODD parity, so we use the conditional operator to invert the result. * has EVEN parity; the above calculation yields ODD parity, so we use the conditional operator to invert the result.
* *
* @this {X86CPU} * @this {X86CPU}
* @return {number} * @return {number} 0 or X86.PS.PF
*/ */
X86CPU.prototype.getPF = function() X86CPU.prototype.getPF = function()
{ {
var v = this.resultParitySign; var flag = this.resultParitySign;
return ((0x6996 >> ((v ^ (v >> 4)) & 0xf)) & 1)? 0 : X86.PS.PF; flag = ((0x6996 >> ((flag ^ (flag >> 4)) & 0xf)) & 1)? 0 : X86.PS.PF;
if (I386) {
if (this.resultType & X86.RESULT.PF) {
this.resultFlags &= ~X86.PS.PF;
if ((0x9669 >> ((this.resultLogic ^ (this.resultLogic >> 4)) & 0xf)) & 1) {
this.resultFlags |= X86.PS.PF;
}
this.resultType &= ~X86.RESULT.PF;
}
if (!OLDFLAGS) return this.resultFlags & X86.PS.PF;
}
return flag;
}; };
/** /**
* getAF() * getAF()
* *
* Notes regarding auxiliary carry following an I386 addition:
*
* To determine if there's been a carry out of the low 4 bits of an arithmetic operation,
* we look at all the possible inputs for bit 4, and calculate AF = PS^(D^S):
*
* D S A D^S AF
* - - - --- --
* 0 0 0 0 0
* 0 0 1 0 1
* 0 1 0 1 1
* 0 1 1 1 0
* 1 0 0 1 1
* 1 0 1 1 0
* 1 1 0 0 0
* 1 1 1 0 1
*
* The final calculation looks like:
*
* (resultArith ^ (resultDst ^ resultSrc)) & AUXOVF_AF
*
* @this {X86CPU} * @this {X86CPU}
* @return {number} * @return {number} 0 or X86.PS.AF
*/ */
X86CPU.prototype.getAF = function() X86CPU.prototype.getAF = function()
{ {
return ((this.resultParitySign ^ this.resultAuxOverflow) & X86.RESULT.AUXOVF_AF)? X86.PS.AF : 0; var flag = ((this.resultParitySign ^ this.resultAuxOverflow) & X86.RESULT.AUXOVF_AF)? X86.PS.AF : 0;
if (I386) {
if (this.resultType & X86.RESULT.AF) {
this.resultFlags &= ~X86.PS.AF;
if ((this.resultArith ^ (this.resultDst ^ this.resultSrc)) & X86.RESULT.AUXOVF_AF) {
this.resultFlags |= X86.PS.AF;
}
this.resultType &= ~X86.RESULT.AF;
}
if (!OLDFLAGS) return this.resultFlags & X86.PS.AF;
}
return flag;
}; };
/** /**
* getZF() * getZF()
* *
* @this {X86CPU} * @this {X86CPU}
* @return {number} * @return {number} 0 or X86.PS.ZF
*/ */
X86CPU.prototype.getZF = function() X86CPU.prototype.getZF = function()
{ {
return (this.resultZeroCarry & (this.resultSize - 1))? 0 : X86.PS.ZF; var flag = (this.resultZeroCarry & (this.resultSize - 1))? 0 : X86.PS.ZF;
if (I386) {
if (this.resultType & X86.RESULT.ZF) {
this.resultFlags &= ~X86.PS.ZF;
if (!(this.resultLogic & (((this.resultType & X86.RESULT.TYPE) - 1) | (this.resultType & X86.RESULT.TYPE)))) {
this.resultFlags |= X86.PS.ZF;
}
this.resultType &= ~X86.RESULT.ZF;
}
if (!OLDFLAGS) return this.resultFlags & X86.PS.ZF;
}
return flag;
}; };
/** /**
* getSF() * getSF()
* *
* @this {X86CPU} * @this {X86CPU}
* @return {number} * @return {number} 0 or X86.PS.SF
*/ */
X86CPU.prototype.getSF = function() X86CPU.prototype.getSF = function()
{ {
return (this.resultParitySign & (this.resultSize >> 1))? X86.PS.SF : 0; var flag = (this.resultParitySign & (this.resultSize >> 1))? X86.PS.SF : 0;
if (I386) {
if (this.resultType & X86.RESULT.SF) {
this.resultFlags &= ~X86.PS.SF;
if (this.resultLogic & (this.resultType & X86.RESULT.TYPE)) {
this.resultFlags |= X86.PS.SF;
}
this.resultType &= ~X86.RESULT.SF;
}
if (!OLDFLAGS) return this.resultFlags & X86.PS.SF;
}
return flag;
}; };
/** /**
* getOF() * getOF()
* *
* Overflow was originally calculated as:
*
* (resultParitySign ^ resultAuxOverflow ^ (resultParitySign >> 1)) & (resultSize >> 1)
*
* but as you can see, that calculation depends on the carry out of the 8/16/32-bit result in
* resultParitySign, which we don't have access to for 32-bit results. So we fall-back to the
* following:
*
* ((resultDst ^ resultArith) & (resultSrc ^ resultArith)) & resultType
*
* which you can verify from the following table of sign bits (where x1 is resultDst ^ resultArith,
* and x2 is resultSrc ^ resultArith):
*
* D S A x1 x2 OF
* - - - -- -- --
* 0 0 0 0 0 0
* 0 0 1 1 1 1 (adding two positive values yielded a negative value)
* 0 1 0 0 1 0
* 0 1 1 1 0 0
* 1 0 0 1 0 0
* 1 0 1 0 1 0
* 1 1 0 1 1 1 (adding two negative values yielded a positive value)
* 1 1 1 0 0 0
*
* @this {X86CPU} * @this {X86CPU}
* @return {number} * @return {number} 0 or X86.PS.OF
*/ */
X86CPU.prototype.getOF = function() X86CPU.prototype.getOF = function()
{ {
return ((this.resultParitySign ^ this.resultAuxOverflow ^ (this.resultParitySign >> 1)) & (this.resultSize >> 1))? X86.PS.OF : 0; var flag = ((this.resultParitySign ^ this.resultAuxOverflow ^ (this.resultParitySign >> 1)) & (this.resultSize >> 1))? X86.PS.OF : 0;
if (I386) {
if (this.resultType & X86.RESULT.OF) {
this.resultFlags &= ~X86.PS.OF;
if (((this.resultDst ^ this.resultArith) & (this.resultSrc ^ this.resultArith)) & (this.resultType & X86.RESULT.TYPE)) {
this.resultFlags |= X86.PS.OF;
}
this.resultType &= ~X86.RESULT.OF;
}
if (!OLDFLAGS) return this.resultFlags & X86.PS.OF;
}
return flag;
}; };
/** /**
* getTF() * getTF()
* *
* @this {X86CPU} * @this {X86CPU}
* @return {number} * @return {number} 0 or X86.PS.TF
*/ */
X86CPU.prototype.getTF = function() X86CPU.prototype.getTF = function()
{ {
@ -1846,7 +2065,7 @@ X86CPU.prototype.getTF = function()
* getIF() * getIF()
* *
* @this {X86CPU} * @this {X86CPU}
* @return {number} * @return {number} 0 or X86.PS.IF
*/ */
X86CPU.prototype.getIF = function() X86CPU.prototype.getIF = function()
{ {
@ -1857,7 +2076,7 @@ X86CPU.prototype.getIF = function()
* getDF() * getDF()
* *
* @this {X86CPU} * @this {X86CPU}
* @return {number} * @return {number} 0 or X86.PS.DF
*/ */
X86CPU.prototype.getDF = function() X86CPU.prototype.getDF = function()
{ {
@ -1872,6 +2091,10 @@ X86CPU.prototype.getDF = function()
X86CPU.prototype.clearCF = function() X86CPU.prototype.clearCF = function()
{ {
this.resultZeroCarry &= ~this.resultSize; this.resultZeroCarry &= ~this.resultSize;
if (I386) {
this.resultType &= ~X86.RESULT.CF;
this.resultFlags &= ~X86.PS.CF;
}
}; };
/** /**
@ -1882,6 +2105,10 @@ X86CPU.prototype.clearCF = function()
X86CPU.prototype.clearPF = function() X86CPU.prototype.clearPF = function()
{ {
if (this.getPF()) this.resultParitySign ^= 0x1; if (this.getPF()) this.resultParitySign ^= 0x1;
if (I386) {
this.resultType &= ~X86.RESULT.PF;
this.resultFlags &= ~X86.PS.PF;
}
}; };
/** /**
@ -1892,6 +2119,10 @@ X86CPU.prototype.clearPF = function()
X86CPU.prototype.clearAF = function() X86CPU.prototype.clearAF = function()
{ {
this.resultAuxOverflow = (this.resultParitySign & X86.RESULT.AUXOVF_AF) | (this.resultAuxOverflow & ~X86.RESULT.AUXOVF_AF); this.resultAuxOverflow = (this.resultParitySign & X86.RESULT.AUXOVF_AF) | (this.resultAuxOverflow & ~X86.RESULT.AUXOVF_AF);
if (I386) {
this.resultType &= ~X86.RESULT.AF;
this.resultFlags &= ~X86.PS.AF;
}
}; };
/** /**
@ -1902,6 +2133,10 @@ X86CPU.prototype.clearAF = function()
X86CPU.prototype.clearZF = function() X86CPU.prototype.clearZF = function()
{ {
this.resultZeroCarry |= (this.resultSize - 1); this.resultZeroCarry |= (this.resultSize - 1);
if (I386) {
this.resultType &= ~X86.RESULT.ZF;
this.resultFlags &= ~X86.PS.ZF;
}
}; };
/** /**
@ -1915,6 +2150,10 @@ X86CPU.prototype.clearSF = function()
this.resultParitySign ^= (this.resultSize >> 1) | (this.resultSize >> 2); this.resultParitySign ^= (this.resultSize >> 1) | (this.resultSize >> 2);
this.resultAuxOverflow ^= X86.RESULT.AUXOVF_OF; this.resultAuxOverflow ^= X86.RESULT.AUXOVF_OF;
} }
if (I386) {
this.resultType &= ~X86.RESULT.SF;
this.resultFlags &= ~X86.PS.SF;
}
}; };
/** /**
@ -1946,6 +2185,10 @@ X86CPU.prototype.clearOF = function()
{ {
this.resultParitySign &= ~this.resultSize; this.resultParitySign &= ~this.resultSize;
this.resultAuxOverflow = (this.resultParitySign & X86.RESULT.AUXOVF_OF) | (this.resultAuxOverflow & ~X86.RESULT.AUXOVF_OF); this.resultAuxOverflow = (this.resultParitySign & X86.RESULT.AUXOVF_OF) | (this.resultAuxOverflow & ~X86.RESULT.AUXOVF_OF);
if (I386) {
this.resultType &= ~X86.RESULT.OF;
this.resultFlags &= ~X86.PS.OF;
}
}; };
/** /**
@ -1956,6 +2199,10 @@ X86CPU.prototype.clearOF = function()
X86CPU.prototype.setCF = function() X86CPU.prototype.setCF = function()
{ {
this.resultZeroCarry |= this.resultSize; this.resultZeroCarry |= this.resultSize;
if (I386) {
this.resultType &= ~X86.RESULT.CF;
this.resultFlags |= X86.PS.CF;
}
}; };
/** /**
@ -1966,6 +2213,10 @@ X86CPU.prototype.setCF = function()
X86CPU.prototype.setPF = function() X86CPU.prototype.setPF = function()
{ {
if (!this.getPF()) this.resultParitySign ^= 0x1; if (!this.getPF()) this.resultParitySign ^= 0x1;
if (I386) {
this.resultType &= ~X86.RESULT.PF;
this.resultFlags |= X86.PS.PF;
}
}; };
/** /**
@ -1976,6 +2227,10 @@ X86CPU.prototype.setPF = function()
X86CPU.prototype.setAF = function() X86CPU.prototype.setAF = function()
{ {
this.resultAuxOverflow = ~(this.resultParitySign & X86.RESULT.AUXOVF_AF) & X86.RESULT.AUXOVF_AF | (this.resultAuxOverflow & ~X86.RESULT.AUXOVF_AF); this.resultAuxOverflow = ~(this.resultParitySign & X86.RESULT.AUXOVF_AF) & X86.RESULT.AUXOVF_AF | (this.resultAuxOverflow & ~X86.RESULT.AUXOVF_AF);
if (I386) {
this.resultType &= ~X86.RESULT.AF;
this.resultFlags |= X86.PS.AF;
}
}; };
/** /**
@ -1986,6 +2241,10 @@ X86CPU.prototype.setAF = function()
X86CPU.prototype.setZF = function() X86CPU.prototype.setZF = function()
{ {
this.resultZeroCarry &= ~(this.resultSize - 1); this.resultZeroCarry &= ~(this.resultSize - 1);
if (I386) {
this.resultType &= ~X86.RESULT.ZF;
this.resultFlags |= X86.PS.ZF;
}
}; };
/** /**
@ -1999,6 +2258,10 @@ X86CPU.prototype.setSF = function()
this.resultParitySign ^= (this.resultSize >> 1) | (this.resultSize >> 2); this.resultParitySign ^= (this.resultSize >> 1) | (this.resultSize >> 2);
this.resultAuxOverflow ^= X86.RESULT.AUXOVF_OF; this.resultAuxOverflow ^= X86.RESULT.AUXOVF_OF;
} }
if (I386) {
this.resultType &= ~X86.RESULT.SF;
this.resultFlags |= X86.PS.SF;
}
}; };
/** /**
@ -2028,6 +2291,10 @@ X86CPU.prototype.setDF = function()
*/ */
X86CPU.prototype.setOF = function() X86CPU.prototype.setOF = function()
{ {
if (I386) {
this.resultType &= ~X86.RESULT.OF;
this.resultFlags |= X86.PS.OF;
}
this.resultParitySign |= this.resultSize; this.resultParitySign |= this.resultSize;
this.resultAuxOverflow = (this.resultParitySign & X86.RESULT.AUXOVF_OF) | (this.resultAuxOverflow & ~X86.RESULT.AUXOVF_OF); this.resultAuxOverflow = (this.resultParitySign & X86.RESULT.AUXOVF_OF) | (this.resultAuxOverflow & ~X86.RESULT.AUXOVF_OF);
}; };
@ -2078,15 +2345,20 @@ X86CPU.prototype.setMSW = function(w)
*/ */
X86CPU.prototype.setPS = function(regPS, cpl) X86CPU.prototype.setPS = function(regPS, cpl)
{ {
this.resultSize = X86.RESULT.SIZE_BYTE; // NOTE: We could have chosen SIZE_WORD, too; it's irrelevant if (I386) {
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = 0; this.resultType = X86.RESULT.BYTE;
this.resultFlags = regPS & (X86.PS.CF | X86.PS.PF | X86.PS.AF | X86.PS.ZF | X86.PS.SF | X86.PS.OF);
if (regPS & X86.PS.CF) this.setCF(); }
if (!(regPS & X86.PS.PF)) this.resultParitySign |= 0x1; if (OLDFLAGS) {
if (regPS & X86.PS.AF) this.resultAuxOverflow |= X86.RESULT.AUXOVF_AF; this.resultSize = X86.RESULT.SIZE_BYTE;
if (!(regPS & X86.PS.ZF)) this.clearZF(); this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = 0;
if (regPS & X86.PS.SF) this.setSF(); if (regPS & X86.PS.CF) this.setCF();
if (regPS & X86.PS.OF) this.setOF(); if (!(regPS & X86.PS.PF)) this.resultParitySign |= 0x1;
if (regPS & X86.PS.AF) this.resultAuxOverflow |= X86.RESULT.AUXOVF_AF;
if (!(regPS & X86.PS.ZF)) this.clearZF();
if (regPS & X86.PS.SF) this.setSF();
if (regPS & X86.PS.OF) this.setOF();
}
/* /*
* OS/2 1.0 discriminates between an 80286 and an 80386 based on whether an IRET in real-mode that * OS/2 1.0 discriminates between an 80286 and an 80386 based on whether an IRET in real-mode that
@ -2166,37 +2438,46 @@ X86CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
{ {
var fBound = false; var fBound = false;
switch (sBinding) { switch (sBinding) {
case "AX": case "EAX":
case "BX": case "EBX":
case "CX": case "ECX":
case "DX": case "EDX":
case "SP": case "ESP":
case "BP": case "EBP":
case "SI": case "ESI":
case "DI": case "EDI":
case "CS": case "EIP":
case "DS": case "AX":
case "SS": case "BX":
case "ES": case "CX":
case "IP": case "DX":
case "PC": // deprecated as an alias for "IP" (still used by older XML files, like the one at http://tpoindex.github.io/crobots/) case "SP":
case "PS": // this refers to "Processor Status", aka the 16-bit flags register (although DEBUG.COM refers to this as "PC", surprisingly) case "BP":
case "C": case "SI":
case "P": case "DI":
case "A": case "IP":
case "Z": case "PC": // deprecated as an alias for "IP" (still used by older XML files, like the one at http://tpoindex.github.io/crobots/)
case "S": case "CS":
case "T": case "DS":
case "I": case "SS":
case "D": case "ES":
case "V": case "PS": // this refers to "Processor Status", aka the 16-bit flags register (although DEBUG.COM refers to this as "PC", surprisingly)
this.bindings[sBinding] = control; case "C":
this.cLiveRegs++; case "P":
fBound = true; case "A":
break; case "Z":
default: case "S":
fBound = this.parent.setBinding.call(this, sHTMLType, sBinding, control); case "T":
break; case "I":
case "D":
case "V":
this.bindings[sBinding] = control;
this.cLiveRegs++;
fBound = true;
break;
default:
fBound = this.parent.setBinding.call(this, sHTMLType, sBinding, control);
break;
} }
return fBound; return fBound;
}; };
@ -3055,14 +3336,44 @@ X86CPU.prototype.delayINTR = function()
this.opFlags |= X86.OPFLAG.NOINTR; this.opFlags |= X86.OPFLAG.NOINTR;
}; };
/**
* updateReg(sReg, nValue)
*
* This function helps updateStatus() by massaging the register names and values according to
* CPU type before passing the call to displayValue(); in the "old days", updateStatus() called
* displayValue() directly (although then it was called displayReg()).
*
* @this {X86CPU}
* @param {string} sReg
* @param {number} nValue
*/
X86CPU.prototype.updateReg = function(sReg, nValue)
{
var cch = 4;
if (sReg.length == 1) {
cch = 1;
nValue = nValue? 1 : 0;
}
if (this.model < 80386) {
if (sReg.length > 2) {
sReg = sReg.substr(0, 2);
}
} else {
if (sReg == "PS" || sReg.length > 2) {
cch = 8;
}
}
this.displayValue(sReg, nValue, cch);
};
/** /**
* updateStatus() * updateStatus()
* *
* This provides periodic Control Panel updates (eg, a few times per second; see STATUS_UPDATES_PER_SECOND). * This provides periodic Control Panel updates (eg, a few times per second; see STATUS_UPDATES_PER_SECOND).
* this is where we take care of any DOM updates (eg, register values) while the CPU is running. * this is where we take care of any DOM updates (eg, register values) while the CPU is running.
* *
* Any high-frequency updates should be performed in updateVideo(), which should avoid DOM updates, since * Any high-frequency updates should be performed in updateVideo(), which should avoid DOM updates, since updateVideo()
* updateVideo() can be called up to 60 times per second (see VIDEO_UPDATES_PER_SECOND). * can be called up to 60 times per second (see VIDEO_UPDATES_PER_SECOND).
* *
* @this {X86CPU} * @this {X86CPU}
* @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled) * @param {boolean} [fForce] (true will display registers even if the CPU is running and "live" registers are not enabled)
@ -3071,30 +3382,30 @@ X86CPU.prototype.updateStatus = function(fForce)
{ {
if (this.cLiveRegs) { if (this.cLiveRegs) {
if (fForce || !this.aFlags.fRunning || this.aFlags.fDisplayLiveRegs) { if (fForce || !this.aFlags.fRunning || this.aFlags.fDisplayLiveRegs) {
this.displayReg("AX", this.regEAX); this.updateReg("EAX", this.regEAX);
this.displayReg("BX", this.regEBX); this.updateReg("EBX", this.regEBX);
this.displayReg("CX", this.regECX); this.updateReg("ECX", this.regECX);
this.displayReg("DX", this.regEDX); this.updateReg("EDX", this.regEDX);
this.displayReg("SP", this.getSP()); this.updateReg("ESP", this.getSP());
this.displayReg("BP", this.regEBP); this.updateReg("EBP", this.regEBP);
this.displayReg("SI", this.regESI); this.updateReg("ESI", this.regESI);
this.displayReg("DI", this.regEDI); this.updateReg("EDI", this.regEDI);
this.displayReg("CS", this.getCS()); this.updateReg("CS", this.getCS());
this.displayReg("DS", this.getDS()); this.updateReg("DS", this.getDS());
this.displayReg("SS", this.getSS()); this.updateReg("SS", this.getSS());
this.displayReg("ES", this.getES()); this.updateReg("ES", this.getES());
this.displayReg("IP", this.getIP()); this.updateReg("EIP", this.getIP());
var regPS = this.getPS(); var regPS = this.getPS();
this.displayReg("PS", regPS); this.updateReg("PS", regPS);
this.displayReg("V", (regPS & X86.PS.OF)? 1 : 0, 1); this.updateReg("V", (regPS & X86.PS.OF));
this.displayReg("D", (regPS & X86.PS.DF)? 1 : 0, 1); this.updateReg("D", (regPS & X86.PS.DF));
this.displayReg("I", (regPS & X86.PS.IF)? 1 : 0, 1); this.updateReg("I", (regPS & X86.PS.IF));
this.displayReg("T", (regPS & X86.PS.TF)? 1 : 0, 1); this.updateReg("T", (regPS & X86.PS.TF));
this.displayReg("S", (regPS & X86.PS.SF)? 1 : 0, 1); this.updateReg("S", (regPS & X86.PS.SF));
this.displayReg("Z", (regPS & X86.PS.ZF)? 1 : 0, 1); this.updateReg("Z", (regPS & X86.PS.ZF));
this.displayReg("A", (regPS & X86.PS.AF)? 1 : 0, 1); this.updateReg("A", (regPS & X86.PS.AF));
this.displayReg("P", (regPS & X86.PS.PF)? 1 : 0, 1); this.updateReg("P", (regPS & X86.PS.PF));
this.displayReg("C", (regPS & X86.PS.CF)? 1 : 0, 1); this.updateReg("C", (regPS & X86.PS.CF));
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -544,7 +544,7 @@ X86.opANDAX = function ANDAX()
* *
* @this {X86CPU} * @this {X86CPU}
*/ */
X86.opANDEAX = function ANDEAX() X86.opANDAXd = function ANDAXd()
{ {
this.regEAX = X86.fnANDd.call(this, this.regEAX, this.getIPLong()); this.regEAX = X86.fnANDd.call(this, this.regEAX, this.getIPLong());
if (BACKTRACK) { if (BACKTRACK) {
@ -581,20 +581,27 @@ X86.opES = function ES()
X86.opDAA = function DAA() X86.opDAA = function DAA()
{ {
var AL = this.regEAX & 0xff; var AL = this.regEAX & 0xff;
var fAuxCarry = this.getAF(); var AF = this.getAF();
var fCarry = (this.resultZeroCarry & this.resultSize); var CF = this.getCF();
if ((AL & 0xf) > 9 || fAuxCarry) { if ((AL & 0xf) > 9 || AF) {
AL += 0x6; AL += 0x6;
fAuxCarry = true; AF = X86.PS.AF;
} }
if (AL > 0x9f || fCarry) { if (AL > 0x9f || CF) {
AL += 0x60; AL += 0x60;
fCarry = true; CF = X86.PS.CF;
} }
this.regEAX = (this.regEAX & ~0xff) | (this.resultZeroCarry = this.resultParitySign = (AL & 0xff)); var b = (AL & 0xff);
this.resultSize = X86.RESULT.SIZE_WORD; this.regEAX = (this.regEAX & ~0xff) | b;
if (fCarry) this.resultZeroCarry |= this.resultSize; if (OLDFLAGS) {
if (fAuxCarry) this.setAF(); else this.clearAF(); this.resultSize = X86.RESULT.SIZE_BYTE;
this.resultZeroCarry = this.resultParitySign = b;
}
if (I386) {
this.setLogicResult(b, X86.RESULT.BYTE);
}
if (CF) this.setCF(); else this.clearCF();
if (AF) this.setAF(); else this.clearAF();
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and DAA have the same cycle times this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and DAA have the same cycle times
}; };
@ -696,20 +703,27 @@ X86.opCS = function CS()
X86.opDAS = function DAS() X86.opDAS = function DAS()
{ {
var AL = this.regEAX & 0xff; var AL = this.regEAX & 0xff;
var fAuxCarry = this.getAF(); var AF = this.getAF();
var fCarry = (this.resultZeroCarry & this.resultSize); var CF = this.getCF();
if ((AL & 0xf) > 9 || fAuxCarry) { if ((AL & 0xf) > 9 || AF) {
AL -= 0x6; AL -= 0x6;
fAuxCarry = true; AF = X86.PS.AF;
} }
if (AL > 0x9f || fCarry) { if (AL > 0x9f || CF) {
AL -= 0x60; AL -= 0x60;
fCarry = true; CF = X86.PS.CF;
} }
this.regEAX = (this.regEAX & ~0xff) | (this.resultZeroCarry = this.resultParitySign = (AL & 0xff)); var b = (AL & 0xff);
this.resultSize = X86.RESULT.SIZE_WORD; this.regEAX = (this.regEAX & ~0xff) | b;
if (fCarry) this.resultZeroCarry |= this.resultSize; if (OLDFLAGS) {
if (fAuxCarry) this.setAF(); else this.clearAF(); this.resultSize = X86.RESULT.SIZE_BYTE;
this.resultZeroCarry = this.resultParitySign = b;
}
if (I386) {
this.setLogicResult(b, X86.RESULT.BYTE);
}
if (CF) this.setCF(); else this.clearCF();
if (AF) this.setAF(); else this.clearAF();
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and DAS have the same cycle times this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and DAS have the same cycle times
}; };
@ -753,6 +767,16 @@ X86.opXORrw = function XORrw()
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnXORw); this.aOpModRegWord[this.getIPByte()].call(this, X86.fnXORw);
}; };
/**
* op=0x33 (XOR reg,dword)
*
* @this {X86CPU}
*/
X86.opXORrd = function XORrd()
{
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnXORd);
};
/** /**
* op=0x34 (XOR AL,imm8) * op=0x34 (XOR AL,imm8)
* *
@ -763,8 +787,8 @@ X86.opXORALb = function XORALb()
this.regEAX = (this.regEAX & ~0xff) | X86.fnXORb.call(this, this.regEAX & 0xff, this.getIPByte()); this.regEAX = (this.regEAX & ~0xff) | X86.fnXORb.call(this, this.regEAX & 0xff, this.getIPByte());
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiMemLo; if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiMemLo;
/* /*
* In the absence of any EA calculations, opGrpXORb() will deduct nOpCyclesArithRR, and for all CPUs through * In the absence of any EA calculations, opGrpXORb() will deduct nOpCyclesArithRR, and for all CPUs
* the 80286, we need deduct only one more cycle. * through the 80286, we need deduct only one more cycle.
*/ */
this.nStepCycles--; this.nStepCycles--;
}; };
@ -781,8 +805,8 @@ X86.opXORAXw = function XORAXw()
this.backTrack.btiAL = this.backTrack.btiMemLo; this.backTrack.btiAH = this.backTrack.btiMemHi; this.backTrack.btiAL = this.backTrack.btiMemLo; this.backTrack.btiAH = this.backTrack.btiMemHi;
} }
/* /*
* In the absence of any EA calculations, opGrpXORw() will deduct nOpCyclesArithRR, and for all CPUs through * In the absence of any EA calculations, opGrpXORw() will deduct nOpCyclesArithRR, and for all CPUs
* the 80286, we need deduct only one more cycle. * through the 80286, we need deduct only one more cycle.
*/ */
this.nStepCycles--; this.nStepCycles--;
}; };
@ -810,21 +834,19 @@ X86.opSS = function SS()
*/ */
X86.opAAA = function AAA() X86.opAAA = function AAA()
{ {
var CF, AF;
var AL = this.regEAX & 0xff; var AL = this.regEAX & 0xff;
var AH = this.regEAX >> 8; var AH = (this.regEAX >> 8) & 0xff;
var fCarry; if ((AL & 0xf) > 9 || this.getAF()) {
var fAuxCarry = this.getAF();
if ((AL & 0xf) > 9 || fAuxCarry) {
AL = (AL + 0x6) & 0xf; AL = (AL + 0x6) & 0xf;
AH = (AH + 1) & 0xff; AH = (AH + 1) & 0xff;
fCarry = fAuxCarry = true; CF = AF = 1;
} else { } else {
fCarry = fAuxCarry = false; CF = AF = 0;
} }
this.regEAX = (this.regEAX & ~0xffff) | (AH << 8) | (this.resultZeroCarry = AL); this.regEAX = (this.regEAX & ~0xffff) | ((AH << 8) | AL);
this.resultSize = X86.RESULT.SIZE_WORD; if (CF) this.setCF(); else this.clearCF();
if (fCarry) this.resultZeroCarry |= this.resultSize; if (AF) this.setAF(); else this.clearAF();
if (fAuxCarry) this.setAF(); else this.clearAF();
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; this.nStepCycles -= this.CYCLES.nOpCyclesAAA;
}; };
@ -917,32 +939,23 @@ X86.opDS = function DS()
/** /**
* op=0x3D (AAS) * op=0x3D (AAS)
* *
* From "The 8086 Book":
*
* 1. If the low-order four bits of the AL register are between 0 and 9 and the AF flag is 0, then go to Step 3.
* 2. If the low-order four bits of the AL register are between A and F or the AF flag is 1, then subtract 6 from the AL register, subtract 1 from the AH register, and set the AF flag to 1.
* 3. Clear the high-order four bits of the AL register.
* 4. Set the CF flag to the value of the AF flag.
*
* @this {X86CPU} * @this {X86CPU}
*/ */
X86.opAAS = function AAS() X86.opAAS = function AAS()
{ {
var CF, AF;
var AL = this.regEAX & 0xff; var AL = this.regEAX & 0xff;
var AH = this.regEAX >> 8; var AH = (this.regEAX >> 8) & 0xff;
var fCarry; if ((AL & 0xf) > 9 || this.getAF()) {
var fAuxCarry = this.getAF();
if ((AL & 0xf) > 9 || fAuxCarry) {
AL = (AL - 0x6) & 0xf; AL = (AL - 0x6) & 0xf;
AH = (AH - 1) & 0xff; AH = (AH - 1) & 0xff;
fCarry = fAuxCarry = true; CF = AF = 1;
} else { } else {
fCarry = fAuxCarry = false; CF = AF = 0;
} }
this.regEAX = (this.regEAX & ~0xffff) | (AH << 8) | (this.resultZeroCarry = AL); this.regEAX = (this.regEAX & ~0xffff) | ((AH << 8) | AL);
this.resultSize = X86.RESULT.SIZE_WORD; if (CF) this.setCF(); else this.clearCF();
if (fCarry) this.resultZeroCarry |= this.resultSize; if (AF) this.setAF(); else this.clearAF();
if (fAuxCarry) this.setAF(); else this.clearAF();
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and AAS have the same cycle times this.nStepCycles -= this.CYCLES.nOpCyclesAAA; // AAA and AAS have the same cycle times
}; };
@ -953,16 +966,7 @@ X86.opAAS = function AAS()
*/ */
X86.opINCAX = function INCAX() X86.opINCAX = function INCAX()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEAX) + 1; this.regEAX = X86.fnINCr.call(this, this.regEAX);
if (I386) {
this.regEAX = (this.regEAX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEAX) & this.dataMask) >>> 16) | (this.regEAX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEAX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEAX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
}; };
/** /**
@ -972,16 +976,7 @@ X86.opINCAX = function INCAX()
*/ */
X86.opINCCX = function INCCX() X86.opINCCX = function INCCX()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regECX) + 1; this.regECX = X86.fnINCr.call(this, this.regECX);
if (I386) {
this.regECX = (this.regECX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regECX) & this.dataMask) >>> 16) | (this.regECX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regECX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regECX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
}; };
/** /**
@ -991,16 +986,7 @@ X86.opINCCX = function INCCX()
*/ */
X86.opINCDX = function INCDX() X86.opINCDX = function INCDX()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEDX) + 1; this.regEDX = X86.fnINCr.call(this, this.regEDX);
if (I386) {
this.regEDX = (this.regEDX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEDX) & this.dataMask) >>> 16) | (this.regEDX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEDX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEDX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
}; };
/** /**
@ -1010,16 +996,7 @@ X86.opINCDX = function INCDX()
*/ */
X86.opINCBX = function INCBX() X86.opINCBX = function INCBX()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEBX) + 1; this.regEBX = X86.fnINCr.call(this, this.regEBX);
if (I386) {
this.regEBX = (this.regEBX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEBX) & this.dataMask) >>> 16) | (this.regEBX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEBX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEBX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
}; };
/** /**
@ -1029,12 +1006,7 @@ X86.opINCBX = function INCBX()
*/ */
X86.opINCSP = function INCSP() X86.opINCSP = function INCSP()
{ {
var regESP; this.setSP(X86.fnINCr.call(this, this.getSP()));
this.resultParitySign = (this.resultAuxOverflow = this.getSP()) + 1;
this.setSP(regESP = (this.resultAuxOverflow & ~this.dataMask) | (this.resultParitySign & this.dataMask));
this.resultZeroCarry = (((regESP) & this.dataMask) >>> 16) | (regESP & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
}; };
/** /**
@ -1044,16 +1016,7 @@ X86.opINCSP = function INCSP()
*/ */
X86.opINCBP = function INCBP() X86.opINCBP = function INCBP()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEBP) + 1; this.regEBP = X86.fnINCr.call(this, this.regEBP);
if (I386) {
this.regEBP = (this.regEBP & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEBP) & this.dataMask) >>> 16) | (this.regEBP & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEBP = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEBP | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
}; };
/** /**
@ -1063,16 +1026,7 @@ X86.opINCBP = function INCBP()
*/ */
X86.opINCSI = function INCSI() X86.opINCSI = function INCSI()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regESI) + 1; this.regESI = X86.fnINCr.call(this, this.regESI);
if (I386) {
this.regESI = (this.regESI & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regESI) & this.dataMask) >>> 16) | (this.regESI & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regESI = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regESI | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
}; };
/** /**
@ -1082,16 +1036,7 @@ X86.opINCSI = function INCSI()
*/ */
X86.opINCDI = function INCDI() X86.opINCDI = function INCDI()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEDI) + 1; this.regEDI = X86.fnINCr.call(this, this.regEDI);
if (I386) {
this.regEDI = (this.regEDI & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEDI) & this.dataMask) >>> 16) | (this.regEDI & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEDI = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEDI | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of INC takes 2 cycles on all CPUs
}; };
/** /**
@ -1101,16 +1046,7 @@ X86.opINCDI = function INCDI()
*/ */
X86.opDECAX = function DECAX() X86.opDECAX = function DECAX()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEAX) - 1; this.regEAX = X86.fnDECr.call(this, this.regEAX);
if (I386) {
this.regEAX = (this.regEAX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEAX) & this.dataMask) >>> 16) | (this.regEAX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEAX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEAX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
}; };
/** /**
@ -1120,11 +1056,7 @@ X86.opDECAX = function DECAX()
*/ */
X86.opDECCX = function DECCX() X86.opDECCX = function DECCX()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regECX) - 1; this.regECX = X86.fnDECr.call(this, this.regECX);
this.regECX = (I386? (this.regECX & ~this.dataMask) | (this.resultParitySign & this.dataMask) : this.resultParitySign & 0xffff);
this.resultZeroCarry = this.regECX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
}; };
/** /**
@ -1134,11 +1066,7 @@ X86.opDECCX = function DECCX()
*/ */
X86.opDECDX = function DECDX() X86.opDECDX = function DECDX()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEDX) - 1; this.regEDX = X86.fnDECr.call(this, this.regEDX);
this.regEDX = (I386? (this.regEDX & ~this.dataMask) | (this.resultParitySign & this.dataMask) : this.resultParitySign & 0xffff);
this.resultZeroCarry = this.regEDX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
}; };
/** /**
@ -1148,16 +1076,7 @@ X86.opDECDX = function DECDX()
*/ */
X86.opDECBX = function DECBX() X86.opDECBX = function DECBX()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEBX) - 1; this.regEBX = X86.fnDECr.call(this, this.regEBX);
if (I386) {
this.regEBX = (this.regEBX & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEBX) & this.dataMask) >>> 16) | (this.regEBX & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEBX = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEBX | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
}; };
/** /**
@ -1167,12 +1086,7 @@ X86.opDECBX = function DECBX()
*/ */
X86.opDECSP = function DECSP() X86.opDECSP = function DECSP()
{ {
var regESP; this.setSP(X86.fnDECr.call(this, this.getSP()));
this.resultParitySign = (this.resultAuxOverflow = this.getSP()) - 1;
this.setSP(regESP = (this.resultAuxOverflow & ~this.dataMask) | (this.resultParitySign & this.dataMask));
this.resultZeroCarry = (((regESP) & this.dataMask) >>> 16) | (regESP & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
}; };
/** /**
@ -1182,16 +1096,7 @@ X86.opDECSP = function DECSP()
*/ */
X86.opDECBP = function DECBP() X86.opDECBP = function DECBP()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEBP) - 1; this.regEBP = X86.fnDECr.call(this, this.regEBP);
if (I386) {
this.regEBP = (this.regEBP & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEBP) & this.dataMask) >>> 16) | (this.regEBP & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEBP = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEBP | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
}; };
/** /**
@ -1201,16 +1106,7 @@ X86.opDECBP = function DECBP()
*/ */
X86.opDECSI = function DECSI() X86.opDECSI = function DECSI()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regESI) - 1; this.regESI = X86.fnDECr.call(this, this.regESI);
if (I386) {
this.regESI = (this.regESI & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regESI) & this.dataMask) >>> 16) | (this.regESI & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regESI = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regESI | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
}; };
/**` /**`
@ -1220,16 +1116,7 @@ X86.opDECSI = function DECSI()
*/ */
X86.opDECDI = function DECDI() X86.opDECDI = function DECDI()
{ {
this.resultParitySign = (this.resultAuxOverflow = this.regEDI) - 1; this.regEDI = X86.fnDECr.call(this, this.regEDI);
if (I386) {
this.regEDI = (this.regEDI & ~this.dataMask) | (this.resultParitySign & this.dataMask);
this.resultZeroCarry = (((this.regEDI) & this.dataMask) >>> 16) | (this.regEDI & 0xffff) | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
} else {
this.regEDI = this.resultParitySign & 0xffff;
this.resultZeroCarry = this.regEDI | (((this.resultZeroCarry & this.resultSize)? 1 : 0) << 16);
}
this.resultSize = X86.RESULT.SIZE_WORD;
this.nStepCycles -= 2; // this form of DEC takes 2 cycles on all CPUs
}; };
/** /**
@ -2921,8 +2808,14 @@ X86.opCMPSw = function CMPSw()
*/ */
X86.opTESTALb = function TESTALb() X86.opTESTALb = function TESTALb()
{ {
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regEAX & this.getIPByte(); var src = this.getIPByte();
this.resultSize = X86.RESULT.SIZE_BYTE; if (OLDFLAGS) {
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regEAX & src;
this.resultSize = X86.RESULT.SIZE_BYTE;
}
if (I386) {
this.setLogicResult(this.regEAX & src, X86.RESULT.BYTE);
}
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; this.nStepCycles -= this.CYCLES.nOpCyclesAAA;
}; };
@ -2933,8 +2826,14 @@ X86.opTESTALb = function TESTALb()
*/ */
X86.opTESTAXw = function TESTAXw() X86.opTESTAXw = function TESTAXw()
{ {
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regEAX & this.getIPWord(); var src = this.getIPWord();
this.resultSize = X86.RESULT.SIZE_WORD; if (OLDFLAGS) {
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regEAX & src;
this.resultSize = X86.RESULT.SIZE_WORD;
}
if (I386) {
this.setLogicResult(this.regEAX & src, X86.RESULT.WORD);
}
this.nStepCycles -= this.CYCLES.nOpCyclesAAA; this.nStepCycles -= this.CYCLES.nOpCyclesAAA;
}; };
@ -3654,23 +3553,32 @@ X86.opGrp2wCL = function GRP2wCL()
* *
* From "The 8086 Book": * From "The 8086 Book":
* *
* 1. Divide the AL register by OA16. Store the quotient in the AH register. Store the remainder in the AL register. * 1. Divide AL by 0x0A; store the quotient in AH and the remainder in AL
* 2. Set the flags in the following manner: * 2. Set PF, SF, and ZF based on the AL register (CF, OF, and AF are undefined)
* Parity: based on the AL register
* Sign : based on the high-order bit of the AL register Zero: based on the AL register
* Carry, Overflow, and Arithmetic: undefined
* *
* @this {X86CPU} * @this {X86CPU}
*/ */
X86.opAAM = function AAM() X86.opAAM = function AAM()
{ {
var bDivisor = this.getIPByte(); var bDivisor = this.getIPByte();
if (!bDivisor) {
/*
* TODO: Generate a divide-by-zero exception, if appropriate for the current CPU
*/
return;
}
var AL = this.regEAX & 0xff; var AL = this.regEAX & 0xff;
var bQuotient = (AL / bDivisor) & 0xff; this.regEAX = (this.regEAX & ~0xffff) | ((AL / bDivisor) << 8) | (AL % bDivisor);
var bRemainder = AL % bDivisor; if (OLDFLAGS) {
this.regEAX = (bQuotient << 8) | bRemainder; this.resultSize = X86.RESULT.SIZE_BYTE;
this.resultSize = X86.RESULT.SIZE_BYTE; this.resultZeroCarry = this.resultParitySign = this.regEAX;
this.resultZeroCarry = this.resultParitySign = AL; }
if (I386) {
/*
* setLogicResult() is slightly overkill, because technically, we don't need to clear CF and OF....
*/
this.setLogicResult(this.regEAX, X86.RESULT.BYTE);
}
this.nStepCycles -= this.CYCLES.nOpCyclesAAM; this.nStepCycles -= this.CYCLES.nOpCyclesAAM;
}; };
@ -3679,22 +3587,25 @@ X86.opAAM = function AAM()
* *
* From "The 8086 Book": * From "The 8086 Book":
* *
* 1. Multiply the contents of the AH register by 0x0A * 1. Multiply AH by 0x0A, add AH to AL, and store 0x00 in AH
* 2. Add AH to AL. * 2. Set PF, SF, and ZF based on the AL register (CF, OF, and AF are undefined)
* 3. Store 0x00 into the AH register.
* 4. Set the flags in the following manner:
* Parity: based on the AL register
* Zero: based on the AL register
* Sign: based on the high-order bit of the AL register
* Carry, Overflow, Arithmetic: undefined
* *
* @this {X86CPU} * @this {X86CPU}
*/ */
X86.opAAD = function AAD() X86.opAAD = function AAD()
{ {
var bMultiplier = this.getIPByte(); var bMultiplier = this.getIPByte();
this.resultZeroCarry = this.resultParitySign = this.regEAX = (((this.regEAX >> 8) * bMultiplier) + this.regEAX) & 0xff; this.regEAX = (this.regEAX & ~0xffff) | (((((this.regEAX >> 8) & 0xff) * bMultiplier) + this.regEAX) & 0xff);
this.resultSize = X86.RESULT.SIZE_BYTE; if (OLDFLAGS) {
this.resultZeroCarry = this.resultParitySign = this.regEAX;
this.resultSize = X86.RESULT.SIZE_BYTE;
}
if (I386) {
/*
* setLogicResult() is slightly overkill, because technically, we don't need to clear CF and OF....
*/
this.setLogicResult(this.regEAX, X86.RESULT.BYTE);
}
this.nStepCycles -= this.CYCLES.nOpCyclesAAD; this.nStepCycles -= this.CYCLES.nOpCyclesAAD;
}; };
@ -3747,7 +3658,7 @@ X86.opESC = function ESC()
X86.opLOOPNZ = function LOOPNZ() X86.opLOOPNZ = function LOOPNZ()
{ {
var disp = this.getIPDisp(); var disp = this.getIPDisp();
if ((this.regECX = (this.regECX - 1) & this.addrMask) && (this.resultZeroCarry & (this.resultSize - 1))) { if ((this.regECX = (this.regECX - 1) & this.addrMask) && !this.getZF()) {
this.setIP(this.getIP() + disp); this.setIP(this.getIP() + disp);
this.nStepCycles -= this.CYCLES.nOpCyclesLoopNZ; this.nStepCycles -= this.CYCLES.nOpCyclesLoopNZ;
return; return;
@ -3763,7 +3674,7 @@ X86.opLOOPNZ = function LOOPNZ()
X86.opLOOPZ = function LOOPZ() X86.opLOOPZ = function LOOPZ()
{ {
var disp = this.getIPDisp(); var disp = this.getIPDisp();
if ((this.regECX = (this.regECX - 1) & this.addrMask) && !(this.resultZeroCarry & (this.resultSize - 1))) { if ((this.regECX = (this.regECX - 1) & this.addrMask) && this.getZF()) {
this.setIP(this.getIP() + disp); this.setIP(this.getIP() + disp);
this.nStepCycles -= this.CYCLES.nOpCyclesLoopZ; this.nStepCycles -= this.CYCLES.nOpCyclesLoopZ;
return; return;
@ -4121,7 +4032,7 @@ X86.opGrp3w = function GRP3w()
*/ */
X86.opCLC = function CLC() X86.opCLC = function CLC()
{ {
this.resultZeroCarry &= ~this.resultSize; this.clearCF();
this.nStepCycles -= 2; // CLC takes 2 cycles on all CPUs this.nStepCycles -= 2; // CLC takes 2 cycles on all CPUs
}; };
@ -4132,7 +4043,7 @@ X86.opCLC = function CLC()
*/ */
X86.opSTC = function STC() X86.opSTC = function STC()
{ {
this.resultZeroCarry |= this.resultSize; this.setCF();
this.nStepCycles -= 2; // STC takes 2 cycles on all CPUs this.nStepCycles -= 2; // STC takes 2 cycles on all CPUs
}; };
@ -4224,6 +4135,17 @@ X86.opUndefined = function()
this.stopCPU(); this.stopCPU();
}; };
/**
* opTBDd()
*
* @this {X86CPU}
*/
X86.opTBDd = function()
{
this.printMessage("unimplemented 80386 opcode", true);
this.stopCPU();
};
/* /*
* This 256-entry array of opcode functions is at the heart of the CPU engine: stepCPU(n). * This 256-entry array of opcode functions is at the heart of the CPU engine: stepCPU(n).
* *
@ -4405,9 +4327,33 @@ X86.aOpGrp4w = [
]; ];
if (I386) { if (I386) {
/*
* Until we have *d() forms of all *w() opcode handlers, we need to put in placeholders (ie, opTBDd())
*/
X86.aOpsD = { X86.aOpsD = {
0x21: X86.opANDmd, 0x01: X86.opTBDd, // opADDmd()
0x23: X86.opANDrd, 0x03: X86.opTBDd, // opADDrd()
0x25: X86.opANDEAX 0x05: X86.opTBDd, // opADDAXd()
0x09: X86.opTBDd, // opORmd()
0x0B: X86.opTBDd, // opORrd()
0x0D: X86.opTBDd, // opORAXd()
0x11: X86.opTBDd, // opADCmd()
0x13: X86.opTBDd, // opADCrd()
0x15: X86.opTBDd, // opADCAXd()
0x19: X86.opTBDd, // opSBBmd()
0x1B: X86.opTBDd, // opSBBrd()
0x1D: X86.opTBDd, // opSBBAXd()
0x21: X86.opANDmd,
0x23: X86.opANDrd,
0x25: X86.opANDAXd,
0x29: X86.opTBDd, // opSUBmd()
0x2B: X86.opTBDd, // opSUBrd()
0x2D: X86.opTBDd, // opSUBAXd()
0x31: X86.opTBDd, // opXORmd()
0x33: X86.opXORrd,
0x35: X86.opTBDd, // opXORAXd()
0x39: X86.opTBDd, // opCMPmd()
0x3B: X86.opTBDd, // opCMPrd()
0x3D: X86.opTBDd // opCMPAXd()
}; };
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff