Some cleanup involving invalid addresses

This commit is contained in:
Jeff Parsons 2014-12-01 13:31:23 -08:00 committed by jeffpar
commit c85398db6b
7 changed files with 124 additions and 82 deletions

View file

@ -585,7 +585,7 @@ if (DEBUGGER) {
* Opcode 0x0F has a distinguished history:
*
* On the 8086, it functioned as POP CS
* On the 80186, it generated an illegal opcode (UD_FAULT) exception
* On the 80186, it generated an Invalid Opcode (UD_FAULT) exception
* On the 80286, it introduced a new (and growing) series of two-byte opcodes
*
* Based on the active CPU model, we make every effort to execute and disassemble this (and every other)

View file

@ -165,7 +165,7 @@ Memory.prototype = {
* @return {number}
*/
readNone: function(off) {
if (DEBUGGER && this.dbg.messageEnabled(Debugger.MESSAGE.MEM) && !off) {
if (DEBUGGER && this.dbg.messageEnabled(Debugger.MESSAGE.MEM) /* && !off */) {
this.dbg.message("attempt to read invalid block %" + str.toHex(this.addr) + " from " + str.toHexAddr(this.cpu.regIP, this.cpu.segCS.sel));
}
return 0;
@ -178,7 +178,7 @@ Memory.prototype = {
* @param {number} v (could be either a byte or word value, since we use the same handler for both kinds of accesses)
*/
writeNone: function(off, v) {
if (DEBUGGER && this.dbg.messageEnabled(Debugger.MESSAGE.MEM) && !off) {
if (DEBUGGER && this.dbg.messageEnabled(Debugger.MESSAGE.MEM) /* && !off */) {
this.dbg.message("attempt to write 0x" + str.toHexWord(v) + " to invalid block %" + str.toHex(this.addr), true);
}
},

View file

@ -41,6 +41,20 @@ var X86 = {
MODEL_80186: 80186,
MODEL_80188: 80188,
MODEL_80286: 80286,
/*
* This constant is used to mark points in the code where the physical address being returned
* is invalid and should not be used. TODO: There are still functions that will use an invalid
* address, which is why we've tried to choose a value that will cause the least harm, but ultimately,
* we must add checks to those functions or throw a special JavaScript exception to bypass them.
*
* This value is also used to indicate non-existent EA address calculations, which are usually
* detected with "regEA < 0" and "regEAWrite < 0" tests, so be careful if you change this value.
* If/when we ever extend our physical address space beyond 24 bits (ie, when we break the 2Gb barrier),
* negative 32-bit values values will become valid addresses, so those tests will have to be revised.
*/
ADDR_INVALID: -4,
/*
* Processor Status flag definitions (stored in regPS)
*/
@ -127,7 +141,8 @@ var X86 = {
MASK: 0x6000,
SHIFT: 13
},
PRESENT: 0x8000
PRESENT: 0x8000,
INVALID: 0 // use X86.DESC.ACC.INVALID for invalid ACC values
},
EXT: { // descriptor extension word (reserved on the 80286; "must be zero")
OFFSET: 0x6,
@ -136,7 +151,8 @@ var X86 = {
DEFSIZE: 0x0040, // clear if default operand/address size is 16-bit, set if 32-bit
GRANULARITY: 0x0080, // clear if limit is bytes, set if limit is 4Kb pages
BASE2431: 0xff00
}
},
INVALID: 0 // use X86.DESC.INVALID for invalid DESC values
},
TSS: {
PREV_TSS: 0x00,
@ -177,12 +193,14 @@ var X86 = {
*
* Interrupts beyond 0x10 (up through 0x1F) are reserved for future exceptions.
*
* Implementation Detail: For any opcode we know must generate a UD_FAULT interrupt, we invoke opInvalid().
* We reserve the term "undefined" for opcodes that require further investigation, and we invoke opUndefined()
* in those cases until an opcode's behavior has been defined; at that point, it's either valid or invalid.
* Implementation Detail: For any opcode we know must generate a UD_FAULT interrupt, we invoke opInvalid(),
* NOT opUndefined(). UD_FAULT is for INVALID opcodes, Intel's choice of "UD" notwithstanding.
*
* As for "illegal", that's a silly (and redundant) term in this context, so we don't use it. Similarly,
* the term "undocumented" should be limited to operations that are valid but that Intel did not document.
* We reserve the term "undefined" for opcodes that require further investigation, and we invoke opUndefined()
* ONLY until an opcode's behavior has finally been defined, at which point it becomes either valid or invalid.
* The term "illegal" seems completely superfluous; we don't need a third way of describing invalid opcodes.
*
* The term "undocumented" should be limited to operations that are valid but Intel simply never documented.
*/
EXCEPTION: {
DIV_ERR: 0x00, // Divide Error Interrupt
@ -214,7 +232,7 @@ var X86 = {
AUXOVF_OF: 0x08080,
AUXOVF_CF: 0x10100
},
PARITY: [ // 256-byte array with a 1 wherever the number of set bits of the array index is EVEN
PARITY: [ // 256-byte array with a 1 wherever the number of set bits in the array index is EVEN
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,

View file

@ -901,7 +901,7 @@ X86CPU.prototype.resetRegs = function()
* The next few initializations mirror what we must do prior to each instruction (ie, inside the stepCPU() function);
* note that opPrefixes, along with segData and segStack, are reset only after we've executed a non-prefix instruction.
*/
this.regEA = this.regEAWrite = -1;
this.regEA = this.regEAWrite = X86.ADDR_INVALID;
this.segData = this.segDS;
this.segStack = this.segSS;
this.opFlags = this.opPrefixes = 0;
@ -1295,8 +1295,8 @@ X86CPU.prototype.setIP = function(off)
* never set without an accompanying IP (well, except for a few undocumented instructions, like POP CS, which
* were available ONLY on the 8086/8088/80186/80188; see setCS() for details).
*
* NOTE: Unlike setIP(), which is often passed a computation, the offsets passed to setCSIP() are strictly
* 16-bit values, so there's never any need to mask them with 0xffff (although it doesn't hurt to assert that).
* NOTE: Unlike setIP(), which is often passed a computation, the offsets passed to setCSIP() are assumed to
* be 16-bit values, so there's no need to mask them with 0xffff (although it doesn't hurt to assert that).
*
* And even though this function is called setCSIP(), please note the order of the parameters is IP,CS,
* which matches the order that CS:IP values are normally stored in memory, allowing us to make calls like this:
@ -1306,8 +1306,8 @@ X86CPU.prototype.setIP = function(off)
* @this {X86CPU}
* @param {number} off
* @param {number} sel
* @param {boolean} [fCall] is true if CALLF in progress, false if RETF in progress, null/undefined otherwise
* @return {boolean|null} true if a stack switch occurred; the only opcode that really needs to care is opRETFn()
* @param {boolean} [fCall] is true if CALLF in progress, false if RETF/IRET in progress, null/undefined otherwise
* @return {boolean|null} true if a stack switch occurred; the only opcode that really needs to pay attention is opRETFn()
*/
X86CPU.prototype.setCSIP = function(off, sel, fCall)
{
@ -1319,12 +1319,12 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall)
*/
this.regIP = off;
var base = this.segCS.load(sel);
if (base == null) {
return null;
if (base != X86.ADDR_INVALID) {
this.regEIP = base + this.regIP;
if (PREFETCH) this.flushPrefetch(this.regEIP);
return this.segCS.fStackSwitch;
}
this.regEIP = base + this.regIP;
if (PREFETCH) this.flushPrefetch(this.regEIP);
return this.segCS.fStackSwitch;
return null;
};
/**
@ -2510,7 +2510,7 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
this.opPrefixes |= opPrefixes;
} else {
this.opEA = this.regEIP;
this.regEA = this.regEAWrite = -1;
this.regEA = this.regEAWrite = X86.ADDR_INVALID;
this.segData = this.segDS;
this.segStack = this.segSS;
this.opPrefixes = this.opFlags & X86.OPFLAG.REPEAT;

View file

@ -277,7 +277,7 @@ var X86Help = {
* TODO: This instruction's 80286 documentation does not discuss conforming code segments; determine
* if we need a special check for them.
*/
if (this.segVER.load(src, true) != null) {
if (this.segVER.load(src, true) != X86.ADDR_INVALID) {
if (this.segVER.dpl >= this.segCS.cpl && this.segVER.dpl >= (src & X86.SEL.RPL)) {
this.setZF();
return this.segVER.acc & X86.DESC.ACC.MASK;
@ -304,7 +304,7 @@ var X86Help = {
* TODO: LSL is explicitly documented as ALSO requiring a non-null selector, so we check X86.SEL.MASK;
* are there any other instructions that were, um, less explicit but also require a non-null selector?
*/
if ((src & X86.SEL.MASK) && this.segVER.load(src, true) != null) {
if ((src & X86.SEL.MASK) && this.segVER.load(src, true) != X86.ADDR_INVALID) {
var fConforming = ((this.segVER.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) == X86.DESC.ACC.TYPE.CODE_CONFORMING);
if ((fConforming || this.segVER.dpl >= this.segCS.cpl) && this.segVER.dpl >= (src & X86.SEL.RPL)) {
this.setZF();
@ -456,6 +456,12 @@ var X86Help = {
/**
* opHelpINT(nIDT, nError, nCycles)
*
* NOTE: We no longer use setCSIP(), because it always loads the new CS using the segCS.load() method,
* which only knows how to load GDT and LDT selectors, whereas interrupt instructions must use setCS.loadIDT().
*
* This means we must take care to replicate critical features of setCSIP(); eg, setting segCS.fCall before
* calling loadIDT(), updating EIP, and flushing the prefetch queue.
*
* @this {X86CPU}
* @param {number} nIDT
* @param {number|null|undefined} nError
@ -471,8 +477,9 @@ var X86Help = {
var regCS = this.segCS.sel;
var regIP = this.regIP;
var base = this.segCS.loadIDT(nIDT);
if (base != null) {
if (base != X86.ADDR_INVALID) {
this.regEIP = base + this.regIP;
if (PREFETCH) this.flushPrefetch(this.regEIP);
this.pushWord(regPS);
this.pushWord(regCS);
this.pushWord(regIP);
@ -572,13 +579,14 @@ var X86Help = {
/*
* Since this fault is likely being issued in the context of an instruction that hasn't finished
* executing, and since we currently don't do anything to interrupt that execution (eg, throw a
* JavaScript exception), and since we don't want that instruction to perform any writes that might
* be destructive, we should shut off all further reads/writes for the current instruction.
* JavaScript exception), we should shut off all further reads/writes for the current instruction.
*
* As long as we're not using EAFUNCS, that's easy for any EA-based memory accesses: simply set both
* the NOREAD and NOWRITE flags. However, there may still be direct, non-EA-based memory accesses that
* could cause us grief. TODO: Implement a better solution, which may involve throwing a special
* JavaScript exception that cpu.js must intercept and quietly ignore.
* That's easy for any EA-based memory accesses (provided we're not using EAFUNCS): simply set both
* the NOREAD and NOWRITE flags. However, there are also direct, non-EA-based memory accesses to
* consider. A perfect example is opPUSHA(): if a GP fault occurs on any PUSH other than the last,
* a subsequent PUSH is likely to cause another fault, which we will misinterpret as a double-fault.
*
* TODO: Throw a special JavaScript exception that cpu.js must intercept and quietly ignore.
*/
if (!EAFUNCS) {
this.opFlags &= ~(X86.OPFLAG.NOREAD | X86.OPFLAG.NOWRITE);

View file

@ -127,7 +127,7 @@ var X86Op0F = {
opLOADALL: function() {
if (this.segCS.cpl) {
/*
* You're not allowed to use LOADALL at any privilege level other than zero
* You're not allowed to use LOADALL if the current privilege level is something other than zero
*/
X86Help.opHelpFault.call(this, X86.EXCEPTION.GP_FAULT, 0, true);
return;
@ -215,7 +215,7 @@ var X86Op0F = {
*/
opLTR: function(dst, src) {
if (EAFUNCS) this.setEAWord = this.setEAWordDisabled; else this.opFlags |= X86.OPFLAG.NOWRITE;
if (this.segTSS.load(dst) != null) {
if (this.segTSS.load(dst) != X86.ADDR_INVALID) {
this.setWord(this.segTSS.addrDesc + X86.DESC.ACC.OFFSET, this.segTSS.acc |= X86.DESC.ACC.TYPE.LDT);
this.segTSS.type = X86.DESC.ACC.TYPE.TSS_BUSY;
}
@ -235,7 +235,7 @@ var X86Op0F = {
* descriptor table or the descriptor is not for a segment.
*/
this.nStepCycles -= (14 + (this.regEA < 0? 0 : 2));
if (this.segVER.load(dst, true) >= 0) {
if (this.segVER.load(dst, true) != X86.ADDR_INVALID) {
/*
* Verify that this is a readable segment; that is, of these four combinations (code+readable,
* code+nonreadable, data+writable, date+nonwritable), make sure we're not the second combination.
@ -271,7 +271,7 @@ var X86Op0F = {
* descriptor table or the descriptor is not for a segment.
*/
this.nStepCycles -= (14 + (this.regEA < 0? 0 : 2));
if (this.segVER.load(dst, true) >= 0) {
if (this.segVER.load(dst, true) != X86.ADDR_INVALID) {
/*
* Verify that this is a writable data segment
*/

View file

@ -57,7 +57,7 @@ function X86Seg(cpu, id, sName, fProt)
this.base = 0;
this.limit = 0xffff;
this.acc = 0;
this.addrDesc = null;
this.addrDesc = X86.ADDR_INVALID;
this.cpl = 0;
this.dpl = 0;
/*
@ -76,11 +76,12 @@ function X86Seg(cpu, id, sName, fProt)
* to a numerically lower privilege, and fCall == false allows a stack switch (restore) and a privilege
* transition to a numerically greater privilege.
*
* As long as setCSIP() is used for all CS changes, the foregoing is automatically taken care of.
* As long as setCSIP() or opHelpINT() are used for all CS changes, the foregoing is automatically
* taken care of.
*
* TODO: Consider making fCall a parameter to load(), instead of a property that must be set prior to
* calling load(); the downside (and why I didn't do that in the first place) is that such a parameter
* to load() would be meaningless for segments other than segCS.
* is meaningless for segments other than segCS.
*/
this.awScratch = (this.id == X86Seg.ID.CODE? new Array(32) : []);
this.fCall = null;
@ -111,7 +112,7 @@ X86Seg.ID = {
* @this {X86Seg}
* @param {number} sel
* @param {boolean} [fSuppress] is true to suppress any errors
* @return {number|null} base address of selected segment, or null if error
* @return {number} base address of selected segment, or ADDR_INVALID if error (TODO: No error conditions exist yet)
*/
X86Seg.loadReal = function loadReal(sel, fSuppress)
{
@ -140,7 +141,7 @@ X86Seg.loadReal = function loadReal(sel, fSuppress)
* @this {X86Seg}
* @param {number} sel
* @param {boolean} [fSuppress] is true to suppress any errors, cycle assessment, etc
* @return {number|null} base address of selected segment, or null if error
* @return {number} base address of selected segment, or ADDR_INVALID if error
*/
X86Seg.loadProt = function loadProt(sel, fSuppress)
{
@ -180,7 +181,7 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
}
}
return null;
return X86.ADDR_INVALID;
};
/**
@ -188,7 +189,7 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
*
* @this {X86Seg}
* @param {number} nIDT
* @return {number|null} base address of selected segment, or null if error
* @return {number} base address of selected segment, or ADDR_INVALID if error (TODO: No error conditions exist yet)
*/
X86Seg.loadRealIDT = function loadRealIDT(nIDT)
{
@ -213,7 +214,7 @@ X86Seg.loadRealIDT = function loadRealIDT(nIDT)
*
* @this {X86Seg}
* @param {number} nIDT
* @return {number|null} base address of selected segment, or null if error
* @return {number} base address of selected segment, or ADDR_INVALID if error (TODO: No error conditions exist yet)
*/
X86Seg.loadProtIDT = function loadProtIDT(nIDT)
{
@ -226,7 +227,7 @@ X86Seg.loadProtIDT = function loadProtIDT(nIDT)
return this.loadDesc8(addrDesc, nIDT);
}
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
return null;
return X86.ADDR_INVALID;
};
/**
@ -239,7 +240,7 @@ X86Seg.loadProtIDT = function loadProtIDT(nIDT)
* @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
* @return {number} corresponding physical address if valid, or ADDR_INVALID if error (TODO: No error conditions exist yet)
*/
X86Seg.checkReadReal = function checkReadReal(off, cb, fSuppress)
{
@ -256,7 +257,7 @@ X86Seg.checkReadReal = function checkReadReal(off, cb, fSuppress)
* @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
* @return {number} corresponding physical address if valid, or ADDR_INVALID if error (TODO: No error conditions exist yet)
*/
X86Seg.checkWriteReal = function checkWriteReal(off, cb, fSuppress)
{
@ -270,7 +271,7 @@ X86Seg.checkWriteReal = function checkWriteReal(off, cb, fSuppress)
* @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
* @return {number} corresponding physical address if valid, or ADDR_INVALID if not
*/
X86Seg.checkReadProt = function checkReadProt(off, cb, fSuppress)
{
@ -287,7 +288,7 @@ X86Seg.checkReadProt = function checkReadProt(off, cb, fSuppress)
* @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
* @return {number} corresponding physical address if valid, ADDR_INVALID if not
*/
X86Seg.checkReadProtDown = function checkReadProtDown(off, cb, fSuppress)
{
@ -304,14 +305,14 @@ X86Seg.checkReadProtDown = function checkReadProtDown(off, cb, fSuppress)
* @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
* @return {number} corresponding physical address if valid, ADDR_INVALID if not
*/
X86Seg.checkReadProtDisallowed = function checkReadProtDisallowed(off, cb, fSuppress)
{
if (!fSuppress) {
X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, 0);
}
return null;
return X86.ADDR_INVALID;
};
/**
@ -321,7 +322,7 @@ X86Seg.checkReadProtDisallowed = function checkReadProtDisallowed(off, cb, fSupp
* @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
* @return {number} corresponding physical address if valid, ADDR_INVALID if not
*/
X86Seg.checkWriteProt = function checkWriteProt(off, cb, fSuppress)
{
@ -338,7 +339,7 @@ X86Seg.checkWriteProt = function checkWriteProt(off, cb, fSuppress)
* @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
* @return {number} corresponding physical address if valid, ADDR_INVALID if not
*/
X86Seg.checkWriteProtDown = function checkWriteProtDown(off, cb, fSuppress)
{
@ -355,14 +356,14 @@ X86Seg.checkWriteProtDown = function checkWriteProtDown(off, cb, fSuppress)
* @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
* @return {number} corresponding physical address if valid, ADDR_INVALID if not
*/
X86Seg.checkWriteProtDisallowed = function checkWriteProtDisallowed(off, cb, fSuppress)
{
if (!fSuppress) {
X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, 0);
}
return null;
return X86.ADDR_INVALID;
};
/**
@ -370,6 +371,20 @@ X86Seg.checkWriteProtDisallowed = function checkWriteProtDisallowed(off, cb, fSu
*
* Implements TSS (Task State Segment) task switching.
*
* NOTES: This typically occurs during double-fault processing, because the IDT entry for DF_FAULT normally
* contains a task gate. Interestingly, if we force a GP_FAULT to occur at a sufficiently early point in the
* OS/2 1.0 initialization code, OS/2 does a nice job of displaying the GP fault and then shutting down:
*
* 0090:067B FB STI
* 0090:067C EBFD JMP 067B
*
* but it may not have yet reprogrammed the master PIC to re-vector hardware interrupts to IDT entries 0x50-0x57,
* so when the next timer interrupt (IRQ 0) occurs, it vectors through IDT entry 0x08, which is the double-fault
* vector. A spurious double-fault is generated, and a clean shutdown turns into a messy crash.
*
* Of course, that all could have been avoided if IBM had heeded Intel's advice and not used Intel-reserved IDT
* entries for PC interrupts.
*
* @this {X86Seg}
* @param {number} selNew
* @param {boolean} fNest is true if nesting, false if un-nesting
@ -390,7 +405,7 @@ X86Seg.switchTSS = function switchTSS(selNew, fNest)
}
cpu.setWord(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, (cpu.segTSS.acc & ~X86.DESC.ACC.TYPE.TSS_BUSY) | X86.DESC.ACC.TYPE.TSS);
}
if (cpu.segTSS.load(selNew) == null) {
if (cpu.segTSS.load(selNew) == X86.ADDR_INVALID) {
return false;
}
var addrNew = cpu.segTSS.base;
@ -455,7 +470,7 @@ X86Seg.switchTSS = function switchTSS(selNew, fNest)
* @this {X86Seg}
* @param {number} sel (protected-mode only)
* @param {boolean} [fGDT] is true if sel must be in the GDT
* @return {number|null} acc field from descriptor, or null if error
* @return {number} acc field from descriptor, or X86.DESC.ACC.INVALID if error
*/
X86Seg.prototype.loadAcc = function(sel, fGDT)
{
@ -477,7 +492,7 @@ X86Seg.prototype.loadAcc = function(sel, fGDT)
}
}
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
return null;
return X86.DESC.ACC.INVALID;
};
/**
@ -508,7 +523,7 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
this.addrDesc = addrDesc;
this.updateMode();
this.messageDebugger(sel, base, limit, acc);
this.messageSeg(sel, base, limit, acc);
return base;
};
@ -529,7 +544,7 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
* @param {number} addrDesc is the descriptor address
* @param {number} sel is the associated selector
* @param {boolean} [fSuppress] is true to suppress any errors, cycle assessment, etc
* @return {number|null} base address of selected segment, or null if error
* @return {number} base address of selected segment, or ADDR_INVALID if error
*/
X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
{
@ -555,7 +570,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
rpl = sel & X86.SEL.RPL;
if (rpl > this.cpl) {
if (fCall !== false) {
base = null;
base = X86.ADDR_INVALID;
break;
}
regSP = cpu.popWord();
@ -572,22 +587,22 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
if (rpl < this.cpl) rpl = this.cpl;
if (rpl > dpl) {
accCode = this.loadAcc(selCode, true);
if (accCode != null && (accCode & X86.DESC.ACC.TYPE.CODE_CONFORMING) == X86.DESC.ACC.TYPE.CODE_CONFORMING) {
if (accCode != X86.DESC.ACC.INVALID && (accCode & X86.DESC.ACC.TYPE.CODE_CONFORMING) == X86.DESC.ACC.TYPE.CODE_CONFORMING) {
rpl = dpl;
}
}
if (rpl <= dpl) {
cplPrev = this.cpl;
if (this.load(selCode, true) == null) {
if (this.load(selCode, true) == X86.ADDR_INVALID) {
cpu.assert(false);
base = null;
base = X86.ADDR_INVALID;
break;
}
cpu.regIP = limit;
if (this.cpl < cplPrev) {
if (fCall !== true) {
cpu.assert(false);
base = null;
base = X86.ADDR_INVALID;
break;
}
regSP = cpu.regSP;
@ -612,28 +627,28 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
}
cpu.assert(false);
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
base = null;
base = X86.ADDR_INVALID;
break;
}
else if (type == X86.DESC.ACC.TYPE.GATE_INT || type == X86.DESC.ACC.TYPE.GATE_TRAP) {
selCode = base & 0xffff;
if (dpl > this.cpl) {
accCode = this.loadAcc(selCode, true);
if (accCode != null && (accCode & X86.DESC.ACC.TYPE.CODE_CONFORMING) == X86.DESC.ACC.TYPE.CODE_CONFORMING) {
if (accCode != X86.DESC.ACC.INVALID && (accCode & X86.DESC.ACC.TYPE.CODE_CONFORMING) == X86.DESC.ACC.TYPE.CODE_CONFORMING) {
dpl = this.cpl;
}
}
if (dpl <= this.cpl) {
cplPrev = this.cpl;
if (this.load(selCode, true) == null) {
if (this.load(selCode, true) == X86.ADDR_INVALID) {
cpu.assert(false);
base = null;
base = X86.ADDR_INVALID;
break;
}
cpu.regIP = limit;
if (this.cpl < cplPrev) {
if (fCall !== true) {
base = null;
base = X86.ADDR_INVALID;
break;
}
regSP = cpu.regSP;
@ -657,22 +672,22 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
}
cpu.assert(false);
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel | X86.ERRCODE.EXT, true);
base = null;
base = X86.ADDR_INVALID;
break;
}
else if (type == X86.DESC.ACC.TYPE.GATE_TASK) {
if (!X86Seg.switchTSS.call(this, base & 0xffff, true)) {
base = null;
base = X86.ADDR_INVALID;
break;
}
return this.base;
}
else {
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
base = null;
base = X86.ADDR_INVALID;
break;
}
cpu.assert(!!selMasked); // a null CS selector should be caught by the final preceding check
cpu.assert(!!selMasked); // a zero CS selector should be caught by the final preceding check
}
else if (this.id == X86Seg.ID.DATA) {
if (selMasked) {
@ -700,7 +715,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
* So, if acc is zero, we won't set fHalt on the following call.
*/
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, acc != 0);
base = null;
base = X86.ADDR_INVALID;
break;
}
}
@ -708,14 +723,14 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
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) {
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
base = null;
base = X86.ADDR_INVALID;
break;
}
}
else if (this.id == X86Seg.ID.TSS) {
if (!selMasked || type != X86.DESC.ACC.TYPE.TSS && type != X86.DESC.ACC.TYPE.TSS_BUSY) {
if (!fSuppress) X86Help.opHelpFault.call(cpu, X86.EXCEPTION.TS_FAULT, sel, true);
base = null;
base = X86.ADDR_INVALID;
break;
}
}
@ -724,7 +739,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
* 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;
base = X86.ADDR_INVALID;
break;
}
}
@ -737,7 +752,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
this.updateMode();
break;
}
if (!fSuppress) this.messageDebugger(sel, base, limit, acc, ext);
if (!fSuppress) this.messageSeg(sel, base, limit, acc, ext);
return base;
};
@ -847,21 +862,22 @@ X86Seg.prototype.updateMode = function(fProt)
this.checkWrite = X86Seg.checkWriteReal;
this.limit = 0xffff;
this.cpl = this.dpl = 0;
this.addrDesc = null;
this.addrDesc = X86.ADDR_INVALID;
}
return fProt;
};
/**
* messageDebugger(sel base, limit, acc, ext)
* messageSeg(sel, base, limit, acc, ext)
*
* @this {X86Seg}
* @param {number} sel
* @param {number|null} base
* @param {number} base
* @param {number} limit
* @param {number} acc
* @param {number} [ext]
*/
X86Seg.prototype.messageDebugger = function(sel, base, limit, acc, ext)
X86Seg.prototype.messageSeg = function(sel, base, limit, acc, ext)
{
if (DEBUG) {
if (DEBUGGER && this.dbg && this.dbg.messageEnabled(Debugger.MESSAGE.SEG)) {
@ -870,7 +886,7 @@ X86Seg.prototype.messageDebugger = function(sel, base, limit, acc, ext)
if (this.id == X86Seg.ID.CODE) sDPL += " cpl=" + this.cpl;
this.dbg.message("loadSeg(" + this.sName + "):" + ch + "sel=" + str.toHexWord(sel) + " base=" + str.toHex(base) + " limit=" + str.toHexWord(limit) + " acc=" + str.toHexWord(acc) + sDPL);
}
this.cpu.assert(/* base != null && */ (!ext || ext == X86.DESC.EXT.AVAIL));
this.cpu.assert(/* base != X86.ADDR_INVALID && */ (!ext || ext == X86.DESC.EXT.AVAIL));
}
};