From 9523de773dfb3df7bf47716a612ff12425cb608b Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Tue, 25 Nov 2014 17:21:20 -0800 Subject: [PATCH] More protected-mode improvements --- modules/diskdump/lib/diskdump.js | 19 ++- modules/pcjs/lib/chipset.js | 7 +- modules/pcjs/lib/debugger.js | 31 ++-- modules/pcjs/lib/x86cpu.js | 9 +- modules/pcjs/lib/x86help.js | 7 +- modules/pcjs/lib/x86seg.js | 239 +++++++++++++++++++------------ modules/shared/lib/component.js | 4 +- 7 files changed, 199 insertions(+), 117 deletions(-) diff --git a/modules/diskdump/lib/diskdump.js b/modules/diskdump/lib/diskdump.js index 050b8305d..69130048d 100644 --- a/modules/diskdump/lib/diskdump.js +++ b/modules/diskdump/lib/diskdump.js @@ -222,12 +222,12 @@ DiskDump.aDefaultBPBs = [ ], [ // define BPB for 1.2Mb diskette 0xEB, 0xFE, 0x90, // 0x00: JMP instruction, following by 8-byte OEM signature - 0x49, 0x42, 0x4d, 0x20, 0x31, 0x30, 0x2e, 0x31, // "IBM 10.0" (which I believe was used on IBM OS/2 1.0 diskettes) + 0x49, 0x42, 0x4D, 0x20, 0x31, 0x30, 0x2E, 0x31, // "IBM 10.0" (which I believe was used on IBM OS/2 1.0 diskettes) 0x00, 0x02, // 0x0B: bytes per sector (0x200 or 512) 0x01, // 0x0D: sectors per cluster (1) 0x01, 0x00, // 0x0E: reserved sectors; ie, # sectors preceding the first FAT--usually just the boot sector (1) 0x02, // 0x10: FAT copies (2) - 0xe0, 0x00, // 0x11: root directory entries (0xe0 or 224) 0xe0 * 0x20 = 0x1c00 (1 sector is 0x200 bytes, total of 14 sectors) + 0xE0, 0x00, // 0x11: root directory entries (0xe0 or 224) 0xe0 * 0x20 = 0x1c00 (1 sector is 0x200 bytes, total of 14 sectors) 0x60, 0x09, // 0x13: number of sectors (0x960 or 2400) 0xF9, // 0x15: media type (0xF9 was used for 1228800-byte diskettes, and later for 737280-byte diskettes) 0x07, 0x00, // 0x16: sectors per FAT (7) @@ -235,6 +235,21 @@ DiskDump.aDefaultBPBs = [ 0x02, 0x00, // 0x1A: number of heads (2) 0x00, 0x00, 0x00, 0x00 // 0x1C: number of hidden sectors (always 0 for non-partitioned media) ], + [ // define BPB for 1.44Mb diskette + 0xEB, 0xFE, 0x90, // 0x00: JMP instruction, following by 8-byte OEM signature + 0x4d, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, // "MSDOS5.0" (an actual OEM signature, arbitrarily chosen for use here) + 0x00, 0x02, // 0x0B: bytes per sector (0x200 or 512) + 0x01, // 0x0D: sectors per cluster (1) + 0x01, 0x00, // 0x0E: reserved sectors; ie, # sectors preceding the first FAT--usually just the boot sector (1) + 0x02, // 0x10: FAT copies (2) + 0xE0, 0x00, // 0x11: root directory entries (0xe0 or 224) 0xe0 * 0x20 = 0x1c00 (1 sector is 0x200 bytes, total of 14 sectors) + 0x40, 0x0B, // 0x13: number of sectors (0xb40 or 2880) + 0xF0, // 0x15: media type (0xF0 was used for 1474560-byte diskettes) + 0x09, 0x00, // 0x16: sectors per FAT (9) + 0x12, 0x00, // 0x18: sectors per track (18) + 0x02, 0x00, // 0x1A: number of heads (2) + 0x00, 0x00, 0x00, 0x00 // 0x1C: number of hidden sectors (always 0 for non-partitioned media) + ], [ // define BPB for 10Mb hard disk 0xEB, 0xFE, 0x90, // 0x00: JMP instruction, following by 8-byte OEM signature 0x49, 0x42, 0x4D, 0x20, 0x20, 0x32, 0x2E, 0x30, // "IBM 2.0" (this is a real OEM signature) diff --git a/modules/pcjs/lib/chipset.js b/modules/pcjs/lib/chipset.js index 1ea6550db..af658254e 100644 --- a/modules/pcjs/lib/chipset.js +++ b/modules/pcjs/lib/chipset.js @@ -2993,7 +2993,7 @@ ChipSet.prototype.outPICLo = function(iPIC, bOut, addrFrom) } else { if (DEBUG) { this.messageDebugger("outPIC" + iPIC + "(" + str.toHexByte(pic.port) + "): unexpected EOI command, IRQ " + nIRQ + " not in service", Debugger.MESSAGE.PIC | Debugger.MESSAGE.WARN); - if (this.dbg && !SAMPLER) this.dbg.stopCPU(); + if (this.dbg && !SAMPLER && MAXDEBUG) this.dbg.stopCPU(); } } /* @@ -4313,9 +4313,8 @@ ChipSet.prototype.set8042OutPort = function(b) * notifyKbdData(fAvail) * * In the old days of PCjs, the Keyboard component would simply call setIRR() when it had some data for the - * keyboard controller. However, that was completely inappropriate. The sole responsibility of the Keyboard - * is to emulate an actual keyboard and notify us whenever it has some data; it has no business messing with - * IRQ lines. + * keyboard controller. However, the sole responsibility of the Keyboard is to emulate an actual keyboard and + * call notifyKbdData() whenever it has some data; it has no business messing with IRQ lines. * * If there's an 8042, we check (this.b8042CmdData & ChipSet.KBC.DATA.CMD.NO_CLOCK); if NO_CLOCK is clear, * we can raise the IRQ immediately. Well, not quite immediately.... diff --git a/modules/pcjs/lib/debugger.js b/modules/pcjs/lib/debugger.js index c96f468db..90bb8e2ec 100644 --- a/modules/pcjs/lib/debugger.js +++ b/modules/pcjs/lib/debugger.js @@ -319,7 +319,7 @@ if (DEBUGGER) { * Instruction names, indexed by instruction ordinal (above) */ Debugger.asIns = [ - "DB", "AAA", "AAD", "AAM", "AAS", "ADC", "ADD", "AND", + "INVALID","AAA", "AAD", "AAM", "AAS", "ADC", "ADD", "AND", "ARPL", "AS:", "BOUND", "BSF", "BSR", "BT", "BTC", "BTR", "BTS", "CALL", "CBW", "CLC", "CLD", "CLI", "CLTS", "CMC", "CMP", "CMPSB", "CMPSW", "CS:", "CWD", "DAA", "DAS", "DEC", @@ -1939,11 +1939,12 @@ if (DEBUGGER) { * * @this {Debugger} * @param {string} [s] + * @param {boolean} [fBlockFaults] */ - Debugger.prototype.stopCPU = function(s) + Debugger.prototype.stopCPU = function(s, fBlockFaults) { if (s) this.println(s); - this.cpu.stopCPU(); + this.cpu.stopCPU(!fBlockFaults); }; /** @@ -2213,7 +2214,15 @@ if (DEBUGGER) { * Assert that general-purpose register contents remain within their respective ranges; * this isn't intended to be complete, just a spot-check. */ - if (DEBUG) this.assert(!(this.cpu.regAX & ~0xffff) && !(this.cpu.regBX & ~0xffff) && !(this.cpu.regCX & ~0xffff) && !(this.cpu.regDX & ~0xffff), "register out of bounds"); + if (DEBUG) { + this.assert(!(this.cpu.regAX & ~0xffff) && !(this.cpu.regBX & ~0xffff) && !(this.cpu.regCX & ~0xffff) && !(this.cpu.regDX & ~0xffff), "register out of bounds"); + if (!fSkipBP && MAXDEBUG) { + if (!this.cpu.regIP) { + this.println("suspicious IP"); + return true; + } + } + } if (!fSkipBP && this.checkBreakpoint(addr, this.aBreakExec)) { return true; @@ -2760,6 +2769,7 @@ if (DEBUGGER) { aOpDesc = Debugger.aaGrpDescs[iIns - Debugger.asIns.length][(bModRM >> 3) & 0x7]; } + var sOpcode = Debugger.asIns[aOpDesc[0]]; var cOperands = 2; var sOperands = ""; if (bOpcode >= X86.OPCODE.MOVSB && bOpcode <= X86.OPCODE.CMPSW || bOpcode >= X86.OPCODE.STOSB && bOpcode <= X86.OPCODE.SCASW) { @@ -2819,8 +2829,9 @@ if (DEBUGGER) { else if (typeMode == Debugger.TYPE_ESDI) { sOperand = "ES:[DI]"; } - if (!sOperand.length) { - sOperand = "type(" + str.toHexWord(type) + ")"; + if (!sOperand || !sOperand.length) { + sOperands = "INVALID"; + break; } if (sOperands.length > 0) sOperands += ","; sOperands += sOperand; @@ -2832,7 +2843,7 @@ if (DEBUGGER) { sBytes += str.toHexByte(this.getByte(aAddrIns, 1)); } while (aAddrIns[0] != aAddr[0]); sLine += (sBytes + " ").substr(0, 14); - sLine += (Debugger.asIns[aOpDesc[0]] + " ").substr(0, 8); + sLine += (sOpcode + " ").substr(0, 8); if (sOperands) sLine += " " + sOperands; if (sComment) { @@ -2895,12 +2906,14 @@ if (DEBUGGER) { * @param {number} bReg * @param {number} type * @param {Array} aAddr - * @return {string} operand + * @return {string|null} operand */ Debugger.prototype.getRegOperand = function(bReg, type, aAddr) { - if ((type & Debugger.TYPE_MODE) == Debugger.TYPE_SEGREG) + if ((type & Debugger.TYPE_MODE) == Debugger.TYPE_SEGREG) { + if (bReg >= 4) return null; bReg += 16; + } else if ((type & Debugger.TYPE_SIZE) >= Debugger.TYPE_WORD) bReg += 8; return Debugger.asRegs[bReg]; diff --git a/modules/pcjs/lib/x86cpu.js b/modules/pcjs/lib/x86cpu.js index 987403441..4ff35b33f 100644 --- a/modules/pcjs/lib/x86cpu.js +++ b/modules/pcjs/lib/x86cpu.js @@ -1307,8 +1307,8 @@ X86CPU.prototype.setIP = function(off) * @this {X86CPU} * @param {number} off * @param {number} sel - * @param {boolean} [fCall] is true if "CALLF" in progress - * @return {boolean} true if "RETF" performed a stack switch + * @param {boolean} [fCall] is true if CALLF in progress, false if RETF in progress, null/undefined otherwise + * @return {boolean} true if a stack switch occurred; the only opcode that needs to care about this is opRETFn() */ X86CPU.prototype.setCSIP = function(off, sel, fCall) { @@ -1316,8 +1316,7 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall) this.segCS.fCall = fCall; /* * We break this operation into the following discrete steps (eg, set IP, load CS, and then update EIP) - * so that the protected-mode version of segCS.load(sel) has the option of modifying IP when sel refers to a - * call gate. + * so that segCS.load(sel) has the option of modifying IP when sel refers to a call gate. */ this.regIP = off; var base = this.segCS.load(sel); @@ -1325,7 +1324,7 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall) this.regEIP = base + this.regIP; } if (PREFETCH) this.flushPrefetch(this.regEIP); - return this.segCS.fReturn; + return this.segCS.fStackSwitch; }; /** diff --git a/modules/pcjs/lib/x86help.js b/modules/pcjs/lib/x86help.js index d8272ac4b..0f38a32f5 100644 --- a/modules/pcjs/lib/x86help.js +++ b/modules/pcjs/lib/x86help.js @@ -549,7 +549,7 @@ var X86Help = { case X86.DESC.ACC.TYPE.GATE_TASK: break; default: - if (DEBUG) this.assert(false); + if (DEBUG) this.assert(false, "INT 0x" + str.toHexByte(nIDT) + ": unrecognized IDT entry"); return false; } return true; @@ -672,6 +672,11 @@ var X86Help = { * @param {boolean} [fHalt] will halt the CPU if true *and* a Debugger is loaded */ opHelpFault: function(nFault, nError, fHalt) { + if (!this.aFlags.fComplete) { + // this.messageDebugger("Fault " + str.toHexByte(nFault) + " blocked by Debugger", Debugger.MESSAGE.WARN); + this.setIP(this.opEA - this.segCS.base); + return; + } var fFault = false; if (this.model >= X86.MODEL_80186) { if (this.nFault < 0) { diff --git a/modules/pcjs/lib/x86seg.js b/modules/pcjs/lib/x86seg.js index 039762306..5bc42cad8 100644 --- a/modules/pcjs/lib/x86seg.js +++ b/modules/pcjs/lib/x86seg.js @@ -60,7 +60,7 @@ function X86Seg(cpu, id, sName, fProt) this.cpl = 0; this.dpl = 0; this.awScratch = (this.id == X86Seg.ID.CODE? new Array(32) : []); - this.updateAccess(fProt || false); + this.updateAccess(fProt); } X86Seg.ID = { @@ -138,6 +138,9 @@ X86Seg.loadProt = function loadProt(sel, fSuppress) if (!fSuppress) this.cpu.nStepCycles -= 15; return this.loadDesc8(sel, addrDesc); } + if (!fSuppress) { + X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, sel); + } return null; }; @@ -176,7 +179,7 @@ X86Seg.checkWriteReal = function checkWriteReal(off, cb, fSuppress) }; /** - * checkReadProtEnabled(off, cb, fSuppress) + * checkReadProt(off, cb, fSuppress) * * @this {X86Seg} * @param {number} off is a segment-relative offset @@ -184,16 +187,16 @@ X86Seg.checkWriteReal = function checkWriteReal(off, cb, fSuppress) * @param {boolean} [fSuppress] is true to suppress any errors * @return {number|null} corresponding physical address if valid, null if not */ -X86Seg.checkReadProtEnabled = function checkReadProtEnabled(off, cb, fSuppress) +X86Seg.checkReadProt = function checkReadProt(off, cb, fSuppress) { if (off + cb <= this.limit) { return this.base + off; } - return X86Seg.checkReadProtDisabled.call(this, off, cb, fSuppress); + return X86Seg.checkReadProtDisallowed.call(this, off, cb, fSuppress); }; /** - * checkReadProtDisabled(off, cb, fSuppress) + * checkReadProtDown(off, cb, fSuppress) * * @this {X86Seg} * @param {number} off is a segment-relative offset @@ -201,7 +204,24 @@ X86Seg.checkReadProtEnabled = function checkReadProtEnabled(off, cb, fSuppress) * @param {boolean} [fSuppress] is true to suppress any errors * @return {number|null} corresponding physical address if valid, null if not */ -X86Seg.checkReadProtDisabled = function checkReadProtDisabled(off, cb, fSuppress) +X86Seg.checkReadProtDown = function checkReadProtDown(off, cb, fSuppress) +{ + if (off + cb > this.limit) { + return this.base + off; + } + return X86Seg.checkReadProtDisallowed.call(this, off, cb, fSuppress); +}; + +/** + * checkReadProtDisallowed(off, cb, fSuppress) + * + * @this {X86Seg} + * @param {number} off is a segment-relative offset + * @param {number} cb is number of extra bytes to check (0 or 1) + * @param {boolean} [fSuppress] is true to suppress any errors + * @return {number|null} corresponding physical address if valid, null if not + */ +X86Seg.checkReadProtDisallowed = function checkReadProtDisallowed(off, cb, fSuppress) { if (!fSuppress) { X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, 0); @@ -210,7 +230,7 @@ X86Seg.checkReadProtDisabled = function checkReadProtDisabled(off, cb, fSuppress }; /** - * checkWriteProtEnabled(off, cb, fSuppress) + * checkWriteProt(off, cb, fSuppress) * * @this {X86Seg} * @param {number} off is a segment-relative offset @@ -218,16 +238,16 @@ X86Seg.checkReadProtDisabled = function checkReadProtDisabled(off, cb, fSuppress * @param {boolean} [fSuppress] is true to suppress any errors * @return {number|null} corresponding physical address if valid, null if not */ -X86Seg.checkWriteProtEnabled = function checkWriteProtEnabled(off, cb, fSuppress) +X86Seg.checkWriteProt = function checkWriteProt(off, cb, fSuppress) { if (off + cb <= this.limit) { return this.base + off; } - return X86Seg.checkWriteProtDisabled.call(this, off, cb, fSuppress); + return X86Seg.checkWriteProtDisallowed.call(this, off, cb, fSuppress); }; /** - * checkWriteProtDisabled(off, cb, fSuppress) + * checkWriteProtDown(off, cb, fSuppress) * * @this {X86Seg} * @param {number} off is a segment-relative offset @@ -235,7 +255,24 @@ X86Seg.checkWriteProtEnabled = function checkWriteProtEnabled(off, cb, fSuppress * @param {boolean} [fSuppress] is true to suppress any errors * @return {number|null} corresponding physical address if valid, null if not */ -X86Seg.checkWriteProtDisabled = function checkWriteProtDisabled(off, cb, fSuppress) +X86Seg.checkWriteProtDown = function checkWriteProtDown(off, cb, fSuppress) +{ + if (off + cb > this.limit) { + return this.base + off; + } + return X86Seg.checkWriteProtDisallowed.call(this, off, cb, fSuppress); +}; + +/** + * checkWriteProtDisallowed(off, cb, fSuppress) + * + * @this {X86Seg} + * @param {number} off is a segment-relative offset + * @param {number} cb is number of extra bytes to check (0 or 1) + * @param {boolean} [fSuppress] is true to suppress any errors + * @return {number|null} corresponding physical address if valid, null if not + */ +X86Seg.checkWriteProtDisallowed = function checkWriteProtDisallowed(off, cb, fSuppress) { if (!fSuppress) { X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, 0); @@ -250,7 +287,7 @@ X86Seg.checkWriteProtDisabled = function checkWriteProtDisabled(off, cb, fSuppre /** * loadDesc6(sel, addrDesc) * - * Used to load a protected-mode selector that refers to a 6-byte descriptor "cache" (LOADALL) entry: + * Used to load a protected-mode selector that refers to a 6-byte "descriptor cache" (aka LOADALL) entry: * * word 0: base address low * word 1: base address high (0-7), segment type (8-11), descriptor type (12), DPL (13-14), present bit (15) @@ -282,7 +319,7 @@ X86Seg.prototype.loadDesc6 = function(sel, addrDesc) /** * loadDesc8(sel, addrDesc) * - * Used to load a protected-mode selector that refers to an 8-byte descriptor table (GDT, LDT, IDT) entry: + * Used to load a protected-mode selector that refers to an 8-byte "descriptor table" (GDT, LDT, IDT) entry: * * word 0: segment limit (0-15) * word 1: base address low @@ -303,93 +340,101 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc) var type = (acc & X86.DESC.ACC.TYPE.MASK); var base = this.cpu.getWord(addrDesc + X86.DESC.BASE.OFFSET) | ((acc & X86.DESC.ACC.BASE1623) << 16); var ext = (DEBUG? this.cpu.getWord(addrDesc + X86.DESC.EXT.OFFSET) : 0); + var selMasked = sel & X86.SEL.MASK; while (true) { - if (sel) { - /* - * TODO: These descriptor tests are far from complete.... - */ - if (this.id == X86Seg.ID.CODE) { - this.fReturn = false; - var rpl = sel & X86.SEL.RPL; - var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT; - var regSP; - if (type == X86.DESC.ACC.TYPE.GATE_CALL) { - /* - * Since we are X86Seg.ID.CODE, we can use this.cpl instead of the more generic this.cpu.segCS.cpl - */ - if (rpl < this.cpl) rpl = this.cpl; - if (rpl <= dpl) { - var cplPrev = this.cpl; - if (this.load(base & 0xffff, true) != null) { - this.cpu.regIP = limit; - if (this.cpl < cplPrev) { - if (this.fCall !== true) { - base = null; - break; - } - regSP = this.cpu.regSP; - var i = 0, nWords = (acc & 0x1f); - while (nWords--) { - this.awScratch[i++] = this.cpu.getSOWord(this.cpu.segSS, regSP); - regSP += 2; - } - var addrTSS = this.cpu.segTSS.base; - var offSP = (this.cpl << 2) + X86.TSS.CPL0_SP; - var offSS = offSP + 2; - var regSPPrev = this.cpu.regSP; - var regSSPrev = this.cpu.segSS.sel; - this.cpu.regSP = this.cpu.getWord(addrTSS + offSP); - this.cpu.segSS.load(this.cpu.getWord(addrTSS + offSS)); - this.cpu.pushWord(regSSPrev); - this.cpu.pushWord(regSPPrev); - while (i) this.cpu.pushWord(this.awScratch[--i]); + if (this.id == X86Seg.ID.CODE) { + this.fStackSwitch = false; + var fCall = this.fCall; + var rpl = sel & X86.SEL.RPL; + var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT; + var regSP; + if (type == X86.DESC.ACC.TYPE.GATE_CALL) { + /* + * Since we are X86Seg.ID.CODE, we can use this.cpl instead of the more generic this.cpu.segCS.cpl + */ + if (rpl < this.cpl) rpl = this.cpl; + if (rpl <= dpl) { + var cplPrev = this.cpl; + if (this.load(base & 0xffff, true) != null) { + this.cpu.regIP = limit; + if (this.cpl < cplPrev) { + if (fCall !== true) { + base = null; + break; } - return this.base; + regSP = this.cpu.regSP; + var i = 0, nWords = (acc & 0x1f); + while (nWords--) { + this.awScratch[i++] = this.cpu.getSOWord(this.cpu.segSS, regSP); + regSP += 2; + } + var addrTSS = this.cpu.segTSS.base; + var offSP = (this.cpl << 2) + X86.TSS.CPL0_SP; + var offSS = offSP + 2; + var regSPPrev = this.cpu.regSP; + var regSSPrev = this.cpu.segSS.sel; + this.cpu.regSP = this.cpu.getWord(addrTSS + offSP); + this.cpu.segSS.load(this.cpu.getWord(addrTSS + offSS)); + this.cpu.pushWord(regSSPrev); + this.cpu.pushWord(regSPPrev); + while (i) this.cpu.pushWord(this.awScratch[--i]); + this.fStackSwitch = true; } + return this.base; } } - else if (type >= X86.DESC.ACC.TYPE.CODE_EXECONLY /* || dpl > this.cpu.segCS.cpl */) { - rpl = sel & X86.SEL.RPL; - if (rpl > this.cpl) { - if (this.fCall !== false) { - base = null; - break; - } - regSP = this.cpu.popWord(); - this.cpu.segSS.load(this.cpu.popWord()); - this.cpu.regSP = regSP; - this.fReturn = true; - } - } - else { - X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, sel, true); - base = null; - break; - } } - else if (this.id == X86Seg.ID.DATA || this.id == X86Seg.ID.STACK) { + else if (type >= X86.DESC.ACC.TYPE.CODE_EXECONLY /* || dpl > this.cpu.segCS.cpl */) { + rpl = sel & X86.SEL.RPL; + if (rpl > this.cpl) { + if (fCall !== false) { + base = null; + break; + } + regSP = this.cpu.popWord(); + this.cpu.segSS.load(this.cpu.popWord()); + this.cpu.regSP = regSP; + this.fStackSwitch = true; + } + } + else { + X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, sel, true); + base = null; + break; + } + if (DEBUG) this.cpu.assert(!!selMasked); // a null CS selector should be caught by the final preceding check + } + else if (this.id == X86Seg.ID.DATA) { + if (selMasked) { if (type < X86.DESC.ACC.TYPE.DATA_READONLY || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.READABLE)) == X86.DESC.ACC.TYPE.CODE) { X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, sel, true); base = null; break; } } - else if (this.id == X86Seg.ID.TSS) { - if (type != X86.DESC.ACC.TYPE.TSS && type != X86.DESC.ACC.TYPE.TSS_BUSY) { - X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.TS_FAULT, sel, true); - base = null; - break; - } + } + else if (this.id == X86Seg.ID.STACK) { + if (!selMasked || type < X86.DESC.ACC.TYPE.DATA_READONLY || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.READABLE)) == X86.DESC.ACC.TYPE.CODE) { + X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, sel, true); + base = null; + break; } - else if (this.id == X86Seg.ID.OTHER) { - /* - * For LSL, we must support any descriptor marked X86.DESC.ACC.TYPE.SEG, as well as TSS and LDT descriptors. - */ - if (!(acc & X86.DESC.ACC.TYPE.SEG) && type > X86.DESC.ACC.TYPE.TSS_BUSY) { - base = null; - break; - } + } + else if (this.id == X86Seg.ID.TSS) { + if (!selMasked || type != X86.DESC.ACC.TYPE.TSS && type != X86.DESC.ACC.TYPE.TSS_BUSY) { + X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.TS_FAULT, sel, true); + base = null; + break; + } + } + else if (this.id == X86Seg.ID.OTHER) { + /* + * For LSL, we must support any descriptor marked X86.DESC.ACC.TYPE.SEG, as well as TSS and LDT descriptors. + */ + if (!(acc & X86.DESC.ACC.TYPE.SEG) && type > X86.DESC.ACC.TYPE.TSS_BUSY) { + base = null; + break; } } this.sel = sel; @@ -473,28 +518,32 @@ X86Seg.prototype.restore = function(a) */ X86Seg.prototype.updateAccess = function(fProt) { - if (fProt !== undefined) { - this.fCall = null; // true if "CALLF" in progress, false if "RETF [n]" in progress, null/undefined otherwise (X86Seg.ID.CODE only) - this.fReturn = false; // true if "RETF" performed, false otherwise - } else { + if (fProt === undefined) { fProt = !!(this.cpu.regMSW & X86.MSW.PE); } if (fProt) { this.load = X86Seg.loadProt; - this.checkRead = X86Seg.checkReadProtEnabled; - this.checkWrite = X86Seg.checkWriteProtEnabled; + this.checkRead = X86Seg.checkReadProt; + this.checkWrite = X86Seg.checkWriteProt; if (this.acc & X86.DESC.ACC.TYPE.SEG) { /* * If the READABLE bit of CODE_READABLE is not set, then disallow reads */ if ((this.acc & X86.DESC.ACC.TYPE.CODE_READABLE) == X86.DESC.ACC.TYPE.CODE_EXECONLY) { - this.checkWrite = X86Seg.checkReadProtDisabled; + this.checkWrite = X86Seg.checkReadProtDisallowed; } /* * If the CODE bit is set, or the the WRITABLE bit is not set, then disallow writes */ if ((this.acc & X86.DESC.ACC.TYPE.CODE) || !(this.acc & X86.DESC.ACC.TYPE.WRITABLE)) { - this.checkWrite = X86Seg.checkWriteProtDisabled; + this.checkWrite = X86Seg.checkWriteProtDisallowed; + } + /* + * If the CODE bit is not set *and* the EXPDOWN bit is set, then invert the limit check + */ + if ((this.acc & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.EXPDOWN)) == X86.DESC.ACC.TYPE.EXPDOWN) { + if (this.checkRead == X86Seg.checkReadProt) this.checkRead = X86Seg.checkReadProtDown; + if (this.checkWrite == X86Seg.checkWriteProt) this.checkWrite = X86Seg.checkWriteProtDown; } } this.cpl = this.sel & X86.SEL.RPL; @@ -506,6 +555,8 @@ X86Seg.prototype.updateAccess = function(fProt) this.cpl = this.dpl = 0; this.addrDesc = null; } + this.fCall = null; // true if CALLF in progress, false if RETF in progress, null/undefined otherwise (X86Seg.ID.CODE only) + this.fStackSwitch = false; // true if a stack switch occurred on the last loadDesc8(), false otherwise (X86Seg.ID.CODE only) return fProt; }; diff --git a/modules/shared/lib/component.js b/modules/shared/lib/component.js index 97742e5d3..45b980781 100644 --- a/modules/shared/lib/component.js +++ b/modules/shared/lib/component.js @@ -715,9 +715,9 @@ Component.prototype = { /* * TODO: An accompanying source file/line number/function call (eg, stack trace) would be nice. */ - if (!s) s = "assertion failure in " + (this.id || this.type); + s = "assertion failure in " + (this.id || this.type) + (s? ": " + s : ""); if (DEBUGGER && this.dbg) { - this.dbg.stopCPU(s); + this.dbg.stopCPU(s, true); /* * Why do we throw an Error only to immediately catch it and ignore it? Simply to give our IDE * the opportunity to stop and smell the roses. If the user has no desire to stop on assertions,