Implemented (but have not yet tested) 286 LOADALL
This commit is contained in:
parent
426fd0ff40
commit
5e6604b760
9 changed files with 335 additions and 173 deletions
|
|
@ -2828,7 +2828,7 @@ if (DEBUGGER) {
|
|||
this.getFlagStr("S") + this.getFlagStr("Z") + this.getFlagStr("A") + this.getFlagStr("P") + this.getFlagStr("C");
|
||||
if (fProt) {
|
||||
s += " MS=" + str.toHexWord(this.cpu.regMSW) + '\n' +
|
||||
this.getDTRStr("LD", this.cpu.segLDT.sel, this.cpu.segLDT.base, this.cpu.segLDT.limit) + ' ' +
|
||||
this.getDTRStr("LD", this.cpu.segLDT.sel, this.cpu.segLDT.base, this.cpu.segLDT.base + this.cpu.segLDT.limit) + ' ' +
|
||||
this.getDTRStr("GD", null, this.cpu.addrGDT, this.cpu.addrGDTLimit) + ' ' +
|
||||
this.getDTRStr("ID", null, this.cpu.addrIDT, this.cpu.addrIDTLimit) + " TR=" + str.toHexWord(this.cpu.segTSS.sel) +
|
||||
" A20=" + (this.bus.getA20()? "ON" : "OFF");
|
||||
|
|
@ -2948,7 +2948,7 @@ if (DEBUGGER) {
|
|||
break;
|
||||
/*
|
||||
* I used to alias "PC" to "IP", until I discovered that early (perhaps even ALL) versions of DEBUG.COM
|
||||
* treat "PC" as an alias for the 16-bit flags register. TODO: Add support for "PC" as the flags register.
|
||||
* treat "PC" as an alias for the 16-bit flags register. So for purposes of parseValue(), "PC" has been removed.
|
||||
*/
|
||||
case "IP":
|
||||
value = this.cpu.regIP;
|
||||
|
|
@ -4082,105 +4082,130 @@ if (DEBUGGER) {
|
|||
}
|
||||
var w = parseInt(sValue, 16);
|
||||
if (!isNaN(w)) {
|
||||
switch (sReg.toUpperCase()) {
|
||||
case "AL":
|
||||
this.cpu.regAX = (this.cpu.regAX & 0xff00) | (w & 0xff);
|
||||
break;
|
||||
case "AH":
|
||||
this.cpu.regAX = (this.cpu.regAX & 0x00ff) | ((w << 8) & 0xff);
|
||||
break;
|
||||
case "AX":
|
||||
this.cpu.regAX = (w & 0xffff);
|
||||
break;
|
||||
case "BL":
|
||||
this.cpu.regBX = (this.cpu.regBX & 0xff00) | (w & 0xff);
|
||||
break;
|
||||
case "BH":
|
||||
this.cpu.regBX = (this.cpu.regBX & 0x00ff) | ((w << 8) & 0xff);
|
||||
break;
|
||||
case "BX":
|
||||
this.cpu.regBX = (w & 0xffff);
|
||||
break;
|
||||
case "CL":
|
||||
this.cpu.regCX = (this.cpu.regCX & 0xff00) | (w & 0xff);
|
||||
break;
|
||||
case "CH":
|
||||
this.cpu.regCX = (this.cpu.regCX & 0x00ff) | ((w << 8) & 0xff);
|
||||
break;
|
||||
case "CX":
|
||||
this.cpu.regCX = (w & 0xffff);
|
||||
break;
|
||||
case "DL":
|
||||
this.cpu.regDX = (this.cpu.regDX & 0xff00) | (w & 0xff);
|
||||
break;
|
||||
case "DH":
|
||||
this.cpu.regDX = (this.cpu.regDX & 0x00ff) | ((w << 8) & 0xff);
|
||||
break;
|
||||
case "DX":
|
||||
this.cpu.regDX = (w & 0xffff);
|
||||
break;
|
||||
case "SP":
|
||||
this.cpu.regSP = (w & 0xffff);
|
||||
break;
|
||||
case "BP":
|
||||
this.cpu.regBP = (w & 0xffff);
|
||||
break;
|
||||
case "SI":
|
||||
this.cpu.regSI = (w & 0xffff);
|
||||
break;
|
||||
case "DI":
|
||||
this.cpu.regDI = (w & 0xffff);
|
||||
break;
|
||||
case "DS":
|
||||
this.cpu.setDS(w);
|
||||
break;
|
||||
case "ES":
|
||||
this.cpu.setES(w);
|
||||
break;
|
||||
case "SS":
|
||||
this.cpu.setSS(w);
|
||||
break;
|
||||
case "CS":
|
||||
fIns = true;
|
||||
this.cpu.setCS(w);
|
||||
this.aAddrNextCode = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel);
|
||||
break;
|
||||
/*
|
||||
* I used to alias "PC" to "IP", until I discovered that early (perhaps ALL) versions of
|
||||
* DEBUG.COM treat "PC" as an alias for the 16-bit flags register. TODO: Add support for "PC".
|
||||
*/
|
||||
case "IP":
|
||||
fIns = true;
|
||||
this.cpu.setIP(w);
|
||||
this.aAddrNextCode = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel);
|
||||
break;
|
||||
case "C":
|
||||
if (w) this.cpu.setCF(); else this.cpu.clearCF();
|
||||
break;
|
||||
case "P":
|
||||
if (w) this.cpu.setPF(); else this.cpu.clearPF();
|
||||
break;
|
||||
case "A":
|
||||
if (w) this.cpu.setAF(); else this.cpu.clearAF();
|
||||
break;
|
||||
case "Z":
|
||||
if (w) this.cpu.setZF(); else this.cpu.clearZF();
|
||||
break;
|
||||
case "S":
|
||||
if (w) this.cpu.setSF(); else this.cpu.clearSF();
|
||||
break;
|
||||
case "I":
|
||||
if (w) this.cpu.setIF(); else this.cpu.clearIF();
|
||||
break;
|
||||
case "D":
|
||||
if (w) this.cpu.setDF(); else this.cpu.clearDF();
|
||||
break;
|
||||
case "V":
|
||||
if (w) this.cpu.setOF(); else this.cpu.clearOF();
|
||||
break;
|
||||
default:
|
||||
sReg = sReg.toUpperCase();
|
||||
switch (sReg) {
|
||||
case "AL":
|
||||
this.cpu.regAX = (this.cpu.regAX & 0xff00) | (w & 0xff);
|
||||
break;
|
||||
case "AH":
|
||||
this.cpu.regAX = (this.cpu.regAX & 0x00ff) | ((w << 8) & 0xff);
|
||||
break;
|
||||
case "AX":
|
||||
this.cpu.regAX = (w & 0xffff);
|
||||
break;
|
||||
case "BL":
|
||||
this.cpu.regBX = (this.cpu.regBX & 0xff00) | (w & 0xff);
|
||||
break;
|
||||
case "BH":
|
||||
this.cpu.regBX = (this.cpu.regBX & 0x00ff) | ((w << 8) & 0xff);
|
||||
break;
|
||||
case "BX":
|
||||
this.cpu.regBX = (w & 0xffff);
|
||||
break;
|
||||
case "CL":
|
||||
this.cpu.regCX = (this.cpu.regCX & 0xff00) | (w & 0xff);
|
||||
break;
|
||||
case "CH":
|
||||
this.cpu.regCX = (this.cpu.regCX & 0x00ff) | ((w << 8) & 0xff);
|
||||
break;
|
||||
case "CX":
|
||||
this.cpu.regCX = (w & 0xffff);
|
||||
break;
|
||||
case "DL":
|
||||
this.cpu.regDX = (this.cpu.regDX & 0xff00) | (w & 0xff);
|
||||
break;
|
||||
case "DH":
|
||||
this.cpu.regDX = (this.cpu.regDX & 0x00ff) | ((w << 8) & 0xff);
|
||||
break;
|
||||
case "DX":
|
||||
this.cpu.regDX = (w & 0xffff);
|
||||
break;
|
||||
case "SP":
|
||||
this.cpu.regSP = (w & 0xffff);
|
||||
break;
|
||||
case "BP":
|
||||
this.cpu.regBP = (w & 0xffff);
|
||||
break;
|
||||
case "SI":
|
||||
this.cpu.regSI = (w & 0xffff);
|
||||
break;
|
||||
case "DI":
|
||||
this.cpu.regDI = (w & 0xffff);
|
||||
break;
|
||||
case "DS":
|
||||
this.cpu.setDS(w);
|
||||
break;
|
||||
case "ES":
|
||||
this.cpu.setES(w);
|
||||
break;
|
||||
case "SS":
|
||||
this.cpu.setSS(w);
|
||||
break;
|
||||
case "CS":
|
||||
fIns = true;
|
||||
this.cpu.setCS(w);
|
||||
this.aAddrNextCode = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel);
|
||||
break;
|
||||
case "IP":
|
||||
fIns = true;
|
||||
this.cpu.setIP(w);
|
||||
this.aAddrNextCode = this.newAddr(this.cpu.regIP, this.cpu.segCS.sel);
|
||||
break;
|
||||
/*
|
||||
* I used to alias "PC" to "IP", until I discovered that early (perhaps ALL) versions of
|
||||
* DEBUG.COM treat "PC" as an alias for the 16-bit flags register.
|
||||
*/
|
||||
case "PC":
|
||||
this.cpu.setPS(w);
|
||||
break;
|
||||
case "C":
|
||||
if (w) this.cpu.setCF(); else this.cpu.clearCF();
|
||||
break;
|
||||
case "P":
|
||||
if (w) this.cpu.setPF(); else this.cpu.clearPF();
|
||||
break;
|
||||
case "A":
|
||||
if (w) this.cpu.setAF(); else this.cpu.clearAF();
|
||||
break;
|
||||
case "Z":
|
||||
if (w) this.cpu.setZF(); else this.cpu.clearZF();
|
||||
break;
|
||||
case "S":
|
||||
if (w) this.cpu.setSF(); else this.cpu.clearSF();
|
||||
break;
|
||||
case "I":
|
||||
if (w) this.cpu.setIF(); else this.cpu.clearIF();
|
||||
break;
|
||||
case "D":
|
||||
if (w) this.cpu.setDF(); else this.cpu.clearDF();
|
||||
break;
|
||||
case "V":
|
||||
if (w) this.cpu.setOF(); else this.cpu.clearOF();
|
||||
break;
|
||||
default:
|
||||
var fUnknown = true;
|
||||
if (this.cpu.model >= X86.MODEL_80286) {
|
||||
fUnknown = false;
|
||||
switch(sReg){
|
||||
case "MS":
|
||||
X86Help.opHelpLMSW(w);
|
||||
break;
|
||||
case "TR":
|
||||
this.cpu.segTSS.load(w);
|
||||
break;
|
||||
/*
|
||||
* TODO: Add support for GDTR (addr and limit), IDTR (addr and limit), and perhaps
|
||||
* even the ability to edit descriptor information associated with each segment register.
|
||||
*/
|
||||
default:
|
||||
fUnknown = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fUnknown) {
|
||||
this.println("unknown register: " + sReg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,10 @@ var X86 = {
|
|||
IF: 0x0200, // bit 9: Interrupt flag
|
||||
DF: 0x0400, // bit 10: Direction flag
|
||||
OF: 0x0800, // bit 11: Overflow flag
|
||||
IOPL: 0x3000, // 12-13: I/O Privilege Level, always set on 8086/80186, clear on 80286
|
||||
IOPL: {
|
||||
MASK: 0x3000, // 12-13: I/O Privilege Level (always set on 8086/80186, clear on 80286)
|
||||
SHIFT: 12
|
||||
},
|
||||
NT: 0x4000, // bit 14: Nested Task flag, always set on 8086/80186, clear on 80286
|
||||
BIT15: 0x8000 // bit 15: reserved, always set on 8086/80186, clear otherwise
|
||||
},
|
||||
|
|
@ -200,7 +203,7 @@ var X86 = {
|
|||
NOINTR: 0x0004, // indicates a segreg has been set, or a prefix, or an STI (delay INTR acknowledgement)
|
||||
SEG: 0x0010,
|
||||
LOCK: 0x0020,
|
||||
REPZ: 0x0040, // repeat while Z (NOTE: this value MUST match PS_ZF; see opCMPSb/opCMPSw/opSCASb/opSCASw)
|
||||
REPZ: 0x0040, // repeat while Z (NOTE: this value MUST match PS.ZF; see opCMPSb/opCMPSw/opSCASb/opSCASw)
|
||||
REPNZ: 0x0080, // repeat while NZ
|
||||
REPEAT: 0x0100, // this indicates that an instruction is being repeated (ie, some iteration AFTER the first)
|
||||
PUSHSP: 0x0200 // the SP register is potentially being referenced by a PUSH SP opcode, adjustment may be required
|
||||
|
|
@ -279,10 +282,13 @@ X86.PS.DIRECT = (X86.PS.TF | X86.PS.IF | X86.PS.DF);
|
|||
X86.PS.INDIRECT = (X86.PS.CF | X86.PS.PF | X86.PS.AF | X86.PS.ZF | X86.PS.SF | X86.PS.OF);
|
||||
|
||||
/*
|
||||
* NOTE: This is the default for 8086/8088; other processors must tweak these bits before
|
||||
* calling setPS(). TODO: Verify that PS_1 was always set on reset, even on the 8086/8088.
|
||||
* NOTE: These are the default "always set" PS bits for the 8086/8088; other processors must
|
||||
* adjust these bits accordingly. The final adjusted value is then stored in the X86CPU object
|
||||
* as "this.PS_SET"; setPS() must use that value, NOT this one.
|
||||
*
|
||||
* TODO: Verify that PS.BIT1 was always set on reset, even on the 8086/8088.
|
||||
*/
|
||||
X86.PS.SET = (X86.PS.BIT1 | X86.PS.IOPL | X86.PS.NT | X86.PS.BIT15);
|
||||
X86.PS.SET = (X86.PS.BIT1 | X86.PS.IOPL.MASK | X86.PS.NT | X86.PS.BIT15);
|
||||
|
||||
/*
|
||||
* getPS() brings all the direct and indirect flags together, and setPS() performs the
|
||||
|
|
@ -295,7 +301,7 @@ X86.PS.SET = (X86.PS.BIT1 | X86.PS.IOPL | X86.PS.NT | X86.PS.BIT15);
|
|||
* this.resultParitySign
|
||||
* this.resultAuxOverflow
|
||||
*
|
||||
* PS_SAHF is a subset of the arithmetic flags, and refers only to those flags that the
|
||||
* PS.SAHF is a subset of the arithmetic flags, and refers only to those flags that the
|
||||
* SAHF and LAHF "8080 legacy" opcodes affect.
|
||||
*/
|
||||
X86.PS.SAHF = (X86.PS.CF | X86.PS.PF | X86.PS.AF | X86.PS.ZF | X86.PS.SF);
|
||||
|
|
|
|||
|
|
@ -671,7 +671,7 @@ X86CPU.prototype.initProcessor = function()
|
|||
{
|
||||
this.PS_SET = X86.PS.SET;
|
||||
this.OPFLAG_NOINTR8086 = X86.OPFLAG.NOINTR;
|
||||
this.nShiftCountMask = 0xff; // on an 8086/8088, there effectively is NO mask
|
||||
this.nShiftCountMask = 0xff; // on an 8086/8088, all shift counts are used as-is
|
||||
|
||||
this.CYCLES = (this.model >= X86.MODEL_80286? X86CPU.CYCLES_80286 : X86CPU.CYCLES_8088);
|
||||
|
||||
|
|
@ -715,7 +715,7 @@ X86CPU.prototype.initProcessor = function()
|
|||
this.OPFLAG_NOINTR8086 = 0; // used with instructions that should *not* set NOINTR on an 80286 (eg, non-SS segment loads)
|
||||
this.aOps[0x0F] = X86OpXX.op0F;
|
||||
this.aOps[X86.OPCODE.ARPL] = X86OpXX.opARPL;
|
||||
this.aOps[X86.OPCODE.PUSHSP]= X86OpXX.op286PUSHSP;
|
||||
this.aOps[X86.OPCODE.PUSHSP]= X86OpXX.opPUSHSP;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -760,6 +760,9 @@ X86CPU.prototype.reset = function()
|
|||
* which takes both an offset and a segment, or setIP(), whichever is appropriate; in unusual cases where only
|
||||
* segCS is changing (eg, undocumented 8086 opcodes), use setCS().
|
||||
*
|
||||
* On the 80286, another "register" that mirrors segCS is nCPL: whenever CS is updated, nCPL is updated
|
||||
* with the CS selector's access level.
|
||||
*
|
||||
* The other segment registers (DS, SS and ES) have similar setters (for segDS, segSS and segES), but those
|
||||
* functions do not mirror any special segment:offset values in the same way that regEIP mirrors CS:IP.
|
||||
*
|
||||
|
|
@ -784,6 +787,7 @@ X86CPU.prototype.resetRegs = function()
|
|||
this.regMSW = X86.MSW.SET;
|
||||
this.addrIDT = 0; this.addrIDTLimit = 0x03FF;
|
||||
this.descIDT = {off: 0, sel: 0, acc: 0, maskPS: -1};
|
||||
this.nIOPL = 0; // this should be set before the first setPS() call
|
||||
|
||||
/*
|
||||
* Segment registers used to be defined as separate variables (eg, regCS and regCS0 stored the
|
||||
|
|
@ -795,7 +799,7 @@ X86CPU.prototype.resetRegs = function()
|
|||
this.segSS = new X86Seg(this, "SS");
|
||||
this.segES = new X86Seg(this, "ES");
|
||||
this.segZERO = new X86Seg(this, "ZERO");
|
||||
this.setCSIP(0, 0xFFFF);
|
||||
this.setCSIP(0, 0xFFFF); // this should be called before the first setPS() call
|
||||
|
||||
/*
|
||||
* Assorted 80286-specific registers. The GDTR and IDTR registers are stored as the following pieces:
|
||||
|
|
@ -822,7 +826,8 @@ X86CPU.prototype.resetRegs = function()
|
|||
}
|
||||
|
||||
/*
|
||||
* This resets the Processor Status flags (regPS), along with all the internal "result registers".
|
||||
* This resets the Processor Status flags (regPS), along with all the internal "result registers";
|
||||
* we've taken care to ensure that both nCPL and nIOPL are initialized before this first setPS() call.
|
||||
*/
|
||||
this.setPS(0);
|
||||
|
||||
|
|
@ -1019,7 +1024,7 @@ X86CPU.prototype.setProtMode = function(fProt)
|
|||
X86CPU.prototype.saveProtMode = function()
|
||||
{
|
||||
if (this.addrGDT != null) {
|
||||
return [this.regMSW, this.addrGDT, this.addrGDTLimit, this.addrIDT, this.addrIDTLimit, this.segLDT.save(), this.segTSS.save()];
|
||||
return [this.regMSW, this.addrGDT, this.addrGDTLimit, this.addrIDT, this.addrIDTLimit, this.segLDT.save(), this.segTSS.save(), this.nIOPL];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
|
@ -1042,6 +1047,8 @@ X86CPU.prototype.restoreProtMode = function(a)
|
|||
this.addrIDTLimit = a[4];
|
||||
this.segLDT.restore(a[5]);
|
||||
this.segTSS.restore(a[6]);
|
||||
this.nIOPL = a[7];
|
||||
this.nCPL = this.segCS.level;
|
||||
this.setProtMode();
|
||||
}
|
||||
};
|
||||
|
|
@ -1059,8 +1066,8 @@ X86CPU.prototype.restoreProtMode = function(a)
|
|||
X86CPU.prototype.save = function()
|
||||
{
|
||||
var state = new State(this);
|
||||
state.set(0, [this.regAX, this.regBX, this.regCX, this.regDX, this.regSP, this.regBP, this.regSI, this.regDI]);
|
||||
state.set(1, [this.regIP, this.segCS.save(), this.segDS.save(), this.segSS.save(), this.segES.save(), this.getPS(), this.saveProtMode()]);
|
||||
state.set(0, [this.regAX, this.regBX, this.regCX, this.regDX, this.regSP, this.regBP, this.regSI, this.regDI, this.nIOPL]);
|
||||
state.set(1, [this.regIP, this.segCS.save(), this.segDS.save(), this.segSS.save(), this.segES.save(), this.saveProtMode(), this.getPS()]);
|
||||
state.set(2, [this.segData.sName, this.segStack.sName, this.opFlags, this.opPrefixes, this.intFlags, this.regEA, this.regEAWrite]);
|
||||
state.set(3, [this.nBurstDivisor, this.nTotalCycles, this.getSpeed()]);
|
||||
state.set(4, this.bus.saveMemory());
|
||||
|
|
@ -1088,13 +1095,14 @@ X86CPU.prototype.restore = function(data)
|
|||
this.regBP = a[5];
|
||||
this.regSI = a[6];
|
||||
this.regDI = a[7];
|
||||
this.nIOPL = a[8] || 0;
|
||||
a = data[1];
|
||||
this.segCS.restore(a[1]);
|
||||
this.segDS.restore(a[2]);
|
||||
this.segSS.restore(a[3]);
|
||||
this.segES.restore(a[4]);
|
||||
this.setPS(a[5]);
|
||||
this.restoreProtMode(a[6]);
|
||||
this.restoreProtMode(a[5]);
|
||||
this.setPS(a[6]);
|
||||
this.setIP(a[0]);
|
||||
a = data[2];
|
||||
this.segData = this.getSeg(a[0]);
|
||||
|
|
@ -1240,6 +1248,7 @@ X86CPU.prototype.loadIDTEntry = function(nIDT)
|
|||
X86CPU.prototype.setCS = function(sel)
|
||||
{
|
||||
this.regEIP = this.segCS.load(sel) + this.regIP;
|
||||
this.nCPL = this.segCS.level;
|
||||
this.opFlags |= this.OPFLAG_NOINTR8086;
|
||||
if (PREFETCH) this.flushPrefetch(this.regEIP);
|
||||
};
|
||||
|
|
@ -1308,9 +1317,8 @@ X86CPU.prototype.setIP = function(off)
|
|||
* 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).
|
||||
*
|
||||
* As an aside, this function is called setCSIP() instead of setCSIP() to reflect the order of the parameters
|
||||
* (IP value first, CS value second), which matches the order that CS:IP values are normally stored in memory,
|
||||
* allowing us to make calls like this:
|
||||
* 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.setCSIP(this.popWord(), this.popWord());
|
||||
*
|
||||
|
|
@ -1322,6 +1330,7 @@ X86CPU.prototype.setCSIP = function(off, sel)
|
|||
{
|
||||
Component.assert((off & 0xffff) == off);
|
||||
this.regEIP = this.segCS.load(sel) + (this.regIP = off);
|
||||
this.nCPL = this.segCS.level;
|
||||
if (PREFETCH) this.flushPrefetch(this.regEIP);
|
||||
};
|
||||
|
||||
|
|
@ -1623,14 +1632,30 @@ X86CPU.prototype.getPS = function()
|
|||
*/
|
||||
X86CPU.prototype.setPS = function(regPS)
|
||||
{
|
||||
this.resultSize = X86.RESULT.SIZE_BYTE; // NOTE: We could have chosen SIZE_WORD, too; the choice here seems irrelevant
|
||||
this.resultSize = X86.RESULT.SIZE_BYTE; // NOTE: We could have chosen SIZE_WORD, too; it's irrelevant
|
||||
this.resultValue = this.resultParitySign = this.resultAuxOverflow = 0;
|
||||
|
||||
if (regPS & X86.PS.CF) this.setCF();
|
||||
if (!(regPS & X86.PS.PF)) this.resultParitySign |= 0x1;
|
||||
if (regPS & X86.PS.AF) this.resultAuxOverflow |= X86.RESULT.AUXOVF_AF;
|
||||
if (!(regPS & X86.PS.ZF)) this.clearZF();
|
||||
if (regPS & X86.PS.SF) this.setSF();
|
||||
if (regPS & X86.PS.OF) this.setOF();
|
||||
|
||||
/*
|
||||
* Since PS.IOPL and PS.IF are part of PS.DIRECT, we need to take care of any 80286-specific checks before
|
||||
* setting the PS.DIRECT bits. Specifically, PS.IOPL is unchanged if CPL > 0, and PS.IF is unchanged if CPL > IOPL.
|
||||
*/
|
||||
if (!this.nCPL) {
|
||||
this.nIOPL = (regPS & X86.PS.IOPL.MASK) >> X86.PS.IOPL.SHIFT; // IOPL allowed to change
|
||||
if (this.nIOPL && !(this.regMSW & X86.MSW.PE)) this.nIOPL = 0; // effective IOPL remains 0
|
||||
} else {
|
||||
regPS = (regPS & ~X86.PS.IOPL.MASK) | (this.regPS & X86.PS.IOPL.MASK); // IOPL not allowed to change
|
||||
}
|
||||
if (this.nCPL > this.nIOPL) {
|
||||
regPS = (regPS & ~X86.PS.IF) | (this.regPS & X86.PS.IF); // IF not allowed to change
|
||||
}
|
||||
|
||||
this.regPS = (this.regPS & ~X86.PS.DIRECT) | (regPS & X86.PS.DIRECT) | this.PS_SET;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -426,6 +426,31 @@ var X86Help = {
|
|||
* TODO: Now what?
|
||||
*/
|
||||
},
|
||||
/**
|
||||
* opHelpLMSW(w)
|
||||
*
|
||||
* Factored out of x86op0f.js, since both opLMSW and opLOADALL are capable of loading a new MSW.
|
||||
* The caller is responsible for assessing the appropriate cycle cost.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} w
|
||||
*/
|
||||
opHelpLMSW: function(w) {
|
||||
/*
|
||||
* This instruction is always allowed to set MSW.PE, but it cannot clear MSW.PE once set;
|
||||
* therefore, we always OR the previous value of MSW.PE into the new value before loading.
|
||||
*/
|
||||
w |= (this.regMSW & X86.MSW.PE);
|
||||
this.regMSW = (this.regMSW & X86.MSW.SET) | (w & ~X86.MSW.SET);
|
||||
/*
|
||||
* Since the 80286 cannot return to real-mode via this instruction, the only transition we
|
||||
* must worry about is to protected-mode. And don't worry, there's no harm calling setProtMode()
|
||||
* if the CPU is already in protected-mode (we could certainly optimize the call out in that
|
||||
* case, but this instruction isn't used frequently enough to warrant it).
|
||||
*/
|
||||
if (this.regMSW & X86.MSW.PE) this.setProtMode(true);
|
||||
|
||||
},
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -82,6 +82,79 @@ var X86Op0F = {
|
|||
opLSL: function() {
|
||||
X86Mods.aOpModsRegWord[this.getIPByte()].call(this, X86Help.opHelpLSL);
|
||||
},
|
||||
/**
|
||||
* opLOADALL()
|
||||
*
|
||||
* From the "Undocumented iAPX 286 Test Instruction" document at http://www.pcjs.org/pubs/pc/reference/intel/80286/loadall/:
|
||||
*
|
||||
* Physical Address (Hex) Associated CPU Register
|
||||
* 800-805 None
|
||||
* 806-807 MSW
|
||||
* 808-815 None
|
||||
* 816-817 TR
|
||||
* 818-819 Flag word
|
||||
* 81A-81B IP
|
||||
* 81C-81D LDT
|
||||
* 81E-81F DS
|
||||
* 820-821 SS
|
||||
* 822-823 CS
|
||||
* 824-825 ES
|
||||
* 826-827 DI
|
||||
* 828-829 SI
|
||||
* 82A-82B BP
|
||||
* 82C-82D SP
|
||||
* 82E-82F BX
|
||||
* 830-831 DX
|
||||
* 832-833 CX
|
||||
* 834-835 AX
|
||||
* 836-83B ES descriptor cache
|
||||
* 83C-841 CS descriptor cache
|
||||
* 842-847 SS descriptor cache
|
||||
* 848-84D DS descriptor cache
|
||||
* 84E-853 GDTR
|
||||
* 854-859 LDT descriptor cache
|
||||
* 85A-85F IDTR
|
||||
* 860-865 TSS descriptor cache
|
||||
*
|
||||
* Oddly, the above document gives two contradictory cycle counts for LOADALL: 190 and 195. I'll go with 195, for
|
||||
* no particular reason.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*
|
||||
* op=0x0F,0x05 (loadall)
|
||||
*/
|
||||
opLOADALL: function() {
|
||||
X86Help.opHelpLMSW.call(this, this.getWord(0x806));
|
||||
this.setPS(this.getWord(0x818));
|
||||
this.regDI = this.getWord(0x826);
|
||||
this.regSI = this.getWord(0x828);
|
||||
this.regBP = this.getWord(0x82A);
|
||||
this.regSP = this.getWord(0x82C);
|
||||
this.regBX = this.getWord(0x82E);
|
||||
this.regDX = this.getWord(0x830);
|
||||
this.regCX = this.getWord(0x832);
|
||||
this.regAX = this.getWord(0x834);
|
||||
/*
|
||||
* loadDesc() is an X86Seg class method that we must use to force the specified descriptor to be loaded;
|
||||
* since the processor might still be in real-mode, we can't use normal segment register instance methods.
|
||||
*/
|
||||
X86Seg.loadDesc.call(this.segES, this.getWord(0x824), 0x836);
|
||||
X86Seg.loadDesc.call(this.segCS, this.getWord(0x822), 0x83C);
|
||||
X86Seg.loadDesc.call(this.segSS, this.getWord(0x820), 0x842);
|
||||
X86Seg.loadDesc.call(this.segDS, this.getWord(0x81E), 0x848);
|
||||
this.nCPL = this.segCS.level;
|
||||
this.setIP(this.getWord(0x81A));
|
||||
/*
|
||||
* TODO: The bytes at 0x851 and 0x85D "should be zeroes", but do we rely on that, or should we load zeroes ourselves?
|
||||
*/
|
||||
this.addrGDT = this.getWord(0x84E) | (this.getWord(0x850) << 16);
|
||||
this.addrGDTLimit = this.addrGDT + this.getWord(0x852);
|
||||
this.segLDT.loadDesc(this.getWord(0x81C), 0x854);
|
||||
this.addrIDT = this.getWord(0x85A) | (this.getWord(0x85C) << 16);
|
||||
this.addrIDTLimit = this.addrIDT + this.getWord(0x85E);
|
||||
this.segTSS.loadDesc(this.getWord(0x816), 0x860);
|
||||
this.nStepCycles -= 195;
|
||||
},
|
||||
/**
|
||||
* @this {X86CPU}
|
||||
* @param {number} dst
|
||||
|
|
@ -244,8 +317,9 @@ var X86Op0F = {
|
|||
* 145E:4BC3 CB RETF
|
||||
*
|
||||
* This code is expecting SGDT on an 80286 to set the 6th "undefined" byte to 0xFF. So we use setWord()
|
||||
* instead of setByte() and force the upper byte to 0xFF. TODO: Remove the 0xFF00 below on post-80286
|
||||
* processors; also, this behavior may be unique to real-mode.
|
||||
* instead of setByte() and force the upper byte to 0xFF.
|
||||
*
|
||||
* TODO: Remove the 0xFF00 below on post-80286 processors; also, determine whether this behavior is unique to real-mode.
|
||||
*/
|
||||
this.setWord(this.regEA + 4, 0xFF00 | (this.addrGDT >> 16));
|
||||
this.nStepCycles -= 11;
|
||||
|
|
@ -270,8 +344,9 @@ var X86Op0F = {
|
|||
this.setWord(this.regEA + 2, this.addrIDT);
|
||||
/*
|
||||
* As with SGDT, the 6th byte is technically "undefined" on an 80286, but we now set it to 0xFF, for the
|
||||
* same reasons discussed in SGDT (above). TODO: Remove the 0xFF00 below on post-80286 processors; also,
|
||||
* this behavior may be unique to real-mode.
|
||||
* same reasons discussed in SGDT (above).
|
||||
*
|
||||
* TODO: Remove the 0xFF00 below on post-80286 processors; also, determine whether this behavior is unique to real-mode.
|
||||
*/
|
||||
this.setWord(this.regEA + 4, 0xFF00 | (this.addrIDT >> 16));
|
||||
this.nStepCycles -= 12;
|
||||
|
|
@ -341,17 +416,8 @@ var X86Op0F = {
|
|||
* @return {number}
|
||||
*/
|
||||
opLMSW: function(dst, src) {
|
||||
this.regMSW = (this.regMSW & X86.MSW.SET) | (dst & ~X86.MSW.SET);
|
||||
this.nStepCycles -= (3 + (this.regEA < 0? 0 : 3));
|
||||
/*
|
||||
* Since the 80286 did not allow you to disable protected-mode (ie, return to real-mode) by
|
||||
* CLEARING the X86.MSW.PE bit, we need only check for the bit being SET. And the only functions
|
||||
* that call setProtMode() are resetRegs() and this function, so there's no danger of the mode
|
||||
* getting out of sync with the X86.MSW.PE bit.
|
||||
*/
|
||||
if (this.regMSW & X86.MSW.PE) {
|
||||
this.setProtMode(true);
|
||||
}
|
||||
X86Help.opHelpLMSW.call(this, dst);
|
||||
this.nStepCycles -= (this.regEA < 0? 3 : 6);
|
||||
if (FASTDISABLE) this.setEAWord = this.setEAWordDisabled; else this.opFlags |= X86.OPFLAG.NOWRITE;
|
||||
return dst;
|
||||
}
|
||||
|
|
@ -359,7 +425,7 @@ var X86Op0F = {
|
|||
|
||||
X86Op0F.aOps0F = [
|
||||
X86Op0F.opGRP6, X86Op0F.opGRP7, X86Op0F.opLAR, X86Op0F.opLSL, // 0x00-0x03
|
||||
X86Help.opUndefined, X86Help.opUndefined, X86Help.opUndefined, X86Help.opUndefined, // 0x04-0x07
|
||||
X86Help.opUndefined, X86Op0F.opLOADALL, X86Help.opUndefined, X86Help.opUndefined, // 0x04-0x07
|
||||
/*
|
||||
* On all processors (except the 8086/8088, of course), 0x0F,0x0B is also referred to as "UD2": an
|
||||
* instruction guaranteed to raise a #UD (Invalid Opcode) exception (INT 0x06) on all future x86 processors.
|
||||
|
|
|
|||
|
|
@ -985,7 +985,7 @@ var X86OpXX = {
|
|||
*
|
||||
* op=0x54 (push SP)
|
||||
*/
|
||||
opPUSHSP: function() {
|
||||
opPUSHSP8086: function() {
|
||||
var w = (this.regSP - 2) & 0xffff;
|
||||
this.pushWord(w);
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesPushReg;
|
||||
|
|
@ -995,7 +995,7 @@ var X86OpXX = {
|
|||
*
|
||||
* op=0x54 (push SP)
|
||||
*/
|
||||
op286PUSHSP: function() {
|
||||
opPUSHSP: function() {
|
||||
this.pushWord(this.regSP);
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesPushReg;
|
||||
},
|
||||
|
|
@ -3385,7 +3385,7 @@ X86OpXX.aOps = [
|
|||
X86OpXX.opDECAX, X86OpXX.opDECCX, X86OpXX.opDECDX, X86OpXX.opDECBX, // 0x48-0x4B
|
||||
X86OpXX.opDECSP, X86OpXX.opDECBP, X86OpXX.opDECSI, X86OpXX.opDECDI, // 0x4C-0x4F
|
||||
X86OpXX.opPUSHAX, X86OpXX.opPUSHCX, X86OpXX.opPUSHDX, X86OpXX.opPUSHBX, // 0x50-0x53
|
||||
X86OpXX.opPUSHSP, X86OpXX.opPUSHBP, X86OpXX.opPUSHSI, X86OpXX.opPUSHDI, // 0x54-0x57
|
||||
X86OpXX.opPUSHSP8086, X86OpXX.opPUSHBP, X86OpXX.opPUSHSI, X86OpXX.opPUSHDI, // 0x54-0x57
|
||||
X86OpXX.opPOPAX, X86OpXX.opPOPCX, X86OpXX.opPOPDX, X86OpXX.opPOPBX, // 0x58-0x5B
|
||||
X86OpXX.opPOPSP, X86OpXX.opPOPBP, X86OpXX.opPOPSI, X86OpXX.opPOPDI, // 0x5C-0x5F
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ X86Seg.loadReal = function loadReal(sel, fSuppress)
|
|||
{
|
||||
this.sel = sel;
|
||||
this.limit = 0xffff; // TODO: Consider NOT setting the limit field in real-mode (unless it's required for, say, LOADALL support?)
|
||||
this.level = 0;
|
||||
return this.base = sel << 4;
|
||||
};
|
||||
|
||||
|
|
@ -110,51 +111,64 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
|
|||
addrDT = this.cpu.segLDT.base;
|
||||
addrDTLimit = this.cpu.segLDT.limit;
|
||||
}
|
||||
var offDT = addrDT + (sel & X86.SEL.MASK);
|
||||
if (offDT + 7 <= addrDTLimit) {
|
||||
this.checkRead = X86Seg.checkReadProtEnabled;
|
||||
this.checkWrite = X86Seg.checkWriteProtEnabled;
|
||||
|
||||
var offDesc = addrDT + (sel & X86.SEL.MASK);
|
||||
if (offDesc + 7 <= addrDTLimit) {
|
||||
/*
|
||||
* TODO: This is only the first of many steps toward accurately counting cycles in protected mode;
|
||||
* I simply noted that "POP segreg" takes 5 cycles in real mode and 20 in protected mode, so I'm
|
||||
* starting with a 15-cycle difference. Obviously the difference will be much greater when the load fails.
|
||||
*/
|
||||
this.cpu.nStepCycles -= 15;
|
||||
return X86Seg.loadDesc.call(this, sel, offDesc, true);
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
/*
|
||||
* TODO: Use (direct) Bus memory interfaces instead of (indirect) CPU memory interfaces here?
|
||||
*/
|
||||
var limit = this.cpu.getWord(offDT + X86.DESC.LIMIT.OFFSET);
|
||||
var acc = this.cpu.getWord(offDT + X86.DESC.ACC.OFFSET);
|
||||
var base = this.cpu.getWord(offDT + X86.DESC.BASE.OFFSET) | ((acc & X86.DESC.ACC.BASE1623) << 16);
|
||||
/**
|
||||
* loadDesc(sel, offDesc, fProt)
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} sel containing a selector
|
||||
* @param {number} offDesc is the physical address of a descriptor
|
||||
* @param {boolean} [fProt] is true if protected-mode, false if real-mode, undefined if TBD
|
||||
* @return {number} base address of selected segment, or -1 if error
|
||||
*/
|
||||
X86Seg.loadDesc = function(sel, offDesc, fProt)
|
||||
{
|
||||
var limit = this.cpu.getWord(offDesc + X86.DESC.LIMIT.OFFSET);
|
||||
var acc = this.cpu.getWord(offDesc + X86.DESC.ACC.OFFSET);
|
||||
var base = this.cpu.getWord(offDesc + X86.DESC.BASE.OFFSET) | ((acc & X86.DESC.ACC.BASE1623) << 16);
|
||||
|
||||
Component.assert(this.cpu.getWord(offDT + 0x06) == 0);
|
||||
Component.assert(this.cpu.getWord(offDesc + 0x06) == 0);
|
||||
|
||||
/*
|
||||
* For LSL (which uses fSuppress), we must support X86.DESC.ACC.TYPE.SEG as well as TSS and LDT.
|
||||
*/
|
||||
var accType = (acc & X86.DESC.ACC.TYPE.MASK);
|
||||
if (accType & X86.DESC.ACC.TYPE.SEG) {
|
||||
fProt = this.setProt(fProt);
|
||||
|
||||
/*
|
||||
* For LSL (which uses fSuppress), we must support X86.DESC.ACC.TYPE.SEG as well as TSS and LDT.
|
||||
*/
|
||||
var accType = (acc & X86.DESC.ACC.TYPE.MASK);
|
||||
|
||||
if (accType & X86.DESC.ACC.TYPE.SEG) {
|
||||
if (fProt) {
|
||||
if ((accType & X86.DESC.ACC.TYPE.CODE_READABLE) == X86.DESC.ACC.TYPE.CODE) {
|
||||
this.checkWrite = X86Seg.checkReadProtDisabled;
|
||||
}
|
||||
if ((accType & X86.DESC.ACC.TYPE.CODE) || !(accType & X86.DESC.ACC.TYPE.WRITEABLE)) {
|
||||
this.checkWrite = X86Seg.checkWriteProtDisabled;
|
||||
}
|
||||
this.sel = sel;
|
||||
this.limit = limit;
|
||||
this.acc = acc;
|
||||
this.level = (acc & X86.DESC.ACC.LEVEL.MASK) >> X86.DESC.ACC.LEVEL.SHIFT;
|
||||
return this.base = base;
|
||||
}
|
||||
else if (accType && accType <= X86.DESC.ACC.TYPE.TSS_BUSY) {
|
||||
this.sel = sel;
|
||||
this.limit = limit;
|
||||
this.acc = acc;
|
||||
this.level = (acc & X86.DESC.ACC.LEVEL.MASK) >> X86.DESC.ACC.LEVEL.SHIFT;
|
||||
return this.base = base;
|
||||
}
|
||||
this.sel = sel;
|
||||
this.limit = limit;
|
||||
this.acc = acc;
|
||||
this.level = (acc & X86.DESC.ACC.LEVEL.MASK) >> X86.DESC.ACC.LEVEL.SHIFT;
|
||||
return this.base = base;
|
||||
}
|
||||
else if (accType && accType <= X86.DESC.ACC.TYPE.TSS_BUSY) {
|
||||
this.sel = sel;
|
||||
this.limit = limit;
|
||||
this.acc = acc;
|
||||
this.level = (acc & X86.DESC.ACC.LEVEL.MASK) >> X86.DESC.ACC.LEVEL.SHIFT;
|
||||
return this.base = base;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
|
@ -321,8 +335,12 @@ X86Seg.prototype.setBase = function(addr)
|
|||
/**
|
||||
* setProt(fProt)
|
||||
*
|
||||
* This must be used to ensure that the segment register's state (ie, load and check methods) matches
|
||||
* the current operating mode (real or protected).
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {boolean} [fProt]
|
||||
* @return {boolean}
|
||||
*/
|
||||
X86Seg.prototype.setProt = function(fProt)
|
||||
{
|
||||
|
|
@ -338,6 +356,7 @@ X86Seg.prototype.setProt = function(fProt)
|
|||
this.checkRead = X86Seg.checkReadReal;
|
||||
this.checkWrite = X86Seg.checkWriteReal;
|
||||
}
|
||||
return fProt;
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined') module.exports = X86Seg;
|
||||
|
|
|
|||
|
|
@ -32,8 +32,4 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Unless we declare "module", even code like "if (typeof module === 'undefined') ..." is disallowed by the compiler.
|
||||
*/
|
||||
var module;
|
||||
var webkitAudioContext;
|
||||
var webkitAudioContext;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ iAPX 86 real mode programs.
|
|||
All CPU registers (including LDTR, TR, GDTR, IDTR, and MSW) are loaded from memory by this instruction.
|
||||
The normally hidden descriptor cache registers for the ES, DS, SS, CS, TR, and LDT registers are also loaded.
|
||||
LOADALL may be executed in either real address mode or protected mode (CPL must be 0). Any attempt to execute
|
||||
LOADALL at any privilege level other than 0 in protected rode causes exception 13 with an error code of 0.
|
||||
LOADALL at any privilege level other than 0 in protected mode causes exception 13 with an error code of 0.
|
||||
|
||||
LOADALL allows direct control over the base, limit, and access rights associated with each segment register.
|
||||
These values are kept in the descriptor cache registers which are normally hidden from programs. In protected mode,
|
||||
|
|
@ -147,4 +147,4 @@ For proper protected mode operation, the following is required:
|
|||
4. The DPL fields of the ES and DS descriptors should be 3 to prevent their being zeroed by RET or IRET
|
||||
instructions.
|
||||
|
||||
[This information is from an undated 15-page Intel document titled "Undocumented iAPX 286 Test Instruction"]
|
||||
[This information is from an undated 15-page Intel document titled "Undocumented iAPX 286 Test Instruction"]
|
||||
|
|
|
|||
Loading…
Reference in a new issue