Some cleanup involving invalid addresses
This commit is contained in:
parent
019ce280f6
commit
c85398db6b
7 changed files with 124 additions and 82 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue