80386 0x0F opcodes more or less fleshed out

This commit is contained in:
Jeff Parsons 2015-03-29 20:37:46 -07:00 committed by jeffpar
commit 177032223c
4 changed files with 452 additions and 15 deletions

View file

@ -426,6 +426,7 @@ X86CPU.CYCLES_80286 = {
};
X86CPU.CYCLES_80386 = {
nOpCyclesBitScan: 11,
nOpCyclesBitSetR: 6,
nOpCyclesBitSetM: 8,
nOpCyclesBitSetMExtra: 5, // extra cycle cost for non-immediate BTC/BTR/BTS opcodes
@ -434,6 +435,8 @@ X86CPU.CYCLES_80386 = {
nOpCyclesBitTestMExtra: 6, // extra cycle cost for non-immediate BT opcode
nOpCyclesIMulR: 9,
nOpCyclesIMulM: 12,
nOpCyclesMovXR: 3,
nOpCyclesMovXM: 6,
nOpCyclesSetR: 4,
nOpCyclesSetM: 5,
nOpCyclesShiftDR: 3,
@ -770,8 +773,11 @@ X86CPU.prototype.initProcessor = function()
this.OPFLAG_NOINTR_8086 = 0; // used with instructions that should *not* set NOINTR on an 80286 (eg, non-SS segment loads)
this.aOps0F = X86.aOps0F;
this.aOps[0x0F] = X86.op0F;
this.aOps[0x0F] = X86.op0F;
this.aOps0F = X86.aOps0F.slice();
for (var i = 0; i < this.aOps0F.length; i++) {
if (!this.aOps0F[i]) this.aOps0F[i] = X86.opUndefined;
}
this.aOps[X86.OPCODE.PUSHSP] = X86.opPUSHSP; // 0x54
this.aOps[X86.OPCODE.ARPL] = X86.opARPL; // 0x63
@ -781,7 +787,6 @@ X86CPU.prototype.initProcessor = function()
this.aOps[X86.OPCODE.GS] = X86.opGS; // 0x65
this.aOps[X86.OPCODE.OS] = X86.opOS; // 0x66
this.aOps[X86.OPCODE.AS] = X86.opAS; // 0x67
this.aOps0F = X86.aOps0F.slice();
for (bOpcode in X86.aOps0F386) {
this.aOps0F[+bOpcode] = X86.aOps0F386[bOpcode];
}

View file

@ -204,6 +204,76 @@ X86.fnBOUND = function BOUND(dst, src)
return dst;
};
/**
* fnBSF(dst, src)
*
* Scan src starting at bit 0. If a set bit is found, the bit index is stored in dst and ZF is cleared;
* otherwise, ZF is set and dst is unchanged.
*
* NOTES: Early versions of the 80386 manuals misstated how ZF was set/cleared. Also, Intel insists that
* dst is undefined whenever ZF is set, but in fact, the 80386 leaves dst unchanged when that happens;
* unfortunately, some early 80486s would always modify dst, so it is unsafe to rely on dst when ZF is set.
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnBSF = function BSF(dst, src)
{
if (!src) {
this.setZF();
} else {
this.clearZF();
var i = 0, bit = 0x1;
while (bit & this.dataMask) {
if (src & bit) {
dst = i;
break;
}
bit <<= 1;
i++;
}
}
this.nStepCycles -= X86CPU.CYCLES_80386.nOpCyclesBitScan + i * 3;
return dst;
};
/**
* fnBSR(dst, src)
*
* Scan src starting from the highest bit. If a set bit is found, the bit index is stored in dst and ZF is
* cleared; otherwise, ZF is set and dst is unchanged.
*
* NOTES: Early versions of the 80386 manuals misstated how ZF was set/cleared. Also, Intel insists that
* dst is undefined whenever ZF is set, but in fact, the 80386 leaves dst unchanged when that happens;
* unfortunately, some early 80486s would always modify dst, so it is unsafe to rely on dst when ZF is set.
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnBSR = function BSR(dst, src)
{
if (!src) {
this.setZF();
} else {
this.clearZF();
var i = (this.dataSize == 2? 15 : 31), j = i, bit = 1 << i;
while (bit) {
if (src & bit) {
dst = i;
break;
}
bit >>>= 1;
i--;
}
}
this.nStepCycles -= X86CPU.CYCLES_80386.nOpCyclesBitScan + (j - i) * 3;
return dst;
};
/**
* fnBT(dst, src)
*
@ -1052,6 +1122,25 @@ X86.fnLES = function LES(dst, src)
return src;
};
/**
* fnLFS(dst, src)
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnLFS = function LFS(dst, src)
{
if (this.regEA === X86.ADDR_INVALID) {
X86.opUndefined.call(this);
return dst;
}
this.setFS(this.getShort(this.regEA + 2));
this.nStepCycles -= this.CYCLES.nOpCyclesLS;
return src;
};
/**
* fnLGDT(dst, src)
*
@ -1079,6 +1168,25 @@ X86.fnLGDT = function LGDT(dst, src)
return dst;
};
/**
* fnLGS(dst, src)
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnLGS = function LGS(dst, src)
{
if (this.regEA === X86.ADDR_INVALID) {
X86.opUndefined.call(this);
return dst;
}
this.setGS(this.getShort(this.regEA + 2));
this.nStepCycles -= this.CYCLES.nOpCyclesLS;
return src;
};
/**
* fnLIDT(dst, src)
*
@ -1173,6 +1281,25 @@ X86.fnLSL = function LSL(dst, src)
return dst;
};
/**
* fnLSS(dst, src)
*
* @this {X86CPU}
* @param {number} dst
* @param {number} src
* @return {number}
*/
X86.fnLSS = function LSS(dst, src)
{
if (this.regEA === X86.ADDR_INVALID) {
X86.opUndefined.call(this);
return dst;
}
this.setSS(this.getShort(this.regEA + 2));
this.nStepCycles -= this.CYCLES.nOpCyclesLS;
return src;
};
/**
* fnLTR(dst, src)
*
@ -1208,6 +1335,19 @@ X86.fnMOV = function MOV(dst, src)
return src;
};
/**
* fnMOVX(dst, src)
*
* @this {X86CPU}
* @param {number} dst (current value, ignored)
* @param {number} src (new value)
* @return {number} dst (updated value, from src)
*/
X86.fnMOVX = function MOVX(dst, src)
{
return src;
};
/**
* fnMOVn(dst, src)
*

View file

@ -219,7 +219,7 @@ X86.opMOVrcr = function MOVrcr()
}
*/
var reg = (bModRM & 0x38) >> 3;
switch (reg) {
switch(reg) {
case 0x0:
this.regMD16 = this.regCR0;
break;
@ -288,7 +288,7 @@ X86.opMOVcrr = function MOVcrr()
return;
}
this.aOpModRegWord[bModRM].call(this, X86.fnMOV);
switch (reg) {
switch(reg) {
case 0x0:
reg = this.regEAX;
this.regEAX = temp;
@ -958,6 +958,20 @@ X86.opIMUL = function IMUL()
this.aOpModRegWord[this.getIPByte()].call(this, this.dataSize == 2? X86.fnIMULrw : X86.fnIMULrd);
};
/**
* opLSS()
*
* op=0x0F,0xB2 (LSS reg,word)
*
* This is like a "MOV reg,rm" operation, but it also loads SS from the next word.
*
* @this {X86CPU}
*/
X86.opLSS = function LSS()
{
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnLSS);
};
/**
* opBTR()
*
@ -971,6 +985,146 @@ X86.opBTR = function BTR()
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= X86CPU.CYCLES_80386.nOpCyclesBitSetMExtra;
};
/**
* opLFS()
*
* op=0x0F,0xB4 (LFS reg,word)
*
* This is like a "MOV reg,rm" operation, but it also loads FS from the next word.
*
* @this {X86CPU}
*/
X86.opLFS = function LFS()
{
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnLFS);
};
/**
* opLGS()
*
* op=0x0F,0xB5 (LGS reg,word)
*
* This is like a "MOV reg,rm" operation, but it also loads GS from the next word.
*
* @this {X86CPU}
*/
X86.opLGS = function LGS()
{
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnLGS);
};
/**
* opMOVZXb()
*
* op=0x0F,0xB6 (MOVZX reg,byte)
*
* @this {X86CPU}
*/
X86.opMOVZXb = function MOVZXb()
{
/*
* The ModRegByte handlers update the registers in the 1st column, but we need to update those in the 2nd column.
*
* 000: AL -> 000: AX
* 001: CL -> 001: CX
* 010: DL -> 010: DX
* 011: BL -> 011: BX
* 100: AH -> 100: SP
* 101: CH -> 101: BP
* 110: DH -> 110: SI
* 111: BH -> 111: DI
*/
var temp;
var bModRM = this.getIPByte();
var reg = (bModRM & 0x38) >> 3;
switch(reg) {
case 0x4:
temp = this.regEAX;
break;
case 0x5:
temp = this.regECX;
break;
case 0x6:
temp = this.regEDX;
break;
case 0x7:
temp = this.regEBX;
break;
}
this.aOpModRegByte[bModRM].call(this, X86.fnMOVX);
switch(reg) {
case 0x0:
this.regEAX = (this.regEAX & ~this.dataMask) | (this.regEAX & 0xff);
break;
case 0x1:
this.regECX = (this.regECX & ~this.dataMask) | (this.regECX & 0xff);
break;
case 0x2:
this.regEDX = (this.regEDX & ~this.dataMask) | (this.regEDX & 0xff);
break;
case 0x3:
this.regEBX = (this.regEBX & ~this.dataMask) | (this.regEBX & 0xff);
break;
case 0x4:
this.regESP = (this.regESP & ~this.dataMask) | ((this.regEAX >> 8) & 0xff);
this.regEAX = temp;
break;
case 0x5:
this.regEBP = (this.regEBP & ~this.dataMask) | ((this.regECX >> 8) & 0xff);
this.regECX = temp;
break;
case 0x6:
this.regESI = (this.regESI & ~this.dataMask) | ((this.regEDX >> 8) & 0xff);
this.regEDX = temp;
break;
case 0x7:
this.regEDI = (this.regEDI & ~this.dataMask) | ((this.regEBX >> 8) & 0xff);
this.regEBX = temp;
break;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? X86CPU.CYCLES_80386.nOpCyclesMovXR : X86CPU.CYCLES_80386.nOpCyclesMovXM);
};
/**
* opMOVZXw()
*
* op=0x0F,0xB7 (MOVZX reg,word)
*
* @this {X86CPU}
*/
X86.opMOVZXw = function MOVZXw()
{
var bModRM = this.getIPByte();
this.aOpModRegWord[bModRM].call(this, X86.fnMOVX);
switch((bModRM & 0x38) >> 3) {
case 0x0:
this.regEAX = (this.regEAX & 0xffff);
break;
case 0x1:
this.regECX = (this.regECX & 0xffff);
break;
case 0x2:
this.regEDX = (this.regEDX & 0xffff);
break;
case 0x3:
this.regEBX = (this.regEBX & 0xffff);
break;
case 0x4:
this.regESP = (this.regESP & 0xffff);
break;
case 0x5:
this.regEBP = (this.regEBP & 0xffff);
break;
case 0x6:
this.regESI = (this.regESI & 0xffff);
break;
case 0x7:
this.regEDI = (this.regEDI & 0xffff);
break;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? X86CPU.CYCLES_80386.nOpCyclesMovXR : X86CPU.CYCLES_80386.nOpCyclesMovXM);
};
/**
* op=0x0F,0xBA (GRP8 mem/reg) (80386 and up)
*
@ -994,6 +1148,142 @@ X86.opBTC = function BTC()
if (this.regEA !== X86.ADDR_INVALID) this.nStepCycles -= X86CPU.CYCLES_80386.nOpCyclesBitSetMExtra;
};
/**
* opBSF()
*
* op=0x0F,0xBB (BSF reg,mem/reg)
*
* @this {X86CPU}
*/
X86.opBSF = function BSF()
{
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnBSF);
};
/**
* opBSR()
*
* op=0x0F,0xBC (BSR reg,mem/reg)
*
* @this {X86CPU}
*/
X86.opBSR = function BSR()
{
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnBSR);
};
/**
* opMOVSXb()
*
* op=0x0F,0xBE (MOVSX reg,byte)
*
* @this {X86CPU}
*/
X86.opMOVSXb = function MOVSXb()
{
/*
* The ModRegByte handlers update the registers in the 1st column, but we need to update those in the 2nd column.
*
* 000: AL -> 000: AX
* 001: CL -> 001: CX
* 010: DL -> 010: DX
* 011: BL -> 011: BX
* 100: AH -> 100: SP
* 101: CH -> 101: BP
* 110: DH -> 110: SI
* 111: BH -> 111: DI
*/
var temp;
var bModRM = this.getIPByte();
var reg = (bModRM & 0x38) >> 3;
switch(reg) {
case 0x4:
temp = this.regEAX;
break;
case 0x5:
temp = this.regECX;
break;
case 0x6:
temp = this.regEDX;
break;
case 0x7:
temp = this.regEBX;
break;
}
this.aOpModRegByte[bModRM].call(this, X86.fnMOVX);
switch(reg) {
case 0x0:
this.regEAX = (this.regEAX & ~this.dataMask) | ((((this.regEAX & 0xff) << 24) >> 24) & this.dataMask);
break;
case 0x1:
this.regECX = (this.regECX & ~this.dataMask) | ((((this.regECX & 0xff) << 24) >> 24) & this.dataMask);
break;
case 0x2:
this.regEDX = (this.regEDX & ~this.dataMask) | ((((this.regEDX & 0xff) << 24) >> 24) & this.dataMask);
break;
case 0x3:
this.regEBX = (this.regEBX & ~this.dataMask) | ((((this.regEBX & 0xff) << 24) >> 24) & this.dataMask);
break;
case 0x4:
this.regESP = (this.regESP & ~this.dataMask) | (((this.regEAX << 16) >> 24) & this.dataMask);
this.regEAX = temp;
break;
case 0x5:
this.regEBP = (this.regEBP & ~this.dataMask) | (((this.regECX << 16) >> 24) & this.dataMask);
this.regECX = temp;
break;
case 0x6:
this.regESI = (this.regESI & ~this.dataMask) | (((this.regEDX << 16) >> 24) & this.dataMask);
this.regEDX = temp;
break;
case 0x7:
this.regEDI = (this.regEDI & ~this.dataMask) | (((this.regEBX << 16) >> 24) & this.dataMask);
this.regEBX = temp;
break;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? X86CPU.CYCLES_80386.nOpCyclesMovXR : X86CPU.CYCLES_80386.nOpCyclesMovXM);
};
/**
* opMOVSXw()
*
* op=0x0F,0xBF (MOVSX reg,word)
*
* @this {X86CPU}
*/
X86.opMOVSXw = function MOVSXw()
{
var bModRM = this.getIPByte();
this.aOpModRegWord[bModRM].call(this, X86.fnMOVX);
switch((bModRM & 0x38) >> 3) {
case 0x0:
this.regEAX = ((this.regEAX << 16) >> 16);
break;
case 0x1:
this.regECX = ((this.regECX << 16) >> 16);
break;
case 0x2:
this.regEDX = ((this.regEDX << 16) >> 16);
break;
case 0x3:
this.regEBX = ((this.regEBX << 16) >> 16);
break;
case 0x4:
this.regESP = ((this.regESP << 16) >> 16);
break;
case 0x5:
this.regEBP = ((this.regEBP << 16) >> 16);
break;
case 0x6:
this.regESI = ((this.regESI << 16) >> 16);
break;
case 0x7:
this.regEDI = ((this.regEDI << 16) >> 16);
break;
}
this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? X86CPU.CYCLES_80386.nOpCyclesMovXR : X86CPU.CYCLES_80386.nOpCyclesMovXM);
};
X86.aOps0F = new Array(256);
X86.aOps0F[0x00] = X86.opGRP6;
@ -1009,10 +1299,6 @@ X86.aOps0F[0x06] = X86.opCLTS;
*/
X86.aOps0F[0x0B] = X86.opInvalid;
for (var i = 0; i < X86.aOps0F.length; i++) {
if (!X86.aOps0F[i]) X86.aOps0F[i] = X86.opUndefined;
}
if (I386) {
X86.aOps0F386 = [];
X86.aOps0F386[0x20] = X86.opMOVrcr;
@ -1060,9 +1346,17 @@ if (I386) {
X86.aOps0F386[0xAC] = X86.opSHRDn;
X86.aOps0F386[0xAD] = X86.opSHRDcl;
X86.aOps0F386[0xAF] = X86.opIMUL;
X86.aOps0F386[0xB2] = X86.opLSS;
X86.aOps0F386[0xB3] = X86.opBTR;
X86.aOps0F386[0xB4] = X86.opLFS;
X86.aOps0F386[0xB5] = X86.opLGS;
X86.aOps0F386[0xB6] = X86.opMOVZXb;
X86.aOps0F386[0xB7] = X86.opMOVZXw;
X86.aOps0F386[0xBA] = X86.opGRP8;
X86.aOps0F386[0xBB] = X86.opBTC;
X86.aOps0F386[0xBC] = X86.opBSF;
X86.aOps0F386[0xBE] = X86.opMOVSXb;
X86.aOps0F386[0xBF] = X86.opMOVSXw;
}
/*

View file

@ -3318,26 +3318,24 @@ X86.opRET = function RET()
/**
* op=0xC4 (LES reg,word)
*
* This is like a "MOV reg,rm" operation, but it also loads ES from the next word.
*
* @this {X86CPU}
*/
X86.opLES = function LES()
{
/*
* This is like a "MOV reg,rm" operation, but it also loads ES from the next word.
*/
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnLES);
};
/**
* op=0xC5 (LDS reg,word)
*
* This is like a "MOV reg,rm" operation, but it also loads DS from the next word.
*
* @this {X86CPU}
*/
X86.opLDS = function LDS()
{
/*
* This is like a "MOV reg,rm" operation, but it also loads DS from the next word.
*/
this.aOpModRegWord[this.getIPByte()].call(this, X86.fnLDS);
};