Made RETF restartable when the target segment is not present
This commit is contained in:
parent
2d7cbe2864
commit
5b64488286
5 changed files with 100 additions and 41 deletions
|
|
@ -627,7 +627,7 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
Debugger.TRACE_LIMIT = 100000;
|
||||
Debugger.HISTORY_LIMIT = DEBUG? 100000 : 10000;
|
||||
Debugger.HISTORY_LIMIT = DEBUG? 100000 : 1000;
|
||||
|
||||
/*
|
||||
* Opcode 0x0F has a distinguished history:
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ var X86 = {
|
|||
EXT: 0x0001,
|
||||
IDT: 0x0002,
|
||||
LDT: 0x0004,
|
||||
MASK: 0xFFF8 // index of corresponding entry in GDT, LDT or IDT
|
||||
SELMASK: 0xFFFC
|
||||
},
|
||||
RESULT: {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -1127,7 +1127,7 @@ X86CPU.prototype.getReg = function(i)
|
|||
reg = this.regEBX;
|
||||
break;
|
||||
case 0x4:
|
||||
reg = this.regESP;
|
||||
reg = this.getSP();
|
||||
break;
|
||||
case 0x5:
|
||||
reg = this.regEBP;
|
||||
|
|
@ -1165,7 +1165,7 @@ X86CPU.prototype.setReg = function(i, reg)
|
|||
this.regEBX = reg;
|
||||
break;
|
||||
case 0x4:
|
||||
this.regESP = reg;
|
||||
this.setSP(reg);
|
||||
break;
|
||||
case 0x5:
|
||||
this.regEBP = reg;
|
||||
|
|
@ -1295,6 +1295,13 @@ X86CPU.prototype.resetRegs = function()
|
|||
*/
|
||||
this.nFault = -1;
|
||||
|
||||
/*
|
||||
* These are used to snapshot regLIP and regLSP, to help make instructions restartable;
|
||||
* currently opLIP is updated prior to every instruction, but opLSP is updated only for
|
||||
* "problematic" instructions (eg, RETF) and should otherwise remain set to X86.ADDR_INVALID.
|
||||
*/
|
||||
this.opLIP = this.opLSP = X86.ADDR_INVALID;
|
||||
|
||||
/*
|
||||
* Segment registers used to be defined as separate variables (eg, regCS and regCS0 stored the segment
|
||||
* number and base linear address, respectively), but segment registers are now defined as X86Seg objects.
|
||||
|
|
@ -1419,6 +1426,21 @@ X86CPU.prototype.resetRegs = function()
|
|||
this.setProtMode();
|
||||
};
|
||||
|
||||
/**
|
||||
* zeroSeg(seg)
|
||||
*
|
||||
* Helper to zero a segment register as privilege transitions require.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {X86Seg} seg
|
||||
*/
|
||||
X86CPU.prototype.zeroSeg = function(seg)
|
||||
{
|
||||
if ((seg.sel & X86.SEL.MASK) && seg.dpl < this.nCPL && (seg.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||
seg.load(0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* setAddrSize(size)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2228,14 +2228,35 @@ X86.fnRCRd = function RCRd(dst, src)
|
|||
/**
|
||||
* fnRETF(n)
|
||||
*
|
||||
* For protected-mode, this function must be prepared to pop any arguments off the current stack AND
|
||||
* whatever stack we may have switched to (setCSIP() returns true only when a stack switch has occurred).
|
||||
* For protected-mode, this function must pop any arguments off the current stack AND whatever stack
|
||||
* we may have switched to; setCSIP() returns true if a stack switch occurred, false if not, and null
|
||||
* if an error occurred.
|
||||
*
|
||||
* Take a look at our counterpart, fnCALLF():
|
||||
*
|
||||
* if (this.setCSIP(off, sel, true) != null) {
|
||||
* this.pushWord(oldCS);
|
||||
* this.pushWord(oldIP);
|
||||
* }
|
||||
*
|
||||
* That code makes opCALLF() restartable, because it doesn't modify the stack unless setCSIP() succeeds.
|
||||
*
|
||||
* Here, our task is a little more complicated, because 1) it's not convenient to defer our stack
|
||||
* operations (it's much simpler to perform them BEFORE the setCSIP() call rather than AFTER); 2) we
|
||||
* have to deal with an additional stack adjustment value (n); and 3) if setCSIP() triggers a fault
|
||||
* (eg, NP_FAULT), fnFault() must be able to do the rewinding, which happens BEFORE setCSIP() returns.
|
||||
*
|
||||
* The current hack to make the stack "rewindable" involves copying regLSP to opLSP, similar to what we do
|
||||
* for EIP (ie, by copying regLIP into opLIP prior to executing every opcode). However, I don't really want
|
||||
* to snapshot more data inside the opcode loop, so my compromise is to set opLSP only within "problematic"
|
||||
* instructions (like this one), and set it back to X86.ADDR_INVALID when we're done.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} n
|
||||
*/
|
||||
X86.fnRETF = function RETF(n)
|
||||
{
|
||||
this.opLSP = this.regLSP;
|
||||
var newIP = this.popWord();
|
||||
var newCS = this.popWord();
|
||||
|
||||
|
|
@ -2246,12 +2267,12 @@ X86.fnRETF = function RETF(n)
|
|||
if (this.setCSIP(newIP, newCS, false)) { // returns true if a stack switch occurred
|
||||
/*
|
||||
* Fool me once, shame on... whatever. If setCSIP() indicates a stack switch occurred,
|
||||
* make sure we're in protected mode, because automatic stack switches can't occur in real mode,
|
||||
* and adjusting SP again under those circumstances will likely cause great harm.
|
||||
* make sure we're in protected mode, because automatic stack switches can't occur in real mode.
|
||||
*/
|
||||
this.assert(!!(this.regCR0 & X86.CR0.MSW.PE));
|
||||
|
||||
if (n) this.setSP(this.getSP() + n); // TODO: optimize
|
||||
|
||||
/*
|
||||
* As per Intel documentation: "If any of [the DS or ES] registers refer to segments whose DPL is
|
||||
* less than the new CPL (excluding conforming code segments), the segment register is loaded with
|
||||
|
|
@ -2261,15 +2282,14 @@ X86.fnRETF = function RETF(n)
|
|||
* it safe and using CODE_CONFORMING instead of CODE_CONFORMING_READABLE. Also, for the record, I've not
|
||||
* seen this situation occur yet (eg, in OS/2 1.0).
|
||||
*/
|
||||
if ((this.segDS.sel & X86.SEL.MASK) && this.segDS.dpl < this.nCPL && (this.segDS.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||
this.assert(false); // I'm not asserting this is bad, I just want to see it in action
|
||||
this.segDS.load(0);
|
||||
}
|
||||
if ((this.segES.sel & X86.SEL.MASK) && this.segES.dpl < this.nCPL && (this.segES.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) != X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||
this.assert(false); // I'm not asserting this is bad, I just want to see it in action
|
||||
this.segES.load(0);
|
||||
this.zeroSeg(this.segDS);
|
||||
this.zeroSeg(this.segES);
|
||||
if (I386 && this.model >= X86.MODEL_80386) {
|
||||
this.zeroSeg(this.segFS);
|
||||
this.zeroSeg(this.segGS);
|
||||
}
|
||||
}
|
||||
this.opLSP = X86.ADDR_INVALID;
|
||||
if (MAXDEBUG && n == 2 && this.cIntReturn) this.checkIntReturn(this.regLIP);
|
||||
};
|
||||
|
||||
|
|
@ -3693,12 +3713,15 @@ X86.fnFault = function(nFault, nError, fHalt, nCycles)
|
|||
{
|
||||
/*
|
||||
* X86.OPFLAG.FAULT flag is used by selected opcodes to provide an early exit, restore register(s), or whatever is
|
||||
* needed to help ensure instruction restartability; there is currently no mechanism for snapping and restoring all
|
||||
* registers for any instruction that might fault, so it's every opcode for themselves....
|
||||
* needed to help ensure instruction restartability; there is currently no general-purpose mechanism for snapping
|
||||
* and restoring all registers for any instruction that might fault, so it's every opcode for themselves.
|
||||
*
|
||||
* X86.EXCEPTION.DEBUG exceptions set their own special flag, X86.OPFLAG.DEBUG, to prevent redundant DEBUG exceptions,
|
||||
* so we don't need to set OPFLAG.FAULT in that case, because a DEBUG exception doesn't actually prevent an instruction
|
||||
* from executing.
|
||||
*
|
||||
* TODO: Review the restartability of all our opcode handlers, starting with those that affect the segment registers
|
||||
* and then moving on to the rest, and determine whether we really need a general-purpose solution instead.
|
||||
*/
|
||||
if (nFault == X86.EXCEPTION.DEBUG) {
|
||||
this.opFlags |= X86.OPFLAG.DEBUG;
|
||||
|
|
@ -3719,6 +3742,10 @@ X86.fnFault = function(nFault, nError, fHalt, nCycles)
|
|||
* Single-fault (error code is passed through, and the responsible instruction is restartable)
|
||||
*/
|
||||
this.setIP(this.opLIP - this.segCS.base);
|
||||
if (this.opLSP != X86.ADDR_INVALID) {
|
||||
this.setSP((this.regESP & ~this.segSS.addrMask) | (this.opLSP - this.segSS.base));
|
||||
this.opLSP = X86.ADDR_INVALID;
|
||||
}
|
||||
fDispatch = true;
|
||||
} else if (this.nFault != X86.EXCEPTION.DF_FAULT) {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ X86Seg.prototype.loadProt = function loadProt(sel)
|
|||
return this.loadDesc8(addrDesc, sel);
|
||||
}
|
||||
if (this.id < X86Seg.ID.VER) {
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||
}
|
||||
}
|
||||
return X86.ADDR_INVALID;
|
||||
|
|
@ -282,9 +282,9 @@ 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, true) + cpu.regEIP;
|
||||
}
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT, true);
|
||||
return X86.ADDR_INVALID;
|
||||
};
|
||||
|
||||
|
|
@ -499,7 +499,7 @@ X86Seg.prototype.loadAcc = function(sel, fGDT)
|
|||
return cpu.getShort(addrDesc + X86.DESC.ACC.OFFSET);
|
||||
}
|
||||
}
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||
return X86.DESC.ACC.INVALID;
|
||||
};
|
||||
|
||||
|
|
@ -540,7 +540,7 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
|
|||
};
|
||||
|
||||
/**
|
||||
* loadDesc8(addrDesc, sel)
|
||||
* loadDesc8(addrDesc, sel, fIDT)
|
||||
*
|
||||
* Used to load a protected-mode selector that refers to an 8-byte "descriptor table" (GDT, LDT, IDT) entry:
|
||||
*
|
||||
|
|
@ -554,9 +554,10 @@ X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
|
|||
* @this {X86Seg}
|
||||
* @param {number} addrDesc is the descriptor address
|
||||
* @param {number} sel is the associated selector, or nIDT*8 if IDT descriptor
|
||||
* @param {boolean} [fIDT] is true if sel refers to the IDT (only affects error handling)
|
||||
* @return {number} base address of selected segment, or ADDR_INVALID if error
|
||||
*/
|
||||
X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||
X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fIDT)
|
||||
{
|
||||
var cpu = this.cpu;
|
||||
var limit = cpu.getShort(addrDesc + X86.DESC.LIMIT.OFFSET);
|
||||
|
|
@ -584,12 +585,12 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
|||
if (this.id == X86Seg.ID.CODE) {
|
||||
this.fStackSwitch = false;
|
||||
var fCall = this.fCall;
|
||||
var regPSClear, nFaultError, regSP;
|
||||
var regPSClear, regSP;
|
||||
var rpl = sel & X86.SEL.RPL;
|
||||
var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
|
||||
|
||||
if (selMasked && !(acc & X86.DESC.ACC.PRESENT)) {
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel);
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||
base = addrDesc = X86.ADDR_INVALID;
|
||||
break;
|
||||
}
|
||||
|
|
@ -632,19 +633,16 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
|||
else if (type == X86.DESC.ACC.TYPE.GATE_CALL || type == X86.DESC.ACC.TYPE.GATE386_CALL) {
|
||||
fGate = true;
|
||||
regPSClear = 0;
|
||||
nFaultError = sel;
|
||||
if (rpl < this.cpl) rpl = this.cpl; // set RPL to max(RPL,CPL) for call gates
|
||||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE286_INT || type == X86.DESC.ACC.TYPE.GATE386_INT) {
|
||||
fGate = true;
|
||||
regPSClear = (X86.PS.NT | X86.PS.TF | X86.PS.IF);
|
||||
nFaultError = sel | X86.ERRCODE.EXT;
|
||||
cpu.assert(!(acc & 0x1f));
|
||||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE286_TRAP || type == X86.DESC.ACC.TYPE.GATE386_TRAP) {
|
||||
fGate = true;
|
||||
regPSClear = (X86.PS.NT | X86.PS.TF);
|
||||
nFaultError = sel | X86.ERRCODE.EXT;
|
||||
cpu.assert(!(acc & 0x1f));
|
||||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE_TASK) {
|
||||
|
|
@ -722,6 +720,10 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
|||
cpu.setSS(cpu.getShort(addrTSS + offSS), true);
|
||||
cpu.setSP(cpu.getLong(addrTSS + offSP));
|
||||
if (regPS & X86.PS.VM) {
|
||||
/*
|
||||
* segFS amd segGS exist only on 80386 machines
|
||||
*/
|
||||
cpu.assert(I386 && cpu.model >= X86.MODEL_80386);
|
||||
cpu.pushWord(cpu.segGS.sel);
|
||||
cpu.setGS(0);
|
||||
cpu.pushWord(cpu.segFS.sel);
|
||||
|
|
@ -739,14 +741,10 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
|||
}
|
||||
return this.base;
|
||||
}
|
||||
cpu.assert(false);
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, nFaultError, true);
|
||||
base = addrDesc = X86.ADDR_INVALID;
|
||||
break;
|
||||
}
|
||||
else if (fGate !== false) {
|
||||
if (fGate !== false) {
|
||||
cpu.assert(false);
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, (sel & X86.ERRCODE.SELMASK) | (fIDT? X86.ERRCODE.IDT : 0), true);
|
||||
base = addrDesc = X86.ADDR_INVALID;
|
||||
break;
|
||||
}
|
||||
|
|
@ -754,7 +752,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
|||
else if (this.id == X86Seg.ID.DATA) {
|
||||
if (selMasked) {
|
||||
if (!(acc & X86.DESC.ACC.PRESENT)) {
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel);
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.NP_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||
base = addrDesc = X86.ADDR_INVALID;
|
||||
break;
|
||||
}
|
||||
|
|
@ -780,7 +778,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
|||
*
|
||||
* So, if the ACC field is zero, we won't set the last fnFault() parameter (fHalt) to true.
|
||||
*/
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, !!acc);
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK, !!acc);
|
||||
base = addrDesc = X86.ADDR_INVALID;
|
||||
break;
|
||||
}
|
||||
|
|
@ -788,12 +786,12 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
|||
}
|
||||
else if (this.id == X86Seg.ID.STACK) {
|
||||
if (!(acc & X86.DESC.ACC.PRESENT)) {
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.SS_FAULT, sel);
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.SS_FAULT, sel & X86.ERRCODE.SELMASK);
|
||||
base = addrDesc = X86.ADDR_INVALID;
|
||||
break;
|
||||
}
|
||||
if (!selMasked || type < X86.DESC.ACC.TYPE.SEG || (type & (X86.DESC.ACC.TYPE.CODE | X86.DESC.ACC.TYPE.WRITABLE)) != X86.DESC.ACC.TYPE.WRITABLE) {
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK, true);
|
||||
base = addrDesc = X86.ADDR_INVALID;
|
||||
break;
|
||||
}
|
||||
|
|
@ -801,7 +799,7 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
|||
else if (this.id == X86Seg.ID.TSS) {
|
||||
var typeTSS = type & ~X86.DESC.ACC.TSS_BUSY;
|
||||
if (!selMasked || typeTSS != X86.DESC.ACC.TYPE.TSS286 && typeTSS != X86.DESC.ACC.TYPE.TSS386) {
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
|
||||
if (this.id < X86Seg.ID.VER) X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel & X86.ERRCODE.SELMASK, true);
|
||||
base = addrDesc = X86.ADDR_INVALID;
|
||||
break;
|
||||
}
|
||||
|
|
@ -885,7 +883,7 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
|
|||
* TODO: Verify that it is (always) correct to require that the BUSY bit be currently set.
|
||||
*/
|
||||
if (!(cpu.segTSS.type & X86.DESC.ACC.TSS_BUSY)) {
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew, true);
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew & X86.ERRCODE.SELMASK, true);
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
|
|
@ -905,7 +903,7 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
|
|||
|
||||
if (fNest !== false) {
|
||||
if (cpu.segTSS.type & X86.DESC.ACC.TSS_BUSY) {
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew, true);
|
||||
X86.fnFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew & X86.ERRCODE.SELMASK, true);
|
||||
return false;
|
||||
}
|
||||
cpu.setShort(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, cpu.segTSS.acc |= X86.DESC.ACC.TSS_BUSY);
|
||||
|
|
@ -979,8 +977,14 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
|
|||
cpu.setLong(addrOld + X86.TSS386.TASK_CS, cpu.segCS.sel);
|
||||
cpu.setLong(addrOld + X86.TSS386.TASK_SS, cpu.segSS.sel);
|
||||
cpu.setLong(addrOld + X86.TSS386.TASK_DS, cpu.segDS.sel);
|
||||
|
||||
/*
|
||||
* segFS amd segGS exist only on 80386 machines
|
||||
*/
|
||||
cpu.assert(I386 && cpu.model >= X86.MODEL_80386);
|
||||
cpu.setLong(addrOld + X86.TSS386.TASK_FS, cpu.segFS.sel);
|
||||
cpu.setLong(addrOld + X86.TSS386.TASK_GS, cpu.segGS.sel);
|
||||
|
||||
/*
|
||||
* Reload all registers from the new TSS; it's important to reload the LDTR sooner
|
||||
* rather than later, so that as segment registers are reloaded, any LDT selectors will
|
||||
|
|
@ -999,8 +1003,14 @@ X86Seg.prototype.switchTSS = function switchTSS(selNew, fNest)
|
|||
cpu.regEDI = cpu.getLong(addrNew + X86.TSS386.TASK_EDI);
|
||||
cpu.segES.load(cpu.getShort(addrNew + X86.TSS386.TASK_ES));
|
||||
cpu.segDS.load(cpu.getShort(addrNew + X86.TSS386.TASK_DS));
|
||||
|
||||
/*
|
||||
* segFS amd segGS exist only on 80386 machines
|
||||
*/
|
||||
cpu.assert(I386 && cpu.model >= X86.MODEL_80386);
|
||||
cpu.segFS.load(cpu.getShort(addrNew + X86.TSS386.TASK_FS));
|
||||
cpu.segGS.load(cpu.getShort(addrNew + X86.TSS386.TASK_GS));
|
||||
|
||||
cpu.setCSIP(cpu.getLong(addrNew + X86.TSS386.TASK_EIP), cpu.getShort(addrNew + X86.TSS386.TASK_CS));
|
||||
offSS = X86.TSS386.TASK_SS;
|
||||
offSP = X86.TSS386.TASK_ESP;
|
||||
|
|
|
|||
Loading…
Reference in a new issue