diff --git a/blog/2015/02/23/README.md b/blog/2015/02/23/README.md index fca119240..9e6e51644 100644 --- a/blog/2015/02/23/README.md +++ b/blog/2015/02/23/README.md @@ -6,8 +6,7 @@ problems were fixed by a later stepping, seems virtually impossible at this late I won't make the attempt here, either. Using information from various sources, I'll start with an overview of the steppings, including how each stepping was externally marked and internally identified, along with lists -of associated errata, then move on to more detailed errata information (from Intel's own documents), and end -with a summary of undocumented 80386 instructions. +of associated errata, then move on to more detailed errata information, based on Intel's own documents. ### Steppings @@ -866,23 +865,15 @@ Here's more information on the opcodes (IBTS and XBTS) that were removed from th CPU: 80386 step A0-B0 only Type of Instruction: User - Instruction: IBTS base,bitoffset,len,sorc - - Description: - Write bit string length bits from - [bits .. 0 ] (lowest bits) to bitfield, - defined by and bitsoffset from this base - to start of the field to write. String write from this start - field bit to higher memory addresses or register bits. + Instruction: IBTS base,bitoffset,len,src Flags Affected: None CPU mode: RM,PM,VM - +++++++++++++++++++++++ - Physical Form: IBTS r/m16,AX,CL,r16 - IBTS r/m32,EAX,CL,r32 - COP (Code of Operation) : 0FH A7H + IBTS r/m16,AX,CL,r16 + IBTS r/m32,EAX,CL,r32 + COP (Code of Operation): 0FH A7H Clocks: IBTS 80386: 12/19 @@ -894,29 +885,15 @@ Here's more information on the opcodes (IBTS and XBTS) that were removed from th CPU: 80386 step A0-B0 only Type of Instruction: User - Instruction: XBTS dest,base,bitoffset,len - - Description: - Write bit string length bits from bitfield, defined by - and bitsoffset from this base to start of - the field to read. String read from this start field bit to - higher memory addresses or register bits. - And after it string placed to operand, lowest bit of - register or memory to bit 0 of . - - Note: Use SHLD/SHRD instructions for extract bits strings. - On 80386 steps B1+ this opcode generation INT 6, - and on some of 486 other instruction replace this - instruction opcode. + Instruction: XBTS dst,base,bitoffset,len Flags Affected: None CPU mode: RM,PM,VM - +++++++++++++++++++++++ - Physical Form: XBTS r16,r/m16,AX,CL + XBTS r16,r/m16,AX,CL XBTS r32,r/m32,EAX,CL - COP (Code of Operation) : 0FH A6H + COP (Code of Operation): 0FH A6H Clocks: XBTS 80386: 6/13 diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index ab497c0d3..2ec239f70 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -327,25 +327,25 @@ if (DEBUGGER) { FCOM: 40, FCOMP: 41, FDIV: 42, FDIVR: 43, FIADD: 44, FICOM: 45, FICOMP: 46, FIDIV: 47, FIDIVR: 48, FILD: 49, FIMUL: 50, FIST: 51, FISTP: 52, FISUB: 53, FISUBR: 54, FLD: 55, FLDCW: 56, FLDENV: 57, FMUL: 58, FNSAVE: 59, FNSTCW: 60, FNSTENV:61, FNSTSW: 62, FRSTOR: 63, - FS: 64, FST: 65, FSTP: 66, FSUB: 67, FSUBR: 68, GS: 69, HLT: 70, IDIV: 71, - IMUL: 72, IN: 73, INC: 74, INS: 75, INT: 76, INT3: 77, INTO: 78, IRET: 79, - JBE: 80, JC: 81, JCXZ: 82, JG: 83, JGE: 84, JL: 85, JLE: 86, JMP: 87, - JA: 88, JNC: 89, JNO: 90, JNP: 91, JNS: 92, JNZ: 93, JO: 94, JP: 95, - JS: 96, JZ: 97, LAHF: 98, LAR: 99, LDS: 100, LEA: 101, LEAVE: 102, LES: 103, - LFS: 104, LGDT: 105, LGS: 106, LIDT: 107, LLDT: 108, LMSW: 109, LOADALL:110, LOCK: 111, - LODSB: 112, LODSW: 113, LOOP: 114, LOOPNZ: 115, LOOPZ: 116, LSL: 117, LSS: 118, LTR: 119, - MOV: 120, MOVSB: 121, MOVSW: 122, MOVSX: 123, MOVZX: 124, MUL: 125, NEG: 126, NOP: 127, - NOT: 128, OR: 129, OS: 130, OUT: 131, OUTS: 132, POP: 133, POPA: 134, POPF: 135, - PUSHF: 136, PUSHA: 137, PUSH: 138, RCL: 139, RCR: 140, REPNZ: 141, REPZ: 142, RET: 143, - RETF: 144, ROL: 145, ROR: 146, SAHF: 147, SALC: 148, SAR: 149, SBB: 150, SCASB: 151, - SCASW: 152, SETBE: 153, SETC: 154, SETG: 155, SETGE: 156, SETL: 157, SETLE: 158, SETNBE: 159, - SETNC: 160, SETNO: 161, SETNP: 162, SETNS: 163, SETNZ: 164, SETO: 165, SETP: 166, SETS: 167, - SETZ: 168, SGDT: 169, SHL: 170, SHLD: 171, SHR: 172, SHRD: 173, SIDT: 174, SLDT: 175, - SMSW: 176, SS: 177, STC: 178, STD: 179, STI: 180, STOSB: 181, STOSW: 182, STR: 183, - SUB: 184, TEST: 185, VERR: 186, VERW: 187, WAIT: 188, XCHG: 189, XLAT: 190, XOR: 191, - GRP1B: 192, GRP1W: 193, GRP1SW: 194, GRP2B: 195, GRP2W: 196, GRP2B1: 197, GRP2W1: 198, GRP2BC: 199, - GRP2WC: 200, GRP3B: 201, GRP3W: 202, GRP4B: 203, GRP4W: 204, OP0F: 205, GRP6: 206, GRP7: 207, - GRP8: 208 + FS: 64, FST: 65, FSTP: 66, FSUB: 67, FSUBR: 68, GS: 69, HLT: 70, IBTS: 71, + IDIV: 72, IMUL: 73, IN: 74, INC: 75, INS: 76, INT: 77, INT3: 78, INTO: 79, + IRET: 80, JBE: 81, JC: 82, JCXZ: 83, JG: 84, JGE: 85, JL: 86, JLE: 87, + JMP: 88, JA: 89, JNC: 90, JNO: 91, JNP: 92, JNS: 93, JNZ: 94, JO: 95, + JP: 96, JS: 97, JZ: 98, LAHF: 99, LAR: 100, LDS: 101, LEA: 102, LEAVE: 103, + LES: 104, LFS: 105, LGDT: 106, LGS: 107, LIDT: 108, LLDT: 109, LMSW: 110, LOADALL:111, + LOCK: 112, LODSB: 113, LODSW: 114, LOOP: 115, LOOPNZ: 116, LOOPZ: 117, LSL: 118, LSS: 119, + LTR: 120, MOV: 121, MOVSB: 122, MOVSW: 123, MOVSX: 124, MOVZX: 125, MUL: 126, NEG: 127, + NOP: 128, NOT: 129, OR: 130, OS: 131, OUT: 132, OUTS: 133, POP: 134, POPA: 135, + POPF: 136, PUSHF: 137, PUSHA: 138, PUSH: 139, RCL: 140, RCR: 141, REPNZ: 142, REPZ: 143, + RET: 144, RETF: 145, ROL: 146, ROR: 147, SAHF: 148, SALC: 149, SAR: 150, SBB: 151, + SCASB: 152, SCASW: 153, SETBE: 154, SETC: 155, SETG: 156, SETGE: 157, SETL: 158, SETLE: 159, + SETNBE: 160, SETNC: 161, SETNO: 162, SETNP: 163, SETNS: 164, SETNZ: 165, SETO: 166, SETP: 167, + SETS: 168, SETZ: 169, SGDT: 170, SHL: 171, SHLD: 172, SHR: 173, SHRD: 174, SIDT: 175, + SLDT: 176, SMSW: 177, SS: 178, STC: 179, STD: 180, STI: 181, STOSB: 182, STOSW: 183, + STR: 184, SUB: 185, TEST: 186, VERR: 187, VERW: 188, WAIT: 189, XBTS: 190, XCHG: 191, + XLAT: 192, XOR: 193, GRP1B: 194, GRP1W: 195, GRP1SW: 196, GRP2B: 197, GRP2W: 198, GRP2B1: 199, + GRP2W1: 200, GRP2BC: 201, GRP2WC: 202, GRP3B: 203, GRP3W: 204, GRP4B: 205, GRP4W: 206, OP0F: 207, + GRP6: 208, GRP7: 209, GRP8: 210 }; /* @@ -360,22 +360,23 @@ if (DEBUGGER) { "FCOM", "FCOMP", "FDIV", "FDIVR", "FIADD", "FICOM", "FICOMP", "FIDIV", "FIDIVR", "FILD", "FIMUL", "FIST", "FISTP", "FISUB", "FISUBR", "FLD", "FLDCW", "FLDENV", "FMUL", "FNSAVE", "FNSTCW", "FNSTENV","FNSTSW", "FRSTOR", - "FS:", "FST", "FSTP", "FSUB", "FSUBR", "GS:", "HLT", "IDIV", - "IMUL", "IN", "INC", "INS", "INT", "INT3", "INTO", "IRET", - "JBE", "JC", "JCXZ", "JG", "JGE", "JL", "JLE", "JMP", - "JA", "JNC", "JNO", "JNP", "JNS", "JNZ", "JO", "JP", - "JS", "JZ", "LAHF", "LAR", "LDS", "LEA", "LEAVE", "LES", - "LFS", "LGDT", "LGS", "LIDT", "LLDT", "LMSW", "LOADALL","LOCK", - "LODSB", "LODSW", "LOOP", "LOOPNZ", "LOOPZ", "LSL", "LSS", "LTR", - "MOV", "MOVSB", "MOVSW", "MOVSX", "MOVZX", "MUL", "NEG", "NOP", - "NOT", "OR", "OS:", "OUT", "OUTS", "POP", "POPA", "POPF", - "PUSHF", "PUSHA", "PUSH", "RCL", "RCR", "REPNZ", "REPZ", "RET", - "RETF", "ROL", "ROR", "SAHF", "SALC", "SAR", "SBB", "SCASB", - "SCASW", "SETBE", "SETC", "SETG", "SETGE", "SETL", "SETLE", "SETNBE", - "SETNC", "SETNO", "SETNP", "SETNS", "SETNZ", "SETO", "SETP", "SETS", - "SETZ", "SGDT", "SHL", "SHLD", "SHR", "SHRD", "SIDT", "SLDT", - "SMSW", "SS:", "STC", "STD", "STI", "STOSB", "STOSW", "STR", - "SUB", "TEST", "VERR", "VERW", "WAIT", "XCHG", "XLAT", "XOR" + "FS:", "FST", "FSTP", "FSUB", "FSUBR", "GS:", "HLT", "IBTS", + "IDIV", "IMUL", "IN", "INC", "INS", "INT", "INT3", "INTO", + "IRET", "JBE", "JC", "JCXZ", "JG", "JGE", "JL", "JLE", + "JMP", "JA", "JNC", "JNO", "JNP", "JNS", "JNZ", "JO", + "JP", "JS", "JZ", "LAHF", "LAR", "LDS", "LEA", "LEAVE", + "LES", "LFS", "LGDT", "LGS", "LIDT", "LLDT", "LMSW", "LOADALL", + "LOCK", "LODSB", "LODSW", "LOOP", "LOOPNZ", "LOOPZ", "LSL", "LSS", + "LTR", "MOV", "MOVSB", "MOVSW", "MOVSX", "MOVZX", "MUL", "NEG", + "NOP", "NOT", "OR", "OS:", "OUT", "OUTS", "POP", "POPA", + "POPF", "PUSHF", "PUSHA", "PUSH", "RCL", "RCR", "REPNZ", "REPZ", + "RET", "RETF", "ROL", "ROR", "SAHF", "SALC", "SAR", "SBB", + "SCASB", "SCASW", "SETBE", "SETC", "SETG", "SETGE", "SETL", "SETLE", + "SETNBE", "SETNC", "SETNO", "SETNP", "SETNS", "SETNZ", "SETO", "SETP", + "SETS", "SETZ", "SGDT", "SHL", "SHLD", "SHR", "SHRD", "SIDT", + "SLDT", "SMSW", "SS:", "STC", "STD", "STI", "STOSB", "STOSW", + "STR", "SUB", "TEST", "VERR", "VERW", "WAIT", "XBTS", "XCHG", + "XLAT", "XOR" ]; Debugger.CPU_8086 = 0; @@ -981,6 +982,8 @@ if (DEBUGGER) { 0xA3: [Debugger.INS.BT, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_IN], 0xA4: [Debugger.INS.SHLD, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_IN, Debugger.TYPE_IMM | Debugger.TYPE_BYTE | Debugger.TYPE_IN], 0xA5: [Debugger.INS.SHLD, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_IN, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN], + 0xA6: [Debugger.INS.XBTS, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_IN, Debugger.TYPE_IMPREG | Debugger.TYPE_AX | Debugger.TYPE_IN, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN], + 0xA7: [Debugger.INS.IBTS, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_IMPREG | Debugger.TYPE_AX | Debugger.TYPE_IN, Debugger.TYPE_IMPREG | Debugger.TYPE_CL | Debugger.TYPE_IN, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_IN], 0xA8: [Debugger.INS.PUSH, Debugger.TYPE_GS | Debugger.TYPE_IN | Debugger.TYPE_80386], 0xA9: [Debugger.INS.POP, Debugger.TYPE_GS | Debugger.TYPE_OUT | Debugger.TYPE_80386], 0xAB: [Debugger.INS.BTS, Debugger.TYPE_MODRM | Debugger.TYPE_VWORD | Debugger.TYPE_OUT | Debugger.TYPE_80386, Debugger.TYPE_REG | Debugger.TYPE_VWORD | Debugger.TYPE_IN], diff --git a/modules/pcjs/lib/x86.js b/modules/pcjs/lib/x86.js index 1475a11d9..ec89f98ab 100644 --- a/modules/pcjs/lib/x86.js +++ b/modules/pcjs/lib/x86.js @@ -44,10 +44,12 @@ var X86 = { MODEL_80386: 80386, /* - * CPU stepping identifiers (supported) + * 80386 CPU stepping identifiers (supported) */ - STEPPING_B1: 0xB1, // our version of the B1 stepping also includes the infamous 32-bit multiplication bug - STEPPING_B2: 0xB2, // this is an imaginary stepping that simply means "B1 without the 32-bit multiplication bug" (ie, a B1 with the "double sigma" stamp) + STEPPING_80386_A0: (80386+0xA0), + STEPPING_80386_B0: (80386+0xB0), // for now, the only B0 difference is support for XBTS + STEPPING_80386_B1: (80386+0xB1), // our version of the B1 stepping also includes the infamous 32-bit multiplication bug + STEPPING_80386_B2: (80386+0xB2), // this is an imaginary stepping that simply means "B1 without the 32-bit multiplication bug" (ie, a B1 with the "double sigma" stamp) /* * This constant is used to mark points in the code where the physical address being returned diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index b24a2bda5..a76e62b13 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -110,8 +110,13 @@ function X86CPU(parmsCPU) { this.model = parmsCPU['model'] || X86.MODEL_8088; + /* + * We take the 'stepping' value, convert it to a hex value, and then add that to the model to provide + * a single value that's unique for any given CPU stepping. If no stepping is provided, then stepping + * is equal to model. + */ var stepping = parmsCPU['stepping']; - this.stepping = (stepping? str.parseInt(stepping, 16) : 0); + this.stepping = this.model + (stepping? str.parseInt(stepping, 16) : 0); var nCyclesDefault = 0; switch(this.model) { @@ -812,6 +817,10 @@ X86CPU.prototype.initProcessor = function() for (bOpcode in X86.aOps0F386) { this.aOps0F[+bOpcode] = X86.aOps0F386[bOpcode]; } + if (this.stepping >= X86.STEPPING_80386_A0 && this.stepping <= X86.STEPPING_80386_B0) { + this.aOps0F[0xA6] = X86.opXBTS; + this.aOps0F[0xA7] = X86.opIBTS; + } } } } diff --git a/modules/pcjs/lib/x86func.js b/modules/pcjs/lib/x86func.js index 5f5af3496..96fd7528f 100644 --- a/modules/pcjs/lib/x86func.js +++ b/modules/pcjs/lib/x86func.js @@ -683,6 +683,28 @@ X86.fnDECw = function(dst, src) return w & this.maskData; }; +/** + * fnIBTS(dst, src) + * + * As best I can determine, this function copies the specified bits from src (starting at bit 0 for CL + * bits) to dst (starting at bit offset in AX). For register operands, that's simple enough. + * + * TODO: If dst refers to a memory location, then the bit index may refer to higher memory locations, just + * like the BT/BTC/BTR/BTS instructions. For an instruction that no one was really able to use, except + * as a CPU stepping discriminator, that doesn't seem worth the effort. + * + * @this {X86CPU} + * @param {number} dst + * @param {number} src + * @return {number} + */ +X86.fnIBTS = function(dst, src) +{ + var shift = (this.regEAX & this.maskData); + var mask = ((1 << (this.regECX & 0x1f)) - 1); + return (dst & ~(mask << shift)) | ((src & mask) << shift); +}; + /** * fnSet64(lo, hi) * @@ -2033,11 +2055,11 @@ X86.fnMULw = function(dst, src) this.regMDHi = (result >> 16) & 0xffff; } else { X86.fnMUL32.call(this, dst, this.regEAX); - if (this.model == X86.MODEL_80386 && this.stepping == X86.STEPPING_B1) { + if (this.stepping == X86.STEPPING_80386_B1) { if (this.regEAX == 0x0417A000 && dst == 0x00000081) { /* - * In this case, the result should be 0x20FE7A000 (ie, regMDHi should be 0x2), and I'm not - * sure what the typical failure would look like, so I'll just set regMDHi to 0. + * Normally, the result should be 0x20FE7A000 (ie, regMDHi should be 0x2). + * I'm not sure what a typical failure looked like, so I'll just set regMDHi to 0. * * If you want a B1 stepping without this 32-bit multiplication flaw, select the B2 stepping. */ @@ -3538,6 +3560,30 @@ X86.fnVERW = function(dst, src) return dst; }; +/** + * fnXBTS(dst, src) + * + * As best I can determine, this function copies the specified bits from src (starting at the bit offset + * in AX, for the bit length in CL) to dst (starting at bit 0). For register operands, that's simple enough. + * + * TODO: If src refers to a memory location, then the bit index may refer to higher memory locations, just + * like the BT/BTC/BTR/BTS instructions. For an instruction that no one was really able to use, except + * as a CPU stepping discriminator, that doesn't seem worth the effort. + * + * @this {X86CPU} + * @param {number} dst + * @param {number} src + * @return {number} + */ +X86.fnXBTS = function(dst, src) +{ + /* + * Shift src right by the bit offset in [E]AX, then apply a mask equal to the number of bits in CL, + * then mask the resulting bit string with the current OPERAND size. + */ + return ((src >> (this.regEAX & this.maskData)) & ((1 << (this.regECX & 0x1f)) - 1)) & this.maskData; +}; + /** * fnXCHGrb(dst, src) * diff --git a/modules/pcjs/lib/x86op0f.js b/modules/pcjs/lib/x86op0f.js index f60c8a2af..797838a1c 100644 --- a/modules/pcjs/lib/x86op0f.js +++ b/modules/pcjs/lib/x86op0f.js @@ -1166,6 +1166,32 @@ X86.opSHLDcl = function SHLDcl() this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 3 : 7); }; +/** + * opXBTS() + * + * op=0x0F,0xA6 (XBTS reg,mem/reg,[E]AX,CL) + * + * @this {X86CPU} + */ +X86.opXBTS = function XBTS() +{ + this.aOpModRegWord[this.getIPByte()].call(this, X86.fnXBTS); + this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 6 : 13); +}; + +/** + * opIBTS() + * + * op=0x0F,0xA7 (IBTS mem/reg,[E]AX,CL,reg) + * + * @this {X86CPU} + */ +X86.opIBTS = function IBTS() +{ + this.aOpModMemWord[this.getIPByte()].call(this, X86.fnIBTS); + this.nStepCycles -= (this.regEA === X86.ADDR_INVALID? 12 : 19); +}; + /** * opPUSHGS() * diff --git a/modules/pcjs/lib/x86ops.js b/modules/pcjs/lib/x86ops.js index afab25b1c..93bb8029c 100644 --- a/modules/pcjs/lib/x86ops.js +++ b/modules/pcjs/lib/x86ops.js @@ -2952,9 +2952,10 @@ X86.opSTOSb = function STOSb() * only EDI in the case of STOS. The other instructions mentioned below monkey with different * registers, so read the errata carefully. * - * TODO: Extend this errata to STOSW, as well as MOVSB, MOVSW, INSB, and INSW. + * TODO: Extend this errata to STOSW, as well as MOVSB, MOVSW, INSB, and INSW. Also, scope out the + * extent to which this errara also existed on earlier steppings. */ - if (this.model == X86.MODEL_80386 && this.stepping == X86.STEPPING_B1) { + if (this.stepping == X86.STEPPING_80386_B1) { if (!(this.opPrefixes & X86.OPFLAG.ADDRSIZE) != (this.getByte(this.regLIP) != X86.OPCODE.AS)) { maskAddr ^= (0xffff0000|0); }