More protected-mode improvements
This commit is contained in:
parent
173ef0a3fe
commit
9523de773d
7 changed files with 197 additions and 115 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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....
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue