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
"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
revision identifier, also 3, in DL [Surprisingly, these are the same values that the B1 stepping reports
as well. -JP]
revision identifier, also 3, in DL [These B0 values need to be independently confirmed, because they are
the same values reported for the B1 stepping. -JP]
> + *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
@ -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
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
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)
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.
[See Intel Errata #17 below]
> The following sample program has been calculated to produce the [multiplication] error. An 80386 that
fails one or more of these multiply instructions is obviously faulty. However, passing does not guarantee
a perfect part. To their credit, Intel agreed to test all 80386s for a limited time and report on their
> The following sample program has been calculated to produce the [aforementioned multiplication] error.
An 80386 that fails one or more of these multiply instructions is obviously faulty. However, passing does not
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
"For Sixteen-Bit Software Only." [I believe the exact marking was "16 BIT S/W ONLY" -JP] Those that passed
have been marked with a double sigma sign. All 80386s produced after the B1 stepping should be free of this
defect.
"For Sixteen-Bit Software Only." [To be exact: "16 BIT S/W ONLY" -JP] Those that passed have been marked with
a double sigma sign. All 80386s produced after the B1 stepping should be free of this defect.
; 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 |
|
So, the B1 stepping set DL to 0x03 on reset. It also seems a safe bet 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.
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, 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:
@ -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
IBTS r/m32,EAX,CL,r32
COP (Code of Operation) : 0FH A7H Postbyte
COP (Code of Operation) : 0FH A7H
Clocks: IBTS
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
XBTS r32,r/m32,EAX,CL
COP (Code of Operation) : 0FH A6H Postbyte
COP (Code of Operation) : 0FH A6H
Clocks: XBTS
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}
* @param {string} sReg
* @param {number} nVal
* @param {number} [cch] default is 4
* @param {string} sLabel
* @param {number} nValue
* @param {number} cch
*/
CPU.prototype.displayReg = function(sReg, nVal, cch)
CPU.prototype.displayValue = function(sLabel, nValue, cch)
{
if (this.bindings[sReg]) {
if (cch === undefined) cch = 4;
if (nVal === undefined) {
this.setError("Register " + sReg + " is invalid");
if (this.bindings[sLabel]) {
if (nValue === undefined) {
this.setError("Value for " + sLabel + " is invalid");
this.stopCPU();
}
var sVal;
if (!this.aFlags.fRunning || this.aFlags.fDisplayLiveRegs) {
sVal = str.toHex(nVal, cch);
sVal = str.toHex(nValue, cch);
} 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
@ -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
* 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;
/**
* @define {boolean}
*
* Retain support for old flags.
*/
var OLDFLAGS = true;
/**
* @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)
* 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
* second chunk of RAM must have an ID of "ramCPQ".
* remainder (768Kb, 512Kb, or 384Kb) accessible only at addresses just below 0x01000000. In PCjs,
* 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.
* 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
* 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
* register is a read-only diagnostics register that indicates jumper configuration and parity errors.
* any ROMs in the range 0x000E0000-0x000FFFFF, and optionally write-protecting that 128Kb; internally,
* 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
* 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
},
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_WORD: 0x10000,
AUXOVF_AF: 0x00010,
AUXOVF_OF: 0x08080,
AUXOVF_CF: 0x10100
AUXOVF_OF: 0x08080
},
/*
* 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[0x20] = X86.opMOVrcr;
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);
for (var bOpcode in X86.aOpsD) {
this.aOps[parseInt(bOpcode, 10) + 256] = X86.aOpsD[bOpcode];
@ -890,6 +896,11 @@ X86CPU.prototype.resetRegs = function()
this.addrIDT = 0; this.addrIDTLimit = 0x03FF;
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
* "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()
*
* 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
* --- --- --- -----
@ -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 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:
* So, we use the following calculation:
*
* resultZeroCarry = ((resultZeroCarry >>> 16) | (resultZeroCarry & 0xffff)) | (((dst ^ ((dst ^ src) & (src ^ resultZeroCarry))) >>> 15) & SIZE_WORD);
*
* Essentially, wed be cramming all 32 result bits into the low 16 bits (which would effectively represent the
* zero flag), and then setting bit 16 to the effective carry flag. This transforms the zero and carry conditions
* for a DWORD computation into the corresponding conditions for a WORD computation. This would slow down 32-bit
* addition, but it would allow 8-bit and 16-bit addition to remain fast. Languages that support 64-bit values in
* conjunction with bit-wise operators can omit that one-line transformation, allowing us to set SIZE_WORD to a
* 33-bit value, but sadly, we cannot do that in JavaScript.
*
* Alternatively, we could store the src and dst operands into their own result variables (eg, resultSrc and resultDst)
* and compute carry lazily, but that would affect MUCH more existing code (eg, all code that currently inspects carry
* with a single bit test). I think the DWORD-to-WORD flag conversion for 32-bit instructions that modify zero
* and/or carry) is a more reasonable first step.
* (resultDst ^ ((resultDst ^ resultSrc) & (resultSrc ^ resultArith))) & resultType
*
* @this {X86CPU}
* @return {number}
* @return {number} 0 or X86.PS.CF
*/
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.
*
* @this {X86CPU}
* @return {number}
* @return {number} 0 or X86.PS.PF
*/
X86CPU.prototype.getPF = function()
{
var v = this.resultParitySign;
return ((0x6996 >> ((v ^ (v >> 4)) & 0xf)) & 1)? 0 : X86.PS.PF;
var flag = this.resultParitySign;
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()
*
* 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}
* @return {number}
* @return {number} 0 or X86.PS.AF
*/
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()
*
* @this {X86CPU}
* @return {number}
* @return {number} 0 or X86.PS.ZF
*/
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()
*
* @this {X86CPU}
* @return {number}
* @return {number} 0 or X86.PS.SF
*/
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()
*
* 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}
* @return {number}
* @return {number} 0 or X86.PS.OF
*/
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()
*
* @this {X86CPU}
* @return {number}
* @return {number} 0 or X86.PS.TF
*/
X86CPU.prototype.getTF = function()
{
@ -1846,7 +2065,7 @@ X86CPU.prototype.getTF = function()
* getIF()
*
* @this {X86CPU}
* @return {number}
* @return {number} 0 or X86.PS.IF
*/
X86CPU.prototype.getIF = function()
{
@ -1857,7 +2076,7 @@ X86CPU.prototype.getIF = function()
* getDF()
*
* @this {X86CPU}
* @return {number}
* @return {number} 0 or X86.PS.DF
*/
X86CPU.prototype.getDF = function()
{
@ -1872,6 +2091,10 @@ X86CPU.prototype.getDF = function()
X86CPU.prototype.clearCF = function()
{
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()
{
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()
{
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()
{
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.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.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()
{
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()
{
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()
{
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()
{
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.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()
{
if (I386) {
this.resultType &= ~X86.RESULT.OF;
this.resultFlags |= X86.PS.OF;
}
this.resultParitySign |= this.resultSize;
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)
{
this.resultSize = X86.RESULT.SIZE_BYTE; // NOTE: We could have chosen SIZE_WORD, too; it's irrelevant
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = 0;
if (regPS & X86.PS.CF) this.setCF();
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();
if (I386) {
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 (OLDFLAGS) {
this.resultSize = X86.RESULT.SIZE_BYTE;
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = 0;
if (regPS & X86.PS.CF) this.setCF();
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
@ -2166,37 +2438,46 @@ X86CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
{
var fBound = false;
switch (sBinding) {
case "AX":
case "BX":
case "CX":
case "DX":
case "SP":
case "BP":
case "SI":
case "DI":
case "CS":
case "DS":
case "SS":
case "ES":
case "IP":
case "PC": // deprecated as an alias for "IP" (still used by older XML files, like the one at http://tpoindex.github.io/crobots/)
case "PS": // this refers to "Processor Status", aka the 16-bit flags register (although DEBUG.COM refers to this as "PC", surprisingly)
case "C":
case "P":
case "A":
case "Z":
case "S":
case "T":
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;
case "EAX":
case "EBX":
case "ECX":
case "EDX":
case "ESP":
case "EBP":
case "ESI":
case "EDI":
case "EIP":
case "AX":
case "BX":
case "CX":
case "DX":
case "SP":
case "BP":
case "SI":
case "DI":
case "IP":
case "PC": // deprecated as an alias for "IP" (still used by older XML files, like the one at http://tpoindex.github.io/crobots/)
case "CS":
case "DS":
case "SS":
case "ES":
case "PS": // this refers to "Processor Status", aka the 16-bit flags register (although DEBUG.COM refers to this as "PC", surprisingly)
case "C":
case "P":
case "A":
case "Z":
case "S":
case "T":
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;
};
@ -3055,14 +3336,44 @@ X86CPU.prototype.delayINTR = function()
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()
*
* 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.
*
* Any high-frequency updates should be performed in updateVideo(), which should avoid DOM updates, since
* updateVideo() can be called up to 60 times per second (see VIDEO_UPDATES_PER_SECOND).
* Any high-frequency updates should be performed in updateVideo(), which should avoid DOM updates, since updateVideo()
* can be called up to 60 times per second (see VIDEO_UPDATES_PER_SECOND).
*
* @this {X86CPU}
* @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 (fForce || !this.aFlags.fRunning || this.aFlags.fDisplayLiveRegs) {
this.displayReg("AX", this.regEAX);
this.displayReg("BX", this.regEBX);
this.displayReg("CX", this.regECX);
this.displayReg("DX", this.regEDX);
this.displayReg("SP", this.getSP());
this.displayReg("BP", this.regEBP);
this.displayReg("SI", this.regESI);
this.displayReg("DI", this.regEDI);
this.displayReg("CS", this.getCS());
this.displayReg("DS", this.getDS());
this.displayReg("SS", this.getSS());
this.displayReg("ES", this.getES());
this.displayReg("IP", this.getIP());
this.updateReg("EAX", this.regEAX);
this.updateReg("EBX", this.regEBX);
this.updateReg("ECX", this.regECX);
this.updateReg("EDX", this.regEDX);
this.updateReg("ESP", this.getSP());
this.updateReg("EBP", this.regEBP);
this.updateReg("ESI", this.regESI);
this.updateReg("EDI", this.regEDI);
this.updateReg("CS", this.getCS());
this.updateReg("DS", this.getDS());
this.updateReg("SS", this.getSS());
this.updateReg("ES", this.getES());
this.updateReg("EIP", this.getIP());
var regPS = this.getPS();
this.displayReg("PS", regPS);
this.displayReg("V", (regPS & X86.PS.OF)? 1 : 0, 1);
this.displayReg("D", (regPS & X86.PS.DF)? 1 : 0, 1);
this.displayReg("I", (regPS & X86.PS.IF)? 1 : 0, 1);
this.displayReg("T", (regPS & X86.PS.TF)? 1 : 0, 1);
this.displayReg("S", (regPS & X86.PS.SF)? 1 : 0, 1);
this.displayReg("Z", (regPS & X86.PS.ZF)? 1 : 0, 1);
this.displayReg("A", (regPS & X86.PS.AF)? 1 : 0, 1);
this.displayReg("P", (regPS & X86.PS.PF)? 1 : 0, 1);
this.displayReg("C", (regPS & X86.PS.CF)? 1 : 0, 1);
this.updateReg("PS", regPS);
this.updateReg("V", (regPS & X86.PS.OF));
this.updateReg("D", (regPS & X86.PS.DF));
this.updateReg("I", (regPS & X86.PS.IF));
this.updateReg("T", (regPS & X86.PS.TF));
this.updateReg("S", (regPS & X86.PS.SF));
this.updateReg("Z", (regPS & X86.PS.ZF));
this.updateReg("A", (regPS & X86.PS.AF));
this.updateReg("P", (regPS & X86.PS.PF));
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}
*/
X86.opANDEAX = function ANDEAX()
X86.opANDAXd = function ANDAXd()
{
this.regEAX = X86.fnANDd.call(this, this.regEAX, this.getIPLong());
if (BACKTRACK) {
@ -581,20 +581,27 @@ X86.opES = function ES()
X86.opDAA = function DAA()
{
var AL = this.regEAX & 0xff;
var fAuxCarry = this.getAF();
var fCarry = (this.resultZeroCarry & this.resultSize);
if ((AL & 0xf) > 9 || fAuxCarry) {
var AF = this.getAF();
var CF = this.getCF();
if ((AL & 0xf) > 9 || AF) {
AL += 0x6;
fAuxCarry = true;
AF = X86.PS.AF;
}
if (AL > 0x9f || fCarry) {
if (AL > 0x9f || CF) {
AL += 0x60;
fCarry = true;
CF = X86.PS.CF;
}
this.regEAX = (this.regEAX & ~0xff) | (this.resultZeroCarry = this.resultParitySign = (AL & 0xff));
this.resultSize = X86.RESULT.SIZE_WORD;
if (fCarry) this.resultZeroCarry |= this.resultSize;
if (fAuxCarry) this.setAF(); else this.clearAF();
var b = (AL & 0xff);
this.regEAX = (this.regEAX & ~0xff) | b;
if (OLDFLAGS) {
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
};
@ -696,20 +703,27 @@ X86.opCS = function CS()
X86.opDAS = function DAS()
{
var AL = this.regEAX & 0xff;
var fAuxCarry = this.getAF();
var fCarry = (this.resultZeroCarry & this.resultSize);
if ((AL & 0xf) > 9 || fAuxCarry) {
var AF = this.getAF();
var CF = this.getCF();
if ((AL & 0xf) > 9 || AF) {
AL -= 0x6;
fAuxCarry = true;
AF = X86.PS.AF;
}
if (AL > 0x9f || fCarry) {
if (AL > 0x9f || CF) {
AL -= 0x60;
fCarry = true;
CF = X86.PS.CF;
}
this.regEAX = (this.regEAX & ~0xff) | (this.resultZeroCarry = this.resultParitySign = (AL & 0xff));
this.resultSize = X86.RESULT.SIZE_WORD;
if (fCarry) this.resultZeroCarry |= this.resultSize;
if (fAuxCarry) this.setAF(); else this.clearAF();
var b = (AL & 0xff);
this.regEAX = (this.regEAX & ~0xff) | b;
if (OLDFLAGS) {
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
};
@ -753,6 +767,16 @@ X86.opXORrw = function XORrw()
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)
*
@ -763,8 +787,8 @@ X86.opXORALb = function XORALb()
this.regEAX = (this.regEAX & ~0xff) | X86.fnXORb.call(this, this.regEAX & 0xff, this.getIPByte());
if (BACKTRACK) this.backTrack.btiAL = this.backTrack.btiMemLo;
/*
* In the absence of any EA calculations, opGrpXORb() will deduct nOpCyclesArithRR, and for all CPUs through
* the 80286, we need deduct only one more cycle.
* In the absence of any EA calculations, opGrpXORb() will deduct nOpCyclesArithRR, and for all CPUs
* through the 80286, we need deduct only one more cycle.
*/
this.nStepCycles--;
};
@ -781,8 +805,8 @@ X86.opXORAXw = function XORAXw()
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
* the 80286, we need deduct only one more cycle.
* In the absence of any EA calculations, opGrpXORw() will deduct nOpCyclesArithRR, and for all CPUs
* through the 80286, we need deduct only one more cycle.
*/
this.nStepCycles--;
};
@ -810,21 +834,19 @@ X86.opSS = function SS()
*/
X86.opAAA = function AAA()
{
var CF, AF;
var AL = this.regEAX & 0xff;
var AH = this.regEAX >> 8;
var fCarry;
var fAuxCarry = this.getAF();
if ((AL & 0xf) > 9 || fAuxCarry) {
var AH = (this.regEAX >> 8) & 0xff;
if ((AL & 0xf) > 9 || this.getAF()) {
AL = (AL + 0x6) & 0xf;
AH = (AH + 1) & 0xff;
fCarry = fAuxCarry = true;
CF = AF = 1;
} else {
fCarry = fAuxCarry = false;
CF = AF = 0;
}
this.regEAX = (this.regEAX & ~0xffff) | (AH << 8) | (this.resultZeroCarry = AL);
this.resultSize = X86.RESULT.SIZE_WORD;
if (fCarry) this.resultZeroCarry |= this.resultSize;
if (fAuxCarry) this.setAF(); else this.clearAF();
this.regEAX = (this.regEAX & ~0xffff) | ((AH << 8) | AL);
if (CF) this.setCF(); else this.clearCF();
if (AF) this.setAF(); else this.clearAF();
this.nStepCycles -= this.CYCLES.nOpCyclesAAA;
};
@ -917,32 +939,23 @@ X86.opDS = function DS()
/**
* 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}
*/
X86.opAAS = function AAS()
{
var CF, AF;
var AL = this.regEAX & 0xff;
var AH = this.regEAX >> 8;
var fCarry;
var fAuxCarry = this.getAF();
if ((AL & 0xf) > 9 || fAuxCarry) {
var AH = (this.regEAX >> 8) & 0xff;
if ((AL & 0xf) > 9 || this.getAF()) {
AL = (AL - 0x6) & 0xf;
AH = (AH - 1) & 0xff;
fCarry = fAuxCarry = true;
CF = AF = 1;
} else {
fCarry = fAuxCarry = false;
CF = AF = 0;
}
this.regEAX = (this.regEAX & ~0xffff) | (AH << 8) | (this.resultZeroCarry = AL);
this.resultSize = X86.RESULT.SIZE_WORD;
if (fCarry) this.resultZeroCarry |= this.resultSize;
if (fAuxCarry) this.setAF(); else this.clearAF();
this.regEAX = (this.regEAX & ~0xffff) | ((AH << 8) | AL);
if (CF) this.setCF(); else this.clearCF();
if (AF) this.setAF(); else this.clearAF();
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()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEAX) + 1;
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
this.regEAX = X86.fnINCr.call(this, this.regEAX);
};
/**
@ -972,16 +976,7 @@ X86.opINCAX = function INCAX()
*/
X86.opINCCX = function INCCX()
{
this.resultParitySign = (this.resultAuxOverflow = this.regECX) + 1;
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
this.regECX = X86.fnINCr.call(this, this.regECX);
};
/**
@ -991,16 +986,7 @@ X86.opINCCX = function INCCX()
*/
X86.opINCDX = function INCDX()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEDX) + 1;
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
this.regEDX = X86.fnINCr.call(this, this.regEDX);
};
/**
@ -1010,16 +996,7 @@ X86.opINCDX = function INCDX()
*/
X86.opINCBX = function INCBX()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEBX) + 1;
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
this.regEBX = X86.fnINCr.call(this, this.regEBX);
};
/**
@ -1029,12 +1006,7 @@ X86.opINCBX = function INCBX()
*/
X86.opINCSP = function INCSP()
{
var regESP;
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
this.setSP(X86.fnINCr.call(this, this.getSP()));
};
/**
@ -1044,16 +1016,7 @@ X86.opINCSP = function INCSP()
*/
X86.opINCBP = function INCBP()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEBP) + 1;
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
this.regEBP = X86.fnINCr.call(this, this.regEBP);
};
/**
@ -1063,16 +1026,7 @@ X86.opINCBP = function INCBP()
*/
X86.opINCSI = function INCSI()
{
this.resultParitySign = (this.resultAuxOverflow = this.regESI) + 1;
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
this.regESI = X86.fnINCr.call(this, this.regESI);
};
/**
@ -1082,16 +1036,7 @@ X86.opINCSI = function INCSI()
*/
X86.opINCDI = function INCDI()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEDI) + 1;
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
this.regEDI = X86.fnINCr.call(this, this.regEDI);
};
/**
@ -1101,16 +1046,7 @@ X86.opINCDI = function INCDI()
*/
X86.opDECAX = function DECAX()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEAX) - 1;
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
this.regEAX = X86.fnDECr.call(this, this.regEAX);
};
/**
@ -1120,11 +1056,7 @@ X86.opDECAX = function DECAX()
*/
X86.opDECCX = function DECCX()
{
this.resultParitySign = (this.resultAuxOverflow = this.regECX) - 1;
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
this.regECX = X86.fnDECr.call(this, this.regECX);
};
/**
@ -1134,11 +1066,7 @@ X86.opDECCX = function DECCX()
*/
X86.opDECDX = function DECDX()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEDX) - 1;
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
this.regEDX = X86.fnDECr.call(this, this.regEDX);
};
/**
@ -1148,16 +1076,7 @@ X86.opDECDX = function DECDX()
*/
X86.opDECBX = function DECBX()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEBX) - 1;
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
this.regEBX = X86.fnDECr.call(this, this.regEBX);
};
/**
@ -1167,12 +1086,7 @@ X86.opDECBX = function DECBX()
*/
X86.opDECSP = function DECSP()
{
var regESP;
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
this.setSP(X86.fnDECr.call(this, this.getSP()));
};
/**
@ -1182,16 +1096,7 @@ X86.opDECSP = function DECSP()
*/
X86.opDECBP = function DECBP()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEBP) - 1;
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
this.regEBP = X86.fnDECr.call(this, this.regEBP);
};
/**
@ -1201,16 +1106,7 @@ X86.opDECBP = function DECBP()
*/
X86.opDECSI = function DECSI()
{
this.resultParitySign = (this.resultAuxOverflow = this.regESI) - 1;
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
this.regESI = X86.fnDECr.call(this, this.regESI);
};
/**`
@ -1220,16 +1116,7 @@ X86.opDECSI = function DECSI()
*/
X86.opDECDI = function DECDI()
{
this.resultParitySign = (this.resultAuxOverflow = this.regEDI) - 1;
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
this.regEDI = X86.fnDECr.call(this, this.regEDI);
};
/**
@ -2921,8 +2808,14 @@ X86.opCMPSw = function CMPSw()
*/
X86.opTESTALb = function TESTALb()
{
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regEAX & this.getIPByte();
this.resultSize = X86.RESULT.SIZE_BYTE;
var src = this.getIPByte();
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;
};
@ -2933,8 +2826,14 @@ X86.opTESTALb = function TESTALb()
*/
X86.opTESTAXw = function TESTAXw()
{
this.resultZeroCarry = this.resultParitySign = this.resultAuxOverflow = this.regEAX & this.getIPWord();
this.resultSize = X86.RESULT.SIZE_WORD;
var src = this.getIPWord();
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;
};
@ -3654,23 +3553,32 @@ X86.opGrp2wCL = function GRP2wCL()
*
* 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.
* 2. Set the flags in the following manner:
* 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
* 1. Divide AL by 0x0A; store the quotient in AH and the remainder in AL
* 2. Set PF, SF, and ZF based on the AL register (CF, OF, and AF are undefined)
*
* @this {X86CPU}
*/
X86.opAAM = function AAM()
{
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 bQuotient = (AL / bDivisor) & 0xff;
var bRemainder = AL % bDivisor;
this.regEAX = (bQuotient << 8) | bRemainder;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.resultZeroCarry = this.resultParitySign = AL;
this.regEAX = (this.regEAX & ~0xffff) | ((AL / bDivisor) << 8) | (AL % bDivisor);
if (OLDFLAGS) {
this.resultSize = X86.RESULT.SIZE_BYTE;
this.resultZeroCarry = this.resultParitySign = this.regEAX;
}
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;
};
@ -3679,22 +3587,25 @@ X86.opAAM = function AAM()
*
* From "The 8086 Book":
*
* 1. Multiply the contents of the AH register by 0x0A
* 2. Add AH to AL.
* 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
* 1. Multiply AH by 0x0A, add AH to AL, and store 0x00 in AH
* 2. Set PF, SF, and ZF based on the AL register (CF, OF, and AF are undefined)
*
* @this {X86CPU}
*/
X86.opAAD = function AAD()
{
var bMultiplier = this.getIPByte();
this.resultZeroCarry = this.resultParitySign = this.regEAX = (((this.regEAX >> 8) * bMultiplier) + this.regEAX) & 0xff;
this.resultSize = X86.RESULT.SIZE_BYTE;
this.regEAX = (this.regEAX & ~0xffff) | (((((this.regEAX >> 8) & 0xff) * bMultiplier) + this.regEAX) & 0xff);
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;
};
@ -3747,7 +3658,7 @@ X86.opESC = function ESC()
X86.opLOOPNZ = function LOOPNZ()
{
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.nStepCycles -= this.CYCLES.nOpCyclesLoopNZ;
return;
@ -3763,7 +3674,7 @@ X86.opLOOPNZ = function LOOPNZ()
X86.opLOOPZ = function LOOPZ()
{
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.nStepCycles -= this.CYCLES.nOpCyclesLoopZ;
return;
@ -4121,7 +4032,7 @@ X86.opGrp3w = function GRP3w()
*/
X86.opCLC = function CLC()
{
this.resultZeroCarry &= ~this.resultSize;
this.clearCF();
this.nStepCycles -= 2; // CLC takes 2 cycles on all CPUs
};
@ -4132,7 +4043,7 @@ X86.opCLC = function CLC()
*/
X86.opSTC = function STC()
{
this.resultZeroCarry |= this.resultSize;
this.setCF();
this.nStepCycles -= 2; // STC takes 2 cycles on all CPUs
};
@ -4224,6 +4135,17 @@ X86.opUndefined = function()
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).
*
@ -4405,9 +4327,33 @@ X86.aOpGrp4w = [
];
if (I386) {
/*
* Until we have *d() forms of all *w() opcode handlers, we need to put in placeholders (ie, opTBDd())
*/
X86.aOpsD = {
0x21: X86.opANDmd,
0x23: X86.opANDrd,
0x25: X86.opANDEAX
0x01: X86.opTBDd, // opADDmd()
0x03: X86.opTBDd, // opADDrd()
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