Added support for "call breaks", and fixed BTC, BTR and BTS (hopefully correctly this time),
This commit is contained in:
parent
3f52d18182
commit
8d83504d9a
5 changed files with 215 additions and 62 deletions
|
|
@ -1271,7 +1271,7 @@ if (DEBUGGER) {
|
|||
this.messageDump(Messages.TSS, function onDumpTSS(asArgs) { dbg.dumpTSS(asArgs); });
|
||||
this.messageDump(Messages.DOS, function onDumpDOS(asArgs) { dbg.dumpDOS(asArgs); });
|
||||
|
||||
if (Interrupts.WINDBG.ENABLED) {
|
||||
if (Interrupts.WINDBG.ENABLED || Interrupts.WINDBGRM.ENABLED) {
|
||||
this.fWinDbg = null;
|
||||
this.cpu.addIntNotify(Interrupts.WINDBG.VECTOR, this.intWindowsDebugger.bind(this));
|
||||
}
|
||||
|
|
@ -1283,12 +1283,21 @@ if (DEBUGGER) {
|
|||
this.setReady();
|
||||
};
|
||||
|
||||
if (Interrupts.WINDBG.ENABLED) {
|
||||
if (Interrupts.WINDBG.ENABLED || Interrupts.WINDBGRM.ENABLED) {
|
||||
/**
|
||||
* intWindowsDebugger()
|
||||
*
|
||||
* This intercepts calls to the Windows Debugger protected-mode interface (INT 0x41).
|
||||
*
|
||||
* It's enabled if Interrupts.WINDBG.ENABLED is true, but it must ALSO be enabled if
|
||||
* Interrupts.WINDBGRM.ENABLED is true, because if the latter decides to respond to
|
||||
* requests, then we must start responding, too, because Windows assumes that the former
|
||||
* is installed whenever it detects the latter.
|
||||
*
|
||||
* Which is also why intWindowsDebuggerRM() will also set this.fWinDbg to true: we MUST
|
||||
* return false for all INT 0x41 requests, so that all requests are consumed, since there's
|
||||
* no guarantee that a valid interrupt handler exists inside the machine.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} addr
|
||||
* @return {boolean} true to proceed with the INT 0x41 software interrupt, false to skip
|
||||
|
|
@ -1328,10 +1337,15 @@ if (DEBUGGER) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: If this.fWinDbg is true, then all cases should return false, because we're taking full
|
||||
* responsibility for all requests (don't assume there's valid interrupt handler inside the machine).
|
||||
*/
|
||||
switch(AX) {
|
||||
case Interrupts.WINDBG.IS_LOADED:
|
||||
if (this.fWinDbg) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBG.LOADED;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -1341,14 +1355,16 @@ if (DEBUGGER) {
|
|||
*/
|
||||
sModule = this.getSZ(this.newAddr(DI, ES));
|
||||
limit = (seg = this.getSegment(CX))? seg.limit : 0;
|
||||
if (!this.fWinDbg) {
|
||||
if (this.fWinDbg) {
|
||||
this.println(sModule + "!undefined " + ((SI & 0x1)? "data" : "code") + '(' + str.toHex(BX+1, 4) + ")=#" + str.toHex(CX, 4) + " len " + str.toHex(limit+1));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (this.fWinDbg) {
|
||||
this.println("INT 0x41: " + str.toHexWord(AX));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1387,7 +1403,11 @@ if (DEBUGGER) {
|
|||
if ((cpu.regEAX & 0xffff) != Interrupts.WINDBGRM.LOADED) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBGRM.LOADED;
|
||||
dbg.println("INT 0x68 handling enabled");
|
||||
dbg.fWinDbgRM = true;
|
||||
/*
|
||||
* If we turn on INT 0x68 handling, we must also turn on INT 0x41 handling,
|
||||
* because Windows assumes that the latter handler exists whenever the former does.
|
||||
*/
|
||||
dbg.fWinDbg = dbg.fWinDbgRM = true;
|
||||
} else {
|
||||
dbg.println("INT 0x68 monitoring enabled");
|
||||
dbg.fWinDbgRM = false;
|
||||
|
|
@ -1402,9 +1422,20 @@ if (DEBUGGER) {
|
|||
case Interrupts.WINDBGRM.IS_LOADED:
|
||||
if (this.fWinDbgRM) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xffff) | Interrupts.WINDBGRM.LOADED;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Interrupts.WINDBGRM.PREP_PMODE:
|
||||
if (this.fWinDbgRM) {
|
||||
var a = cpu.segCS.addCallBreak(this.callWindowsDebuggerPMInit.bind(this));
|
||||
if (a) {
|
||||
cpu.regEDI = a[0];
|
||||
cpu.setES(a[1]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
case Interrupts.WINDBGRM.LOAD_SEG:
|
||||
if (AL == 0x20) {
|
||||
/*
|
||||
|
|
@ -1450,22 +1481,80 @@ if (DEBUGGER) {
|
|||
var dbgAddrDevice = this.newAddr(this.getLong(dbgAddr, 4), this.getShort(dbgAddr, 2));
|
||||
var dbgAddrModule = this.newAddr(this.getLong(dbgAddr, 4), this.getShort(dbgAddr, 2));
|
||||
var selAlias = this.getShort(dbgAddr, 2) || sel;
|
||||
if (!this.fWinDbgRM) {
|
||||
this.log(this.getSZ(dbgAddrModule) + '!' + this.getSZ(dbgAddrDevice) + "!undefined " + ((AL & 0x1)? "data" : "code") + '(' + str.toHex(nSeg, 4) + ")=" + str.toHex(selAlias, 4) + ':' + str.toHex(off) + " len " + str.toHex(len));
|
||||
if (this.fWinDbgRM) {
|
||||
this.println(this.getSZ(dbgAddrModule) + '!' + this.getSZ(dbgAddrDevice) + "!undefined " + ((AL & 0x1)? "data" : "code") + '(' + str.toHex(nSeg, 4) + ")=" + str.toHex(selAlias, 4) + ':' + str.toHex(off) + " len " + str.toHex(len));
|
||||
}
|
||||
}
|
||||
cpu.regEAX = (cpu.regEAX & ~0xff) | 0x01;
|
||||
if (this.fWinDbgRM) {
|
||||
cpu.regEAX = (cpu.regEAX & ~0xff) | 0x01;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (this.fWinDbgRM) {
|
||||
this.log("INT 0x68: " + str.toHexByte(AH));
|
||||
this.println("INT 0x68: " + str.toHexByte(AH));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* callWindowsDebuggerPMInit()
|
||||
*
|
||||
* This intercepts calls to the Windows Debugger "PMInit" interface; eg:
|
||||
*
|
||||
* AL = function code
|
||||
*
|
||||
* 0 - initialize IDT
|
||||
* ES:EDI points to protected mode IDT
|
||||
*
|
||||
* 1 - initialize page checking
|
||||
* BX = physical selector
|
||||
* ECX = linear bias
|
||||
*
|
||||
* 2 - specify that debug queries are supported
|
||||
*
|
||||
* 3 - initialize spare PTE
|
||||
* EBX = linear address of spare PTE
|
||||
* EDX = linear address the PTE represents
|
||||
*
|
||||
* 4 - set Enter/Exit VMM routine address
|
||||
* EBX = Enter VMM routine address
|
||||
* ECX = Exit VMM routine address
|
||||
* EDX = $_Debug_Out_Service address
|
||||
* ESI = $_Trace_Out_Service address
|
||||
* The VMM enter/exit routines must return with a retfd
|
||||
*
|
||||
* 5 - get debugger size/physical address
|
||||
* returns: AL = 0 (don't call AL = 1)
|
||||
* ECX = size in bytes
|
||||
* ESI = starting physical code/data address
|
||||
*
|
||||
* 6 - set debugger base/initialize spare PTE
|
||||
* EBX = linear address of spare PTE
|
||||
* EDX = linear address the PTE represents
|
||||
* ESI = starting linear address of debug code/data
|
||||
*
|
||||
* 7 - enable memory context functions
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @return {boolean} (must always return false to skip the call, because the call is using a CALLBREAK address)
|
||||
*/
|
||||
Debugger.prototype.callWindowsDebuggerPMInit = function()
|
||||
{
|
||||
var cpu = this.cpu;
|
||||
var AL = cpu.regEAX & 0xff;
|
||||
this.println("INT 0x68 callback: " + str.toHexByte(AL));
|
||||
if (AL == 5) {
|
||||
cpu.regECX = cpu.regESI = 0; // our in-machine debugger footprint is zero
|
||||
cpu.regEAX = (cpu.regEAX & ~0xff) | 0x01;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -62,21 +62,19 @@ var Interrupts = {
|
|||
ALT_DISK: 0x40, // HDC BIOS saves original FDC BIOS vector here
|
||||
ALT_VIDEO: 0x6D, // IBM VGA BIOS saves original video BIOS vector here
|
||||
WINDBG: { // Windows Debugger protected-mode interface
|
||||
VECTOR: 0x41,
|
||||
IS_LOADED: 0x004F, // AX command
|
||||
LOADED: 0xF386, // returned in AX if Windows Debugger loaded
|
||||
LOAD_SEG: 0x0050, // SI==0 if code, 1 if data; BX==segnum-1; CX==selector; ES:[E]DI->module name
|
||||
ENABLED: true // support for WINDBGRM interrupts can be disabled
|
||||
VECTOR: 0x41, // (AX==command)
|
||||
IS_LOADED: 0x004F, // DS_DebLoaded
|
||||
LOADED: 0xF386, // DS_DebPresent (returned in AX if Windows Debugger loaded)
|
||||
LOAD_SEG: 0x0050, // DS_LoadSeg (SI==0 if code, 1 if data; BX==segnum-1; CX==selector; ES:[E]DI->module name)
|
||||
ENABLED: true // support for WINDBG interrupts can be disabled (but NOT if WINDBGRM is enabled)
|
||||
},
|
||||
WINDBGRM: { // Windows Debugger real-mode interface
|
||||
VECTOR: 0x68,
|
||||
IS_LOADED: 0x43, // AH command
|
||||
LOADED: 0xF386, // returned in AX if Windows Debugger loaded
|
||||
LOAD_SEG: 0x50, // AL=segment type, ES:DI->D386_Device_Params
|
||||
/*
|
||||
* This must be disabled until we're able to respond intelligently to requests like D386_Prepare_PMode (0x44)
|
||||
*/
|
||||
ENABLED: false // support for WINDBGRM interrupts can be disabled
|
||||
VECTOR: 0x68, // (AH==command)
|
||||
IS_LOADED: 0x43, // D386_Identify
|
||||
LOADED: 0xF386, // D386_Id (returned in AX if Windows Debugger loaded)
|
||||
PREP_PMODE: 0x44, // D386_Prepare_PMode (must return a 16:32 address in ES:EDI to a "PMinit" handler)
|
||||
LOAD_SEG: 0x50, // D386_Load_Segment (AL=segment type, ES:DI->D386_Device_Params)
|
||||
ENABLED: true // support for WINDBGRM interrupts can be disabled
|
||||
},
|
||||
FUNCS: {} // filled in only if DEBUGGER is true
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2310,18 +2310,14 @@ X86CPU.prototype.setCSIP = function(off, sel, fCall)
|
|||
/*
|
||||
* We break this operation into the following discrete steps (eg, set IP, load CS, and then update IP) so
|
||||
* that segCS.load(sel) has the ability to modify IP when sel refers to a gate (call, interrupt, trap, etc).
|
||||
*
|
||||
* NOTE: regEIP acts merely as a conduit for the IP, if any, that segCS.load() may load; regLIP is still our
|
||||
* internal instruction pointer. Callers that need the real IP must call getIP().
|
||||
*/
|
||||
this.regEIP = off;
|
||||
var base = this.segCS.loadCode(sel, fCall);
|
||||
var base = this.segCS.loadCode(off, sel, fCall);
|
||||
if (base !== X86.ADDR_INVALID) {
|
||||
/*
|
||||
* TODO: Should this code be factored into a setLIP() function? The other primary client would be fnINT().
|
||||
*/
|
||||
if (I386) this.resetSizes();
|
||||
this.regLIP = (base + (this.regEIP & (I386? this.maskData : 0xffff)))|0;
|
||||
this.regLIP = (base + (this.segCS.offIP & (I386? this.maskData : 0xffff)))|0;
|
||||
this.regLIPLimit = (base + this.segCS.limit)|0;
|
||||
this.nCPL = this.segCS.cpl; // cache the current CPL where it's more convenient
|
||||
if (PREFETCH) this.flushPrefetch(this.regLIP);
|
||||
|
|
|
|||
|
|
@ -369,21 +369,29 @@ X86.fnBTMem = function BTMem(dst, src)
|
|||
if (this.regEA === X86.ADDR_INVALID) {
|
||||
return X86.fnBT.call(this, dst, src);
|
||||
}
|
||||
var offByte = src >>> 3;
|
||||
if (offByte >= this.sizeData) {
|
||||
|
||||
/*
|
||||
* TODO: Consider a worker function that performs the following block of code for: BT, BTC, BTR, and BTS.
|
||||
* It's somewhat inconvenient, because it needs to provide two results: an updated src AND an updated dst.
|
||||
*/
|
||||
if ((src >>> 3) >= this.sizeData) {
|
||||
/*
|
||||
* offByte is src divided by 8, but now we need src divided by 16 or 32, according to the OPERAND size,
|
||||
* We just divided src by 8, but now we need to divide src by 16 or 32, according to the OPERAND size,
|
||||
* which means shifting it right by either 4 or 5 bits. That gives us a short or long INDEX, which we then
|
||||
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we add to regEA.
|
||||
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we must add to
|
||||
* the original EA offset.
|
||||
*/
|
||||
var i = src >>> (this.sizeData == 2? 4 : 5);
|
||||
dst = this.getWord(this.regEA += i * this.sizeData);
|
||||
dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData);
|
||||
}
|
||||
/*
|
||||
* Now we convert src from a bit index into a bit mask.
|
||||
* Now we convert src from a bit index to a bit mask.
|
||||
*/
|
||||
src = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
|
||||
if (dst & src) this.setCF(); else this.clearCF();
|
||||
/*
|
||||
* End of common code block
|
||||
*/
|
||||
|
||||
this.nStepCycles -= 6;
|
||||
this.opFlags |= X86.OPFLAG.NOWRITE;
|
||||
|
|
@ -406,18 +414,19 @@ X86.fnBTCMem = function BTCMem(dst, src)
|
|||
if (this.regEA === X86.ADDR_INVALID) {
|
||||
return X86.fnBTC.call(this, dst, src);
|
||||
}
|
||||
var offByte = src >>> 3;
|
||||
if (offByte >= this.sizeData) {
|
||||
|
||||
if ((src >>> 3) >= this.sizeData) {
|
||||
/*
|
||||
* offByte is src divided by 8, but now we need src divided by 16 or 32, according to the OPERAND size,
|
||||
* We just divided src by 8, but now we need to divide src by 16 or 32, according to the OPERAND size,
|
||||
* which means shifting it right by either 4 or 5 bits. That gives us a short or long INDEX, which we then
|
||||
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we add to regEA.
|
||||
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we must add to
|
||||
* the original EA offset.
|
||||
*/
|
||||
var i = src >>> (this.sizeData == 2? 4 : 5);
|
||||
dst = this.getWord(this.regEA += i * this.sizeData);
|
||||
dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData);
|
||||
}
|
||||
/*
|
||||
* Now we convert src from a bit index into a bit mask.
|
||||
* Now we convert src from a bit index to a bit mask.
|
||||
*/
|
||||
src = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
|
||||
if (dst & src) this.setCF(); else this.clearCF();
|
||||
|
|
@ -442,18 +451,19 @@ X86.fnBTRMem = function BTRMem(dst, src)
|
|||
if (this.regEA === X86.ADDR_INVALID) {
|
||||
return X86.fnBTR.call(this, dst, src);
|
||||
}
|
||||
var offByte = src >>> 3;
|
||||
if (offByte >= this.sizeData) {
|
||||
|
||||
if ((src >>> 3) >= this.sizeData) {
|
||||
/*
|
||||
* offByte is src divided by 8, but now we need src divided by 16 or 32, according to the OPERAND size,
|
||||
* We just divided src by 8, but now we need to divide src by 16 or 32, according to the OPERAND size,
|
||||
* which means shifting it right by either 4 or 5 bits. That gives us a short or long INDEX, which we then
|
||||
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we add to regEA.
|
||||
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we must add to
|
||||
* the original EA offset.
|
||||
*/
|
||||
var i = src >>> (this.sizeData == 2? 4 : 5);
|
||||
dst = this.getWord(this.regEA += i * this.sizeData);
|
||||
dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData);
|
||||
}
|
||||
/*
|
||||
* Now we convert src from a bit index into a bit mask.
|
||||
* Now we convert src from a bit index to a bit mask.
|
||||
*/
|
||||
src = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
|
||||
if (dst & src) this.setCF(); else this.clearCF();
|
||||
|
|
@ -478,18 +488,19 @@ X86.fnBTSMem = function BTSMem(dst, src)
|
|||
if (this.regEA === X86.ADDR_INVALID) {
|
||||
return X86.fnBTS.call(this, dst, src);
|
||||
}
|
||||
var offByte = src >>> 3;
|
||||
if (offByte >= this.sizeData) {
|
||||
|
||||
if ((src >>> 3) >= this.sizeData) {
|
||||
/*
|
||||
* offByte is src divided by 8, but now we need src divided by 16 or 32, according to the OPERAND size,
|
||||
* We just divided src by 8, but now we need to divide src by 16 or 32, according to the OPERAND size,
|
||||
* which means shifting it right by either 4 or 5 bits. That gives us a short or long INDEX, which we then
|
||||
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we add to regEA.
|
||||
* multiply by the OPERAND size to obtain to the corresponding short or long OFFSET that we must add to
|
||||
* the original EA offset.
|
||||
*/
|
||||
var i = src >>> (this.sizeData == 2? 4 : 5);
|
||||
dst = this.getWord(this.regEA += i * this.sizeData);
|
||||
dst = this.getEAWord(this.segEA, this.offEA + i * this.sizeData);
|
||||
}
|
||||
/*
|
||||
* Now we convert src from a bit index into a bit mask.
|
||||
* Now we convert src from a bit index to a bit mask.
|
||||
*/
|
||||
src = 1 << (src & (this.sizeData == 2? 0xf : 0x1f));
|
||||
if (dst & src) this.setCF(); else this.clearCF();
|
||||
|
|
|
|||
|
|
@ -124,10 +124,14 @@ function X86Seg(cpu, id, sName, fProt)
|
|||
*
|
||||
* loadIDT() sets fCall to true unconditionally in protected-mode (fCall has no meaning in real-mode).
|
||||
*/
|
||||
this.fCall = null;
|
||||
this.fStackSwitch = false;
|
||||
this.sizeFrame = 2; // must be set by all loadIDT() calls so that callers know the proper frame size
|
||||
this.awParms = (this.id == X86Seg.ID.CODE? new Array(32) : []);
|
||||
if (this.id == X86Seg.ID.CODE) {
|
||||
this.offIP = 0;
|
||||
this.fCall = null;
|
||||
this.fStackSwitch = false;
|
||||
this.sizeFrame = 2; // must be set by all loadIDT() calls so that callers know the proper frame size
|
||||
this.awParms = new Array(32);
|
||||
this.aCallBreaks = [];
|
||||
}
|
||||
this.updateMode(true, fProt);
|
||||
}
|
||||
|
||||
|
|
@ -142,18 +146,41 @@ X86Seg.ID = {
|
|||
DBG: 7 // "DBG"
|
||||
};
|
||||
|
||||
X86Seg.CALLBREAK_SEL = 0x0001;
|
||||
|
||||
/**
|
||||
* loadCode(sel, fCall)
|
||||
* addCallBreak(fn)
|
||||
*
|
||||
* A simple wrapper function that encapsulates setting the fCall property for segCS loads.
|
||||
* Returns a "call break" address in an [off, sel] array. The given function, fn(), is called
|
||||
* whenever that address is called, and if fn() returns false, then the call is skipped. Otherwise,
|
||||
* the call is performed (ie, the old CS:[E]IP is pushed on the stack, and CS:[E]IP is set to the
|
||||
* "call break" address. Which is probably a bad idea, so your function should probably always
|
||||
* return false. Just sayin'.
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {function()} fn
|
||||
* @return {Array.<number>} containing offset and selector of call-break address
|
||||
*/
|
||||
X86Seg.prototype.addCallBreak = function(fn)
|
||||
{
|
||||
this.aCallBreaks.push(fn);
|
||||
return [this.aCallBreaks.length, X86Seg.CALLBREAK_SEL];
|
||||
};
|
||||
|
||||
/**
|
||||
* loadCode(off, sel, fCall)
|
||||
*
|
||||
* A simple wrapper function that encapsulates setting offIP and fCall for segCS loads.
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} off
|
||||
* @param {number} sel
|
||||
* @param {boolean|undefined} fCall is true if CALLF in progress, false if RETF/IRET in progress, undefined otherwise
|
||||
* @return {number} base address of selected segment, or X86.ADDR_INVALID if error
|
||||
*/
|
||||
X86Seg.prototype.loadCode = function loadCode(sel, fCall)
|
||||
X86Seg.prototype.loadCode = function loadCode(off, sel, fCall)
|
||||
{
|
||||
this.offIP = off;
|
||||
this.fCall = fCall;
|
||||
return this.load(sel);
|
||||
};
|
||||
|
|
@ -295,7 +322,7 @@ X86Seg.prototype.loadIDTProt = function loadIDTProt(nIDT)
|
|||
var addrDesc = (cpu.addrIDT + nIDT)|0;
|
||||
if (((cpu.addrIDTLimit - addrDesc)|0) >= 7) {
|
||||
this.fCall = true;
|
||||
return this.loadDesc8(addrDesc, nIDT) + cpu.regEIP;
|
||||
return this.loadDesc8(addrDesc, nIDT) + this.offIP;
|
||||
}
|
||||
/*
|
||||
* TODO: Remove fHalt=true from this fnFault() call once this code path has been tested.
|
||||
|
|
@ -637,6 +664,35 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
this.sizeFrame = this.sizeData;
|
||||
|
||||
var fCall = this.fCall;
|
||||
|
||||
/*
|
||||
* This special bit of code is currently used only by the Debugger, when it needs to inject
|
||||
* a 16:32 callback address into the machine that it can intercept calls to. We call these
|
||||
* "call break" addresses, because they're like private breakpoints that only operate when
|
||||
* a particular address is called; specifically, an address with selector 0x0001 and an offset
|
||||
* that forms an index (1-based) into the aCallBreaks function table.
|
||||
*
|
||||
* In protected-mode, 0x0001 is an invalid code selector (a null selector with an RPL of 1),
|
||||
* and while it's not inconceivable that an operating system might use such a selector for
|
||||
* some strange purpose, I've not seen such an operating system. And in any case, those
|
||||
* operating systems are not likely to trigger the Debugger's call to addCallBreak(), so no
|
||||
* call breaks will be generated, and this code will never execute.
|
||||
*
|
||||
* TODO: If we ever need this to be mode-independent, it can be moved somewhere where it will
|
||||
* trigger for both real and protected-mode code segment loads, because CALLBREAK_SEL (0x0001)
|
||||
* is also a very unlikely real-mode CS value (but again, not inconceivable). I think this is
|
||||
* a reasonable solution, and it's likely the best we can do without injecting code into the
|
||||
* machine that we could address -- and even then, it would not be a mode-independent address.
|
||||
*/
|
||||
if (fCall && sel == X86Seg.CALLBREAK_SEL && this.aCallBreaks.length) {
|
||||
var iBreak = this.offIP - 1;
|
||||
var fnCallBreak = this.aCallBreaks[iBreak];
|
||||
cpu.assert(fnCallBreak);
|
||||
if (fnCallBreak && !fnCallBreak()) {
|
||||
return X86.ADDR_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
var rpl = sel & X86.SEL.RPL;
|
||||
var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
|
||||
|
||||
|
|
@ -789,7 +845,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
|
||||
this.sizeFrame = sizeGate;
|
||||
|
||||
cpu.regEIP = limit;
|
||||
this.offIP = limit;
|
||||
cpu.assert(this.cpl == cplNew);
|
||||
|
||||
if (this.cpl < cplOld) {
|
||||
|
|
@ -943,9 +999,12 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fProbe)
|
|||
|
||||
default:
|
||||
/*
|
||||
* The only other case used to be X86Seg.ID.DBG, but the Debugger uses probeDesc() now, so we should never get here.
|
||||
* The only other cases are:
|
||||
*
|
||||
* X86Seg.ID.NULL, X86Seg.ID.LDT, and X86Seg.ID.DBG
|
||||
*
|
||||
* which correspond to segNULL, segLDT and segDebugger; however, segLDT is the only one that might require further validation (TODO: Investigate).
|
||||
*/
|
||||
cpu.assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue