OS/2 1.0 boots!
(at least through the first couple of screens)
This commit is contained in:
parent
2ff339f190
commit
019ce280f6
25 changed files with 6130 additions and 13 deletions
|
|
@ -1622,8 +1622,9 @@ X86CPU.prototype.getPS = function()
|
|||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} regPS
|
||||
* @param {number} [cpl]
|
||||
*/
|
||||
X86CPU.prototype.setPS = function(regPS)
|
||||
X86CPU.prototype.setPS = function(regPS, cpl)
|
||||
{
|
||||
this.resultSize = X86.RESULT.SIZE_BYTE; // NOTE: We could have chosen SIZE_WORD, too; it's irrelevant
|
||||
this.resultValue = this.resultParitySign = this.resultAuxOverflow = 0;
|
||||
|
|
@ -1638,7 +1639,7 @@ X86CPU.prototype.setPS = function(regPS)
|
|||
/*
|
||||
* OS/2 1.0 discriminates between an 80286 and an 80386 based on whether an IRET in real-mode that
|
||||
* pops 0xF000 into the flags is able to set *any* of flag bits 12-15: if it can, then OS/2 declares
|
||||
* the the CPU an 80386. Therefore, in real-mode, we must zero all incoming bits 12-15.
|
||||
* the CPU an 80386. Therefore, in real-mode, we must zero all incoming bits 12-15.
|
||||
*
|
||||
* This has the added benefit of relieving us from having to zero the effective IOPL (this.nIOPL)
|
||||
* whenever we're in real-mode, since we're zeroing the IOPL bits up front now.
|
||||
|
|
@ -1647,19 +1648,25 @@ X86CPU.prototype.setPS = function(regPS)
|
|||
regPS &= ~(X86.PS.IOPL.MASK | X86.PS.NT | X86.PS.BIT15);
|
||||
}
|
||||
|
||||
/*
|
||||
* There are some cases (eg, an IRET returning to a less privileged code segment) where the CPL
|
||||
* we compare against should come from the outgoing code segment, so if the caller provided it, use it.
|
||||
*/
|
||||
if (cpl === undefined) cpl = this.segCS.cpl;
|
||||
|
||||
/*
|
||||
* Since PS.IOPL and PS.IF are part of PS_DIRECT, we need to take care of any 80286-specific behaviors
|
||||
* before setting the PS_DIRECT bits from the incoming regPS bits.
|
||||
*
|
||||
* Specifically, PS.IOPL is unchanged if CPL > 0, and PS.IF is unchanged if CPL > IOPL.
|
||||
*/
|
||||
if (!this.segCS.cpl) {
|
||||
if (!cpl) {
|
||||
this.nIOPL = (regPS & X86.PS.IOPL.MASK) >> X86.PS.IOPL.SHIFT; // IOPL allowed to change
|
||||
} else {
|
||||
regPS = (regPS & ~X86.PS.IOPL.MASK) | (this.regPS & X86.PS.IOPL.MASK); // IOPL not allowed to change
|
||||
}
|
||||
|
||||
if (this.segCS.cpl > this.nIOPL) {
|
||||
if (cpl > this.nIOPL) {
|
||||
regPS = (regPS & ~X86.PS.IF) | (this.regPS & X86.PS.IF); // IF not allowed to change
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -498,11 +498,12 @@ var X86Help = {
|
|||
return;
|
||||
}
|
||||
}
|
||||
var cpl = this.segCS.cpl;
|
||||
var regIP = this.popWord();
|
||||
var regCS = this.popWord();
|
||||
var regPS = this.popWord();
|
||||
if (this.setCSIP(regIP, regCS, false) != null) {
|
||||
this.setPS(regPS);
|
||||
this.setPS(regPS, cpl);
|
||||
if (this.cIntReturn) this.checkIntReturn(this.regEIP);
|
||||
}
|
||||
},
|
||||
|
|
@ -535,6 +536,7 @@ var X86Help = {
|
|||
this.setIP(this.opEA - this.segCS.base);
|
||||
return;
|
||||
}
|
||||
|
||||
var fDispatch = false;
|
||||
if (this.model >= X86.MODEL_80186) {
|
||||
if (this.nFault < 0) {
|
||||
|
|
@ -560,9 +562,11 @@ var X86Help = {
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (X86Help.opHelpFaultMessage.call(this, nFault, nError, fHalt)) {
|
||||
fDispatch = false;
|
||||
}
|
||||
|
||||
if (fDispatch) X86Help.opHelpINT.call(this, this.nFault = nFault, nError, 0);
|
||||
|
||||
/*
|
||||
|
|
@ -573,7 +577,7 @@ var X86Help = {
|
|||
*
|
||||
* 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 the ultimate solution, which will involve throwing a special
|
||||
* 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.
|
||||
*/
|
||||
if (!EAFUNCS) {
|
||||
|
|
@ -586,9 +590,10 @@ var X86Help = {
|
|||
* Aside from giving the Debugger an opportunity to report every fault, this also gives us the ability to
|
||||
* halt exception processing in tracks: return true to prevent the fault handler from being dispatched.
|
||||
*
|
||||
* TODO: Provide the Debugger with some UI to control its "interference" with fault dispatching, and to
|
||||
* continue the dispatch after it has interfered. At the moment, your only option is to single-step over
|
||||
* the offending instruction, which will allow the fault to be dispatched, and then continue execution.
|
||||
* At the moment, the only Debugger control you have over fault interception is setting MESSAGE.FAULT, which
|
||||
* will display faults as they occur, and MESSAGE.HALT, which will halt after any Debugger message, including
|
||||
* MESSAGE.FAULT. If you want execution to continue after halting, clear MESSAGE.FAULT and/or MESSAGE.HALT,
|
||||
* or single-step over the offending instruction, which will allow the fault to be dispatched.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} nFault
|
||||
|
|
@ -601,8 +606,6 @@ var X86Help = {
|
|||
var bitsMessage = Debugger.MESSAGE.FAULT;
|
||||
var bOpcode = this.bus.getByteDirect(this.regEIP);
|
||||
|
||||
if (this.messageEnabled(bitsMessage)) fHalt = true;
|
||||
|
||||
/*
|
||||
* OS/2 1.0 uses an INT3 (0xCC) opcode in conjunction with an invalid IDT to trigger a triple-fault
|
||||
* reset and return to real-mode, and these resets happen quite frequently during boot; for example,
|
||||
|
|
@ -620,6 +623,7 @@ var X86Help = {
|
|||
fHalt = false;
|
||||
bitsMessage |= Debugger.MESSAGE.CPU;
|
||||
}
|
||||
|
||||
/*
|
||||
* Similarly, the PC AT ROM BIOS deliberately generates a couple of GP faults as part of the POST
|
||||
* (Power-On Self Test); we don't want to ignore those, but we don't want to halt on them either. We
|
||||
|
|
@ -629,16 +633,28 @@ var X86Help = {
|
|||
fHalt = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* However, the foregoing notwithstanding, if MESSAGE.HALT is enabled along with all the other required
|
||||
* MESSAGE bits, then we want to halt regardless.
|
||||
*/
|
||||
if (this.messageEnabled(bitsMessage | Debugger.MESSAGE.HALT)) {
|
||||
fHalt = true;
|
||||
}
|
||||
|
||||
if (this.messageEnabled(bitsMessage) || fHalt) {
|
||||
var sMessage = (fHalt? '\n' : '') + "Fault " + str.toHexByte(nFault) + (nError != null? " (" + str.toHexWord(nError) + ")" : "") + " on opcode 0x" + str.toHexByte(bOpcode) + " at " + str.toHexAddr(this.regIP, this.segCS.sel) + " (%" + str.toHex(this.regEIP, 6) + ")";
|
||||
var fRunning = this.bitField.fRunning;
|
||||
if (this.messageDebugger(sMessage)) {
|
||||
if (fHalt) {
|
||||
/*
|
||||
* By setting fHalt to fRunning (which is true while running but false while single-stepping),
|
||||
* this allows a fault to be dispatched when you single-step over a faulting instruction; you can
|
||||
* then continue single-stepping into the fault handler, or start running again.
|
||||
*
|
||||
* Note that we had to capture fRunning before calling messageDebugger(), because if MESSAGE.HALT
|
||||
* is set, messageDebugger() will have already halted the CPU.
|
||||
*/
|
||||
fHalt = this.bitField.fRunning;
|
||||
fHalt = fRunning;
|
||||
this.dbg.stopCPU();
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -677,7 +677,29 @@ X86Seg.prototype.loadDesc8 = function(addrDesc, sel, fSuppress)
|
|||
else if (this.id == X86Seg.ID.DATA) {
|
||||
if (selMasked) {
|
||||
if (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);
|
||||
/*
|
||||
* OS/2 1.0 triggers this GP fault (what I'll call the "Empty Descriptor" GP fault) multiple times
|
||||
* during boot; eg:
|
||||
*
|
||||
* Fault 0D (002F) on opcode 0x8E at 3190:3A05 (%112625)
|
||||
* stopped (11315208 ops, 41813627 cycles, 498270 ms, 83918 hz)
|
||||
* AX=0000 BX=0970 CX=0300 DX=0300 SP=0ABE BP=0ABA SI=0000 DI=001A
|
||||
* DS=19C0[177300,2C5F] ES=001F[1743A0,07FF] SS=0038[175CE0,0B5F]
|
||||
* CS=3190[10EC20,B89F] IP=3A05 V0 D0 I1 T0 S0 Z1 A0 P1 C0 PS=3246 MS=FFF3
|
||||
* LD=0028[174BC0,003F] GD=[11A4E0,490F] ID=[11F61A,03FF] TR=0010 A20=ON
|
||||
* 3190:3A05 8E4604 MOV ES,[BP+04]
|
||||
* 0038:0ABE 002F 19C0 0000 067C - 07FC 0AD2 0010 C420 /.....|....... .
|
||||
* dumpDesc(002F): %174BE8
|
||||
* base=000000 limit=0000 dpl=00 type=00 (undefined)
|
||||
*
|
||||
* If we allow the GP fault to be dispatched, it recovers, so until I'm able to investigate this
|
||||
* further, I'm going to assume this is normal behavior. If the segment (0x002F in the example)
|
||||
* simply needed to be "faulted" into memory, I would have expected OS/2 to build a descriptor
|
||||
* with the PRESENT bit clear, and rely on NP faults rather than GP faults, but maybe this was simpler.
|
||||
*
|
||||
* 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;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue