Protected-mode interrupt/trap gate fixes
This commit is contained in:
parent
6194aac5af
commit
311868bc95
19 changed files with 442 additions and 407 deletions
|
|
@ -57,8 +57,8 @@ function C1PCPU(parmsCPU)
|
|||
Component.call(this, "C1PCPU", parmsCPU);
|
||||
|
||||
this.clearRegs();
|
||||
this.aFlags.fPowered = false;
|
||||
this.aFlags.fRunning = false;
|
||||
this.bitField.fPowered = false;
|
||||
this.bitField.fRunning = false;
|
||||
this.fAutoStart = parmsCPU["autoStart"];
|
||||
|
||||
/*
|
||||
|
|
@ -482,8 +482,9 @@ Component.subclass(Component, C1PCPU);
|
|||
*/
|
||||
C1PCPU.prototype.reset = function(fPowerOn)
|
||||
{
|
||||
if (this.aFlags.fRunning)
|
||||
if (this.bitField.fRunning) {
|
||||
this.halt();
|
||||
}
|
||||
this.clearRegs();
|
||||
this.regPC = this.getWord(this.VECTOR_RESET);
|
||||
this.clearError(); // clear any fatal error/exception
|
||||
|
|
@ -515,10 +516,11 @@ C1PCPU.prototype.setBinding = function(sHTMLType, sBinding, control)
|
|||
this.bindings[sBinding] = control;
|
||||
control.onclick = function(cpu) {
|
||||
return function() {
|
||||
if (!cpu.aFlags.fRunning)
|
||||
if (!cpu.bitField.fRunning) {
|
||||
cpu.run();
|
||||
else
|
||||
} else {
|
||||
cpu.halt();
|
||||
}
|
||||
};
|
||||
}(this);
|
||||
fBound = true;
|
||||
|
|
@ -578,7 +580,7 @@ C1PCPU.prototype.setBuffer = function(abMemory, start, end)
|
|||
*/
|
||||
C1PCPU.prototype.setPower = function(fOn, cmp)
|
||||
{
|
||||
if (fOn && !this.aFlags.fPowered) {
|
||||
if (fOn && !this.bitField.fPowered) {
|
||||
this.cmp = cmp;
|
||||
/*
|
||||
* Attach the Debugger, if any, to the CPU, so that the CPU can periodically
|
||||
|
|
@ -606,7 +608,7 @@ C1PCPU.prototype.setPower = function(fOn, cmp)
|
|||
};
|
||||
}(video);
|
||||
}
|
||||
this.aFlags.fPowered = true;
|
||||
this.bitField.fPowered = true;
|
||||
this.reset(true);
|
||||
this.update();
|
||||
}
|
||||
|
|
@ -879,7 +881,7 @@ C1PCPU.prototype.displayStatus = function()
|
|||
*/
|
||||
C1PCPU.prototype.isRunning = function()
|
||||
{
|
||||
return this.aFlags.fRunning;
|
||||
return this.bitField.fRunning;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1050,7 +1052,7 @@ C1PCPU.prototype.run = function()
|
|||
if (this.cmp) this.cmp.stop(this.msRunStart, this.nRunCycles);
|
||||
return;
|
||||
}
|
||||
if (!this.aFlags.fRunning) {
|
||||
if (!this.bitField.fRunning) {
|
||||
/*
|
||||
* setSpeed() without a speed parameter leaves the selected speed in place, but also resets the
|
||||
* cycle counter and timestamp for the current series of run() calls, calculates the maximum number
|
||||
|
|
@ -1059,7 +1061,7 @@ C1PCPU.prototype.run = function()
|
|||
*/
|
||||
this.setSpeed();
|
||||
if (this.cmp) this.cmp.start();
|
||||
this.aFlags.fRunning = true;
|
||||
this.bitField.fRunning = true;
|
||||
if (this.bindings["run"]) this.bindings["run"].innerHTML = "Halt";
|
||||
this.setFocus();
|
||||
}
|
||||
|
|
@ -1106,7 +1108,7 @@ C1PCPU.prototype.run = function()
|
|||
this.nCyclesNextYield += this.nCyclesPerYield;
|
||||
break;
|
||||
}
|
||||
} while (this.aFlags.fRunning);
|
||||
} while (this.bitField.fRunning);
|
||||
}
|
||||
catch (e) {
|
||||
this.halt();
|
||||
|
|
@ -1267,8 +1269,8 @@ C1PCPU.prototype.halt = function()
|
|||
this.isBusy(true);
|
||||
this.nBurstCycles -= this.nStepCycles;
|
||||
this.nStepCycles = 0;
|
||||
if (this.aFlags.fRunning) {
|
||||
this.aFlags.fRunning = false;
|
||||
if (this.bitField.fRunning) {
|
||||
this.bitField.fRunning = false;
|
||||
if (this.bindings["run"]) this.bindings["run"].innerHTML = "Run";
|
||||
}
|
||||
};
|
||||
|
|
@ -1302,7 +1304,7 @@ C1PCPU.prototype.update = function()
|
|||
*/
|
||||
C1PCPU.prototype.getCycles = function()
|
||||
{
|
||||
return (this.aFlags.fRunning? this.nRunCycles + this.nBurstCycles - this.nStepCycles : 0);
|
||||
return (this.bitField.fRunning? this.nRunCycles + this.nBurstCycles - this.nStepCycles : 0);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -578,8 +578,8 @@ if (DEBUGGER) {
|
|||
*/
|
||||
C1PDebugger.prototype.setPower = function(fOn, cmp)
|
||||
{
|
||||
if (fOn && !this.aFlags.fPowered) {
|
||||
this.aFlags.fPowered = true;
|
||||
if (fOn && !this.bitField.fPowered) {
|
||||
this.bitField.fPowered = true;
|
||||
this.cpu = cmp.getComponentByType("cpu");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ function C1PDiskController(parmsDC)
|
|||
{
|
||||
Component.call(this, "C1PDiskController", parmsDC);
|
||||
|
||||
this.aFlags.fPowered = false;
|
||||
this.bitField.fPowered = false;
|
||||
|
||||
/*
|
||||
* Our DiskController simulates the combination of an MC6820 PIA and an MC6850 ACIA.
|
||||
|
|
@ -735,8 +735,8 @@ C1PDiskController.prototype.setBuffer = function(abMemory, start, end, cpu)
|
|||
*/
|
||||
C1PDiskController.prototype.setPower = function(fOn, cmp)
|
||||
{
|
||||
if (fOn && !this.aFlags.fPowered) {
|
||||
this.aFlags.fPowered = true;
|
||||
if (fOn && !this.bitField.fPowered) {
|
||||
this.bitField.fPowered = true;
|
||||
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ function C1PKeyboard(parmsKbd)
|
|||
{
|
||||
Component.call(this, "C1PKeyboard", parmsKbd);
|
||||
|
||||
this.aFlags.fPowered = false;
|
||||
this.bitField.fPowered = false;
|
||||
this.nDefaultModel = parmsKbd['model'];
|
||||
|
||||
/*
|
||||
|
|
@ -515,8 +515,8 @@ C1PKeyboard.prototype.setModel = function(nModel)
|
|||
*/
|
||||
C1PKeyboard.prototype.setPower = function(fOn, cmp)
|
||||
{
|
||||
if (fOn && !this.aFlags.fPowered) {
|
||||
this.aFlags.fPowered = true;
|
||||
if (fOn && !this.bitField.fPowered) {
|
||||
this.bitField.fPowered = true;
|
||||
this.cmp = cmp;
|
||||
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ function C1PPanel(parmsPanel)
|
|||
{
|
||||
Component.call(this, "C1PPanel", parmsPanel);
|
||||
|
||||
this.aFlags.fPowered = false;
|
||||
this.bitField.fPowered = false;
|
||||
}
|
||||
|
||||
Component.subclass(Component, C1PPanel);
|
||||
|
|
@ -76,8 +76,8 @@ C1PPanel.prototype.setBinding = function(sHTMLType, sBinding, control)
|
|||
*/
|
||||
C1PPanel.prototype.setPower = function(fOn, cmp)
|
||||
{
|
||||
if (fOn && !this.aFlags.fPowered) {
|
||||
this.aFlags.fPowered = true;
|
||||
if (fOn && !this.bitField.fPowered) {
|
||||
this.bitField.fPowered = true;
|
||||
this.cmp = cmp;
|
||||
this.cpu = cmp.getComponentByType("cpu");
|
||||
this.kbd = cmp.getComponentByType("keyboard");
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ C1PROM.prototype.setBuffer = function(abMemory, start, end, cpu)
|
|||
*/
|
||||
C1PROM.prototype.setPower = function(fOn, cmp)
|
||||
{
|
||||
if (fOn && !this.aFlags.fPowered) {
|
||||
this.aFlags.fPowered = true;
|
||||
if (fOn && !this.bitField.fPowered) {
|
||||
this.bitField.fPowered = true;
|
||||
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ function C1PSerialPort(parmsSerial)
|
|||
{
|
||||
Component.call(this, "C1PSerialPort", parmsSerial);
|
||||
|
||||
this.aFlags.fPowered = false;
|
||||
this.bitField.fPowered = false;
|
||||
this.fDemo = parmsSerial['demo'];
|
||||
|
||||
this.STATUS_NONE = 0x00;
|
||||
|
|
@ -202,8 +202,8 @@ C1PSerialPort.prototype.setBuffer = function(abMemory, start, end, cpu)
|
|||
*/
|
||||
C1PSerialPort.prototype.setPower = function(fOn, cmp)
|
||||
{
|
||||
if (fOn && !this.aFlags.fPowered) {
|
||||
this.aFlags.fPowered = true;
|
||||
if (fOn && !this.bitField.fPowered) {
|
||||
this.bitField.fPowered = true;
|
||||
this.cmp = cmp;
|
||||
this.kbd = cmp.getComponentByType("keyboard");
|
||||
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");
|
||||
|
|
|
|||
|
|
@ -307,8 +307,8 @@ C1PVideo.prototype.setPower = function(fOn, cmp)
|
|||
* it ourselves, too. This also means that updateScreen() need check only fPower and not isReady(),
|
||||
* since we guarantee that the former implies the latter.
|
||||
*/
|
||||
if (fOn && !this.aFlags.fPowered && this.isReady()) {
|
||||
this.aFlags.fPowered = true;
|
||||
if (fOn && !this.bitField.fPowered && this.isReady()) {
|
||||
this.bitField.fPowered = true;
|
||||
if (DEBUGGER) this.dbg = cmp.getComponentByType("debugger");
|
||||
/*
|
||||
* If we have an associated keyboard, then ensure that the keyboard will be notified whenever
|
||||
|
|
@ -327,8 +327,8 @@ C1PVideo.prototype.setPower = function(fOn, cmp)
|
|||
}
|
||||
}
|
||||
else
|
||||
if (!fOn && this.aFlags.fPowered) {
|
||||
this.aFlags.fPowered = false;
|
||||
if (!fOn && this.bitField.fPowered) {
|
||||
this.bitField.fPowered = false;
|
||||
/*
|
||||
* This is where we would add some method of blanking the display, without the disturbing the video
|
||||
* buffer contents, and blocking all further updates to the display.
|
||||
|
|
@ -441,7 +441,7 @@ C1PVideo.prototype.initScreen = function()
|
|||
C1PVideo.prototype.updateScreen = function()
|
||||
{
|
||||
var offset = 0;
|
||||
if (this.aFlags.fPowered) {
|
||||
if (this.bitField.fPowered) {
|
||||
while (offset < this.cbScreen) {
|
||||
var b = this.abMem[this.offVideo + offset];
|
||||
if (this.abScreen[offset] != b) {
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
|
|||
|
||||
Component.call(this, "Computer", parmsComputer, Computer);
|
||||
|
||||
this.aFlags.fPowered = false;
|
||||
this.bitField.fPowered = false;
|
||||
this.nBusWidth = parmsComputer['buswidth'];
|
||||
this.resume = Computer.RESUME_NONE;
|
||||
this.sStateData = null;
|
||||
|
|
@ -537,9 +537,9 @@ Computer.prototype.powerOn = function(resume)
|
|||
*/
|
||||
Computer.prototype.powerRestore = function(component, stateComputer, fRepower, fRestore)
|
||||
{
|
||||
if (!component.aFlags.fPowered) {
|
||||
if (!component.bitField.fPowered) {
|
||||
|
||||
component.aFlags.fPowered = true;
|
||||
component.bitField.fPowered = true;
|
||||
|
||||
if (component.powerUp) {
|
||||
|
||||
|
|
@ -642,9 +642,9 @@ Computer.prototype.donePowerOn = function(aParms)
|
|||
var fRepower = (aParms[1] < 0);
|
||||
var fRestore = aParms[2];
|
||||
|
||||
if (DEBUG && this.aFlags.fPowered) this.messageDebugger("Computer.donePowerOn(): redundant");
|
||||
if (DEBUG && this.bitField.fPowered) this.messageDebugger("Computer.donePowerOn(): redundant");
|
||||
|
||||
this.aFlags.fPowered = true;
|
||||
this.bitField.fPowered = true;
|
||||
|
||||
if (!this.fInitialized) {
|
||||
this.println(Computer.sAppName + " v" + Computer.sAppVer + "\n" + Computer.sCopyright + "\n" + Computer.LICENSE);
|
||||
|
|
@ -747,7 +747,7 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
|
|||
data = this.cpu.powerDown(fSave, fShutdown);
|
||||
if (typeof data === "object") stateComputer.set(this.cpu.id, data);
|
||||
if (fShutdown) {
|
||||
this.cpu.aFlags.fPowered = false;
|
||||
this.cpu.bitField.fPowered = false;
|
||||
if (data === false) sState = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -755,13 +755,13 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
|
|||
var aComponents = Component.getComponents(this.id);
|
||||
for (var iComponent = 0; iComponent < aComponents.length; iComponent++) {
|
||||
var component = aComponents[iComponent];
|
||||
if (component.aFlags.fPowered) {
|
||||
if (component.bitField.fPowered) {
|
||||
if (component.powerDown) {
|
||||
data = component.powerDown(fSave, fShutdown);
|
||||
if (typeof data === "object") stateComputer.set(component.id, data);
|
||||
}
|
||||
if (fShutdown) {
|
||||
component.aFlags.fPowered = false;
|
||||
component.bitField.fPowered = false;
|
||||
if (data === false) sState = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -813,7 +813,7 @@ Computer.prototype.powerOff = function(fSave, fShutdown)
|
|||
}
|
||||
}
|
||||
|
||||
if (fShutdown) this.aFlags.fPowered = false;
|
||||
if (fShutdown) this.bitField.fPowered = false;
|
||||
|
||||
return sState;
|
||||
};
|
||||
|
|
@ -1213,7 +1213,7 @@ Computer.init = function()
|
|||
*/
|
||||
var computer = new Computer(parmsComputer, parmsMachine, true);
|
||||
|
||||
if (DEBUG) computer.messageDebugger("onInit(" + computer.aFlags.fPowered + ")");
|
||||
if (DEBUG) computer.messageDebugger("onInit(" + computer.bitField.fPowered + ")");
|
||||
|
||||
/*
|
||||
* For now, all we support are "reset" and "save" buttons. We may eventually add a "power"
|
||||
|
|
@ -1243,9 +1243,9 @@ Computer.show = function()
|
|||
var computer = Component.getComponentByType("Computer", parmsComputer['id']);
|
||||
if (computer) {
|
||||
|
||||
if (DEBUG) computer.messageDebugger("onShow(" + computer.fInitialized + "," + computer.aFlags.fPowered + ")");
|
||||
if (DEBUG) computer.messageDebugger("onShow(" + computer.fInitialized + "," + computer.bitField.fPowered + ")");
|
||||
|
||||
if (computer.fInitialized && !computer.aFlags.fPowered) {
|
||||
if (computer.fInitialized && !computer.bitField.fPowered) {
|
||||
/**
|
||||
* Repower the computer, notifying every component to continue running as-is.
|
||||
*/
|
||||
|
|
@ -1290,9 +1290,9 @@ Computer.exit = function()
|
|||
var computer = Component.getComponentByType("Computer", parmsComputer['id']);
|
||||
if (computer) {
|
||||
|
||||
if (DEBUG) computer.messageDebugger("onExit(" + computer.aFlags.fPowered + ")");
|
||||
if (DEBUG) computer.messageDebugger("onExit(" + computer.bitField.fPowered + ")");
|
||||
|
||||
if (computer.aFlags.fPowered) {
|
||||
if (computer.bitField.fPowered) {
|
||||
/**
|
||||
* Power "down" the computer, giving every component an opportunity to save its state,
|
||||
* but only if 'resume' has been set AND there is no valid resume path (because if a valid resume
|
||||
|
|
|
|||
|
|
@ -96,15 +96,15 @@ function CPU(parmsCPU, nCyclesDefault)
|
|||
*/
|
||||
this.aCounts.mhzTarget = this.aCounts.mhzDefault * this.aCounts.nCyclesMultiplier;
|
||||
|
||||
this.aFlags.fPowered = false;
|
||||
this.aFlags.fRunning = false;
|
||||
this.aFlags.fStarting = false;
|
||||
this.aFlags.fAutoStart = parmsCPU['autoStart'];
|
||||
this.bitField.fPowered = false;
|
||||
this.bitField.fRunning = false;
|
||||
this.bitField.fStarting = false;
|
||||
this.bitField.fAutoStart = parmsCPU['autoStart'];
|
||||
|
||||
/*
|
||||
* TODO: Add some UI for fDisplayLiveRegs (either an XML property, or a UI checkbox, or both)
|
||||
*/
|
||||
this.aFlags.fDisplayLiveRegs = false;
|
||||
this.bitField.fDisplayLiveRegs = false;
|
||||
|
||||
/*
|
||||
* Provide a power-saving URL-based way of overriding the 'autostart' setting;
|
||||
|
|
@ -113,7 +113,7 @@ function CPU(parmsCPU, nCyclesDefault)
|
|||
*/
|
||||
var sAutoStart = Component.parmsURL['autostart'];
|
||||
if (sAutoStart !== undefined) {
|
||||
this.aFlags.fAutoStart = (sAutoStart == "true"? true : (sAutoStart == "false"? false : null));
|
||||
this.bitField.fAutoStart = (sAutoStart == "true"? true : (sAutoStart == "false"? false : null));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -125,7 +125,7 @@ function CPU(parmsCPU, nCyclesDefault)
|
|||
* command ("x"); for example, "x cs int 5000" will set nCyclesChecksumInterval to 5000
|
||||
* and call resetChecksum().
|
||||
*/
|
||||
this.aFlags.fChecksum = false;
|
||||
this.bitField.fChecksum = false;
|
||||
this.aCounts.nChecksum = this.aCounts.nCyclesChecksumNext = 0;
|
||||
this.aCounts.nCyclesChecksumStart = parmsCPU["csStart"];
|
||||
this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"];
|
||||
|
|
@ -272,7 +272,7 @@ CPU.prototype.powerUp = function(data, fRepower)
|
|||
this.println("No debugger detected");
|
||||
}
|
||||
}
|
||||
this.aFlags.fPowered = true;
|
||||
this.bitField.fPowered = true;
|
||||
if (!this.autoStart() && this.dbg) this.dbg.updateStatus();
|
||||
this.updateCPU();
|
||||
return true;
|
||||
|
|
@ -287,7 +287,7 @@ CPU.prototype.powerUp = function(data, fRepower)
|
|||
*/
|
||||
CPU.prototype.powerDown = function(fSave)
|
||||
{
|
||||
this.aFlags.fPowered = false;
|
||||
this.bitField.fPowered = false;
|
||||
return fSave && this.save ? this.save() : true;
|
||||
};
|
||||
|
||||
|
|
@ -299,7 +299,7 @@ CPU.prototype.powerDown = function(fSave)
|
|||
*/
|
||||
CPU.prototype.autoStart = function()
|
||||
{
|
||||
if (this.aFlags.fAutoStart === true || this.aFlags.fAutoStart === null && (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
|
||||
if (this.bitField.fAutoStart === true || this.bitField.fAutoStart === null && (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
|
||||
this.runCPU(); // start running automatically on power-up, assuming there's no Debugger
|
||||
return true;
|
||||
}
|
||||
|
|
@ -326,7 +326,7 @@ CPU.prototype.setFocus = function()
|
|||
*/
|
||||
CPU.prototype.isPowered = function()
|
||||
{
|
||||
if (!this.aFlags.fPowered) {
|
||||
if (!this.bitField.fPowered) {
|
||||
this.println(this.toString() + " not powered");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -341,7 +341,7 @@ CPU.prototype.isPowered = function()
|
|||
*/
|
||||
CPU.prototype.isRunning = function()
|
||||
{
|
||||
return this.aFlags.fRunning;
|
||||
return this.bitField.fRunning;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -372,8 +372,8 @@ CPU.prototype.resetChecksum = function()
|
|||
if (this.aCounts.nCyclesChecksumStart === undefined) this.aCounts.nCyclesChecksumStart = 0;
|
||||
if (this.aCounts.nCyclesChecksumInterval === undefined) this.aCounts.nCyclesChecksumInterval = -1;
|
||||
if (this.aCounts.nCyclesChecksumStop === undefined) this.aCounts.nCyclesChecksumStop = -1;
|
||||
this.aFlags.fChecksum = (this.aCounts.nCyclesChecksumStart >= 0 && this.aCounts.nCyclesChecksumInterval > 0);
|
||||
if (this.aFlags.fChecksum) {
|
||||
this.bitField.fChecksum = (this.aCounts.nCyclesChecksumStart >= 0 && this.aCounts.nCyclesChecksumInterval > 0);
|
||||
if (this.bitField.fChecksum) {
|
||||
this.aCounts.nChecksum = 0;
|
||||
this.aCounts.nCyclesChecksumNext = this.aCounts.nCyclesChecksumStart - this.nTotalCycles;
|
||||
// this.aCounts.nCyclesChecksumNext = this.aCounts.nCyclesChecksumStart + this.aCounts.nCyclesChecksumInterval - (this.nTotalCycles % this.aCounts.nCyclesChecksumInterval);
|
||||
|
|
@ -395,7 +395,7 @@ CPU.prototype.resetChecksum = function()
|
|||
*/
|
||||
CPU.prototype.updateChecksum = function(nCycles)
|
||||
{
|
||||
if (this.aFlags.fChecksum) {
|
||||
if (this.bitField.fChecksum) {
|
||||
/*
|
||||
* Get a 32-bit summation of the current CPU state and add it to our running 32-bit checksum
|
||||
*/
|
||||
|
|
@ -449,7 +449,7 @@ CPU.prototype.displayReg = function(sReg, nVal, cch)
|
|||
this.stopCPU();
|
||||
}
|
||||
var sVal;
|
||||
if (!this.aFlags.fRunning || this.aFlags.fDisplayLiveRegs) {
|
||||
if (!this.bitField.fRunning || this.bitField.fDisplayLiveRegs) {
|
||||
sVal = str.toHex(nVal, cch);
|
||||
} else {
|
||||
sVal = "----".substr(0, cch);
|
||||
|
|
@ -505,7 +505,7 @@ CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
|
|||
case "run":
|
||||
this.bindings[sBinding] = control;
|
||||
control.onclick = function onClickRun() {
|
||||
if (!cpu.aFlags.fRunning)
|
||||
if (!cpu.bitField.fRunning)
|
||||
cpu.runCPU(true);
|
||||
else
|
||||
cpu.stopCPU(true);
|
||||
|
|
@ -559,7 +559,7 @@ CPU.prototype.setBinding = function(sHTMLType, sBinding, control)
|
|||
*/
|
||||
CPU.prototype.setBurstCycles = function(nCycles)
|
||||
{
|
||||
if (this.aFlags.fRunning) {
|
||||
if (this.bitField.fRunning) {
|
||||
var nDelta = this.nStepCycles - nCycles;
|
||||
/*
|
||||
* NOTE: If nDelta is negative, we will actually be increasing nStepCycles and nBurstCycles.
|
||||
|
|
@ -744,7 +744,7 @@ CPU.prototype.getSpeedCurrent = function() {
|
|||
/*
|
||||
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
|
||||
*/
|
||||
return ((this.aFlags.fRunning && this.aCounts.mhz)? (this.aCounts.mhz.toFixed(2) + "Mhz") : "Stopped");
|
||||
return ((this.bitField.fRunning && this.aCounts.mhz)? (this.aCounts.mhz.toFixed(2) + "Mhz") : "Stopped");
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -967,7 +967,7 @@ CPU.prototype.runCPU = function(fOnClick)
|
|||
this.calcStartTime();
|
||||
try {
|
||||
do {
|
||||
var nCyclesPerBurst = (this.aFlags.fChecksum? 1 : this.aCounts.nCyclesPerBurst);
|
||||
var nCyclesPerBurst = (this.bitField.fChecksum? 1 : this.aCounts.nCyclesPerBurst);
|
||||
|
||||
if (this.chipset) {
|
||||
this.chipset.updateAllTimers();
|
||||
|
|
@ -1009,7 +1009,7 @@ CPU.prototype.runCPU = function(fOnClick)
|
|||
this.aCounts.nCyclesNextYield += this.aCounts.nCyclesPerYield;
|
||||
break;
|
||||
}
|
||||
} while (this.aFlags.fRunning);
|
||||
} while (this.bitField.fRunning);
|
||||
}
|
||||
catch (e) {
|
||||
this.stopCPU();
|
||||
|
|
@ -1031,7 +1031,7 @@ CPU.prototype.runCPU = function(fOnClick)
|
|||
*/
|
||||
CPU.prototype.startCPU = function(fSetFocus)
|
||||
{
|
||||
if (!this.aFlags.fRunning) {
|
||||
if (!this.bitField.fRunning) {
|
||||
/*
|
||||
* setSpeed() without a speed parameter leaves the selected speed in place, but also resets the
|
||||
* cycle counter and timestamp for the current series of runCPU() calls, calculates the maximum number
|
||||
|
|
@ -1040,8 +1040,8 @@ CPU.prototype.startCPU = function(fSetFocus)
|
|||
*/
|
||||
this.setSpeed();
|
||||
if (this.cmp) this.cmp.start(this.aCounts.msStartRun, this.getCycles());
|
||||
this.aFlags.fRunning = true;
|
||||
this.aFlags.fStarting = true;
|
||||
this.bitField.fRunning = true;
|
||||
this.bitField.fStarting = true;
|
||||
if (this.chipset) this.chipset.setSpeaker();
|
||||
var controlRun = this.bindings["run"];
|
||||
if (controlRun) controlRun.textContent = "Halt";
|
||||
|
|
@ -1082,13 +1082,13 @@ CPU.prototype.stopCPU = function(fComplete)
|
|||
this.nStepCycles = 0;
|
||||
this.addCycles(this.nRunCycles);
|
||||
this.nRunCycles = 0;
|
||||
if (this.aFlags.fRunning) {
|
||||
this.aFlags.fRunning = false;
|
||||
if (this.bitField.fRunning) {
|
||||
this.bitField.fRunning = false;
|
||||
if (this.chipset) this.chipset.setSpeaker();
|
||||
var controlRun = this.bindings["run"];
|
||||
if (controlRun) controlRun.textContent = "Run";
|
||||
}
|
||||
this.aFlags.fComplete = fComplete;
|
||||
this.bitField.fComplete = fComplete;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2051,8 +2051,8 @@ if (DEBUGGER) {
|
|||
* it here, so that if the CPU is reset while running, we can prevent stop()
|
||||
* from unnecessarily dumping the CPU state.
|
||||
*/
|
||||
if (this.aFlags.fRunning !== undefined && !fQuiet) this.println("reset");
|
||||
this.aFlags.fRunning = false;
|
||||
if (this.bitField.fRunning !== undefined && !fQuiet) this.println("reset");
|
||||
this.bitField.fRunning = false;
|
||||
this.clearTempBreakpoint();
|
||||
if (!fQuiet) this.updateStatus();
|
||||
};
|
||||
|
|
@ -2119,7 +2119,7 @@ if (DEBUGGER) {
|
|||
Debugger.prototype.start = function(ms, nCycles)
|
||||
{
|
||||
if (!this.fProcStep) this.println("running");
|
||||
this.aFlags.fRunning = true;
|
||||
this.bitField.fRunning = true;
|
||||
this.msStart = ms;
|
||||
this.nCyclesStart = nCycles;
|
||||
};
|
||||
|
|
@ -2135,8 +2135,8 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.stop = function(ms, nCycles)
|
||||
{
|
||||
if (this.aFlags.fRunning) {
|
||||
this.aFlags.fRunning = false;
|
||||
if (this.bitField.fRunning) {
|
||||
this.bitField.fRunning = false;
|
||||
this.nCycles = nCycles - this.nCyclesStart;
|
||||
if (!this.fProcStep) {
|
||||
var sStopped = "stopped";
|
||||
|
|
@ -2855,7 +2855,7 @@ if (DEBUGGER) {
|
|||
sLine += " ";
|
||||
sLine = sLine.substr(0, 50);
|
||||
sLine += ";";
|
||||
if (!this.cpu.aFlags.fChecksum) {
|
||||
if (!this.cpu.bitField.fChecksum) {
|
||||
sLine += sComment + (nSequence != null? '=' + nSequence.toString() : "");
|
||||
} else {
|
||||
var nCycles = this.cpu.getCycles();
|
||||
|
|
@ -3862,7 +3862,7 @@ if (DEBUGGER) {
|
|||
*/
|
||||
Debugger.prototype.doHalt = function(sCount)
|
||||
{
|
||||
if (this.aFlags.fRunning && sCount === undefined) {
|
||||
if (this.bitField.fRunning && sCount === undefined) {
|
||||
this.cpu.stopCPU();
|
||||
return;
|
||||
}
|
||||
|
|
@ -4262,7 +4262,7 @@ if (DEBUGGER) {
|
|||
if (nCycles !== undefined) {
|
||||
this.cpu.resetChecksum();
|
||||
}
|
||||
this.println("checksums " + (this.cpu.aFlags.fChecksum? "enabled" : "disabled"));
|
||||
this.println("checksums " + (this.cpu.bitField.fChecksum? "enabled" : "disabled"));
|
||||
break;
|
||||
case "sp":
|
||||
if (asArgs[2] !== undefined) {
|
||||
|
|
|
|||
|
|
@ -652,7 +652,7 @@ Disk.prototype.doneLoad = function(sDiskFile, sDiskData, nErrorCode, sDiskPath)
|
|||
{
|
||||
var disk = null;
|
||||
this.fWriteProtected = false;
|
||||
var fPrintOnly = (nErrorCode < 0 && this.cmp && !this.cmp.aFlags.fPowered);
|
||||
var fPrintOnly = (nErrorCode < 0 && this.cmp && !this.cmp.bitField.fPowered);
|
||||
|
||||
if (this.fOnDemand) {
|
||||
if (!nErrorCode) {
|
||||
|
|
|
|||
|
|
@ -3905,7 +3905,7 @@ Video.prototype.updateScreen = function(fForce)
|
|||
/*
|
||||
* The Computer component maintains the fPowered setting on our behalf, so we use it.
|
||||
*/
|
||||
if (!this.aFlags.fPowered) return;
|
||||
if (!this.bitField.fPowered) return;
|
||||
|
||||
/*
|
||||
* If the card's video signal is disabled (eg, during a mode change), then skip the update,
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ var X86 = {
|
|||
DATA_EXPDOWN_WRITABLE: 0x1600,
|
||||
CODE_EXECONLY: 0x1800,
|
||||
CODE_READABLE: 0x1a00,
|
||||
CODE_CONFORMING_EXECONLY: 0x1c00,
|
||||
CODE_CONFORMING: 0x1c00,
|
||||
CODE_CONFORMING_READABLE: 0x1e00
|
||||
},
|
||||
DPL: {
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ function X86CPU(parmsCPU) {
|
|||
* stepCPU() call, but it's good form to do so.
|
||||
*/
|
||||
this.nBurstCycles = 0;
|
||||
this.aFlags.fComplete = this.aFlags.fDebugCheck = false;
|
||||
this.bitField.fComplete = this.bitField.fDebugCheck = false;
|
||||
|
||||
/*
|
||||
* We're just declaring aMemBlocks and associated Bus parameters here; they'll be initialized by initMemory()
|
||||
|
|
@ -751,7 +751,7 @@ X86CPU.prototype.initProcessor = function()
|
|||
*/
|
||||
X86CPU.prototype.reset = function()
|
||||
{
|
||||
if (this.aFlags.fRunning) this.stopCPU();
|
||||
if (this.bitField.fRunning) this.stopCPU();
|
||||
this.resetRegs();
|
||||
this.resetCycles();
|
||||
this.clearError(); // clear any fatal error/exception that setError() may have flagged
|
||||
|
|
@ -813,7 +813,6 @@ X86CPU.prototype.resetRegs = function()
|
|||
*/
|
||||
this.regMSW = X86.MSW.SET;
|
||||
this.addrIDT = 0; this.addrIDTLimit = 0x03FF;
|
||||
this.descIDT = {off: 0, sel: 0, acc: 0, maskPS: 0};
|
||||
this.nIOPL = 0; // this should be set before the first setPS() call
|
||||
|
||||
/*
|
||||
|
|
@ -966,7 +965,7 @@ X86CPU.prototype.checkIntNotify = function(nInt)
|
|||
* The enabling of MESSAGE_INT messages is one of the criteria that's also included in the Debugger's
|
||||
* checksEnabled() function, and therefore in fDebugCheck, so for maximum speed, we check fDebugCheck first.
|
||||
*/
|
||||
if (DEBUGGER && this.aFlags.fDebugCheck) {
|
||||
if (DEBUGGER && this.bitField.fDebugCheck) {
|
||||
if (this.dbg.messageEnabled(Debugger.MESSAGE.INT) && this.dbg.messageInt(nInt, this.regEIP)) {
|
||||
this.addIntReturn(this.regEIP, function(cpu, nCycles) {
|
||||
return function onIntReturn(nLevel) {
|
||||
|
|
@ -1040,10 +1039,10 @@ X86CPU.prototype.setProtMode = function(fProt)
|
|||
fProt = !!(this.regMSW & X86.MSW.PE);
|
||||
}
|
||||
this.aOpGrp6 = (fProt? X86Op0F.aOpGrp6Prot : X86Op0F.aOpGrp6Real);
|
||||
this.segCS.updateAccess(fProt);
|
||||
this.segDS.updateAccess(fProt);
|
||||
this.segSS.updateAccess(fProt);
|
||||
this.segES.updateAccess(fProt);
|
||||
this.segCS.updateMode(fProt);
|
||||
this.segDS.updateMode(fProt);
|
||||
this.segSS.updateMode(fProt);
|
||||
this.segES.updateMode(fProt);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2389,7 +2388,7 @@ X86CPU.prototype.delayINTR = function()
|
|||
*/
|
||||
X86CPU.prototype.displayStatus = function(fForce)
|
||||
{
|
||||
if (fForce || !this.aFlags.fRunning || this.aFlags.fDisplayLiveRegs) {
|
||||
if (fForce || !this.bitField.fRunning || this.bitField.fDisplayLiveRegs) {
|
||||
this.displayReg("AX", this.regAX);
|
||||
this.displayReg("BX", this.regBX);
|
||||
this.displayReg("CX", this.regCX);
|
||||
|
|
@ -2452,12 +2451,12 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
* Debugger is single-stepping (even when performing multiple single-steps), fRunning is never set,
|
||||
* so stopCPU() would have no effect as far as the Debugger is concerned.
|
||||
*/
|
||||
this.aFlags.fComplete = true;
|
||||
this.bitField.fComplete = true;
|
||||
|
||||
/*
|
||||
* fDebugCheck is true if we need to "check" every instruction with the Debugger.
|
||||
*/
|
||||
var fDebugCheck = this.aFlags.fDebugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
|
||||
var fDebugCheck = this.bitField.fDebugCheck = (DEBUGGER && this.dbg && this.dbg.checksEnabled());
|
||||
|
||||
/*
|
||||
* fDebugSkip is checked only when fDebugCheck is true, and its sole purpose is to tell the first call
|
||||
|
|
@ -2467,8 +2466,8 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
* Once we snap fStarting, we clear it, because technically, we've moved beyond "starting" and have officially
|
||||
* "started" now.
|
||||
*/
|
||||
var fDebugSkip = this.aFlags.fStarting || !nMinCycles;
|
||||
this.aFlags.fStarting = false;
|
||||
var fDebugSkip = this.bitField.fStarting || !nMinCycles;
|
||||
this.bitField.fStarting = false;
|
||||
|
||||
/*
|
||||
* We move the minimum cycle count to nStepCycles (the number of cycles left to step), so that other
|
||||
|
|
@ -2609,7 +2608,7 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
/*
|
||||
* Make sure that every instruction is assessing a cycle cost, and that the cost is a net positive.
|
||||
*/
|
||||
if (this.aFlags.fComplete && this.nStepCycles >= this.nSnapCycles && !(this.opFlags & X86.OPFLAG.PREFIXES)) {
|
||||
if (this.bitField.fComplete && this.nStepCycles >= this.nSnapCycles && !(this.opFlags & X86.OPFLAG.PREFIXES)) {
|
||||
this.println("cycle miscount: " + (this.nSnapCycles - this.nStepCycles));
|
||||
this.setIP(this.opEA - this.segCS.base);
|
||||
this.stopCPU();
|
||||
|
|
@ -2619,7 +2618,7 @@ X86CPU.prototype.stepCPU = function(nMinCycles)
|
|||
|
||||
} while (this.nStepCycles > 0);
|
||||
|
||||
return (this.aFlags.fComplete? this.nBurstCycles - this.nStepCycles : (this.aFlags.fComplete === undefined? 0 : -1));
|
||||
return (this.bitField.fComplete? this.nBurstCycles - this.nStepCycles : (this.bitField.fComplete === undefined? 0 : -1));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ var X86Help = {
|
|||
* 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) {
|
||||
var fConforming = ((this.segVER.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING_EXECONLY) == X86.DESC.ACC.TYPE.CODE_CONFORMING_EXECONLY);
|
||||
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();
|
||||
return this.segVER.limit;
|
||||
|
|
@ -453,18 +453,6 @@ var X86Help = {
|
|||
this.pushWord(regIP);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* opHelpDIVOverflow()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
*/
|
||||
opHelpDIVOverflow: function() {
|
||||
this.setIP(this.opEA - this.segCS.base);
|
||||
/*
|
||||
* TODO: Determine the proper cycle cost.
|
||||
*/
|
||||
X86Help.opHelpINT.call(this, X86.EXCEPTION.DIV_ERR, null, 2);
|
||||
},
|
||||
/**
|
||||
* opHelpINT(nIDT, nError, nCycles)
|
||||
*
|
||||
|
|
@ -478,6 +466,7 @@ var X86Help = {
|
|||
* TODO: We assess the cycle cost up front, because otherwise, if loadIDT() fails, no cost may be assessed.
|
||||
*/
|
||||
this.nStepCycles -= this.CYCLES.nOpCyclesInt + nCycles;
|
||||
this.segCS.fCall = true;
|
||||
var regPS = this.getPS();
|
||||
var regCS = this.segCS.sel;
|
||||
var regIP = this.regIP;
|
||||
|
|
@ -490,17 +479,6 @@ var X86Help = {
|
|||
if (nError != null) this.pushWord(nError);
|
||||
this.nFault = -1;
|
||||
}
|
||||
/*
|
||||
if (X86Help.opHelpLoadIDT.call(this, nIDT)) {
|
||||
if (this.descIDT.maskPS) {
|
||||
X86Help.opHelpPushPS.call(this, nError);
|
||||
} else {
|
||||
X86Help.opHelpSwitchTSS.call(this, this.descIDT.sel, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
X86Help.opHelpFault.call(this, X86.EXCEPTION.GP_FAULT, (nIDT << 3) | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
|
||||
*/
|
||||
},
|
||||
/**
|
||||
* opHelpIRET()
|
||||
|
|
@ -516,7 +494,7 @@ var X86Help = {
|
|||
if (this.regPS & X86.PS.NT) {
|
||||
var addrNew = this.segTSS.base;
|
||||
var sel = this.getWord(addrNew + X86.TSS.PREV_TSS);
|
||||
X86Help.opHelpSwitchTSS.call(this, sel, false);
|
||||
X86Seg.switchTSS.call(this.segCS, sel, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -529,137 +507,16 @@ var X86Help = {
|
|||
}
|
||||
},
|
||||
/**
|
||||
* opHelpLoadIDT(nIDT)
|
||||
*
|
||||
* Updates descIDT as follows:
|
||||
*
|
||||
* descIDT.off 0x0-0x1 offset of interrupt handler
|
||||
* descIDT.sel 0x2-0x3 selector of interrupt handler
|
||||
* descIDT.acc 0x4-0x5 access word (protected-mode only)
|
||||
* descIDT.maskPS mask to apply PS after saving current PS (0 if none; ie, task switch)
|
||||
* opHelpDIVOverflow()
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} nIDT
|
||||
* @return {boolean} true if successful, false if not (all failure cases currently limited to protected mode)
|
||||
*/
|
||||
opHelpLoadIDT: function(nIDT) {
|
||||
var offIDT;
|
||||
|
||||
if (DEBUG) this.assert(nIDT >= 0 && nIDT < 256);
|
||||
|
||||
if (this.regMSW & X86.MSW.PE) {
|
||||
offIDT = this.addrIDT + (nIDT << 3);
|
||||
if (offIDT + 7 > this.addrIDTLimit) {
|
||||
return false;
|
||||
}
|
||||
this.descIDT.off = this.getWord(offIDT);
|
||||
this.descIDT.sel = this.getWord(offIDT + 2);
|
||||
this.descIDT.acc = this.getWord(offIDT + 4);
|
||||
this.descIDT.maskPS = 0;
|
||||
|
||||
switch (this.descIDT.acc & X86.DESC.ACC.TYPE.MASK) {
|
||||
case X86.DESC.ACC.TYPE.GATE_INT:
|
||||
this.descIDT.maskPS = ~(X86.PS.NT | X86.PS.TF | X86.PS.IF);
|
||||
break;
|
||||
case X86.DESC.ACC.TYPE.GATE_TRAP:
|
||||
this.descIDT.maskPS = ~(X86.PS.NT | X86.PS.TF);
|
||||
break;
|
||||
case X86.DESC.ACC.TYPE.GATE_TASK:
|
||||
break;
|
||||
default:
|
||||
if (DEBUG) this.assert(false, "INT 0x" + str.toHexByte(nIDT) + ": unrecognized IDT entry");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (DEBUG) this.assert(!this.addrIDT && this.addrIDTLimit == 0x03FF);
|
||||
opHelpDIVOverflow: function() {
|
||||
this.setIP(this.opEA - this.segCS.base);
|
||||
/*
|
||||
* Intel documentation for INT/INTO under "REAL ADDRESS MODE EXCEPTIONS" says:
|
||||
*
|
||||
* "[T]he 80286 will shut down if the SP = 1, 3, or 5 before executing the INT or INTO instruction--due to lack of stack space"
|
||||
*
|
||||
* TODO: Verify that 80286 real-mode actually enforces the above. See http://localhost:8088/pubs/pc/reference/intel/80286/progref/#page-260
|
||||
* TODO: Determine the proper cycle cost.
|
||||
*/
|
||||
offIDT = this.addrIDT + (nIDT << 2);
|
||||
this.descIDT.off = this.getWord(offIDT);
|
||||
this.descIDT.sel = this.getWord(offIDT + 2);
|
||||
this.descIDT.maskPS = ~(X86.PS.TF | X86.PS.IF);
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
* opHelpSwitchTSS(selNew, fNest)
|
||||
*
|
||||
* Helper implementing TSS (Task State Segment) task switching.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} selNew
|
||||
* @param {boolean} fNest is true if nesting, false if un-nesting
|
||||
* @return {boolean} true if successful, false if error
|
||||
*/
|
||||
opHelpSwitchTSS: function(selNew, fNest) {
|
||||
var addrOld = this.segTSS.base;
|
||||
var cplOld = this.segCS.cpl;
|
||||
var selOld = this.segTSS.sel;
|
||||
if (!fNest) {
|
||||
if (this.segTSS.type != X86.DESC.ACC.TYPE.TSS_BUSY) {
|
||||
X86Help.opHelpFault.call(this, X86.EXCEPTION.TS_FAULT, selNew, true);
|
||||
return false;
|
||||
}
|
||||
this.setWord(this.segTSS.addrDesc + X86.DESC.ACC.OFFSET, (this.segTSS.acc & ~X86.DESC.ACC.TYPE.TSS_BUSY) | X86.DESC.ACC.TYPE.TSS);
|
||||
}
|
||||
if (this.segTSS.load(selNew) == null) {
|
||||
return false;
|
||||
}
|
||||
var addrNew = this.segTSS.base;
|
||||
if (DEBUG) {
|
||||
this.messageDebugger((fNest? "Task switch" : "Task return") + ": TR " + str.toHexWord(selOld) + " (%" + str.toHex(addrOld, 6) + "), new TR " + str.toHexWord(selNew) + " (%" + str.toHex(addrNew, 6) + ")", Debugger.MESSAGE.TSS);
|
||||
}
|
||||
if (fNest) {
|
||||
if (this.segTSS.type == X86.DESC.ACC.TYPE.TSS_BUSY) {
|
||||
X86Help.opHelpFault.call(this, X86.EXCEPTION.GP_FAULT, selNew, true);
|
||||
return false;
|
||||
}
|
||||
this.setWord(this.segTSS.addrDesc + X86.DESC.ACC.OFFSET, this.segTSS.acc |= X86.DESC.ACC.TYPE.TSS_BUSY);
|
||||
this.segTSS.type = X86.DESC.ACC.TYPE.TSS_BUSY;
|
||||
}
|
||||
this.setWord(addrOld + X86.TSS.TASK_IP, this.regIP);
|
||||
this.setWord(addrOld + X86.TSS.TASK_PS, this.getPS());
|
||||
this.setWord(addrOld + X86.TSS.TASK_AX, this.regAX);
|
||||
this.setWord(addrOld + X86.TSS.TASK_CX, this.regCX);
|
||||
this.setWord(addrOld + X86.TSS.TASK_DX, this.regDX);
|
||||
this.setWord(addrOld + X86.TSS.TASK_BX, this.regBX);
|
||||
this.setWord(addrOld + X86.TSS.TASK_SP, this.regSP);
|
||||
this.setWord(addrOld + X86.TSS.TASK_BP, this.regBP);
|
||||
this.setWord(addrOld + X86.TSS.TASK_SI, this.regSI);
|
||||
this.setWord(addrOld + X86.TSS.TASK_DI, this.regDI);
|
||||
this.setWord(addrOld + X86.TSS.TASK_ES, this.segES.sel);
|
||||
this.setWord(addrOld + X86.TSS.TASK_CS, this.segCS.sel);
|
||||
this.setWord(addrOld + X86.TSS.TASK_SS, this.segSS.sel);
|
||||
this.setWord(addrOld + X86.TSS.TASK_DS, this.segDS.sel);
|
||||
var offSS = X86.TSS.TASK_SS;
|
||||
var offSP = X86.TSS.TASK_SP;
|
||||
this.setPS(this.getWord(addrNew + X86.TSS.TASK_PS) | (fNest? X86.PS.NT : 0));
|
||||
if (DEBUG) this.assert(!fNest || !!(this.regPS & X86.PS.NT));
|
||||
this.regAX = this.getWord(addrNew + X86.TSS.TASK_AX);
|
||||
this.regCX = this.getWord(addrNew + X86.TSS.TASK_CX);
|
||||
this.regDX = this.getWord(addrNew + X86.TSS.TASK_DX);
|
||||
this.regBX = this.getWord(addrNew + X86.TSS.TASK_BX);
|
||||
this.regBP = this.getWord(addrNew + X86.TSS.TASK_BP);
|
||||
this.regSI = this.getWord(addrNew + X86.TSS.TASK_SI);
|
||||
this.regDI = this.getWord(addrNew + X86.TSS.TASK_DI);
|
||||
this.segES.load(this.getWord(addrNew + X86.TSS.TASK_ES));
|
||||
this.segDS.load(this.getWord(addrNew + X86.TSS.TASK_DS));
|
||||
this.setCSIP(this.getWord(addrNew + X86.TSS.TASK_IP), this.getWord(addrNew + X86.TSS.TASK_CS));
|
||||
if (this.segCS.cpl < cplOld) {
|
||||
offSP = (this.segCS.cpl << 2) + X86.TSS.CPL0_SP;
|
||||
offSS = offSP + 2;
|
||||
}
|
||||
this.regSP = this.getWord(addrNew + offSP);
|
||||
this.segSS.load(this.getWord(addrNew + offSS));
|
||||
this.segLDT.load(this.getWord(addrNew + X86.TSS.TASK_LDT));
|
||||
if (fNest) this.setWord(addrNew + X86.TSS.PREV_TSS, selOld);
|
||||
this.regMSW |= X86.MSW.TS;
|
||||
return true;
|
||||
X86Help.opHelpINT.call(this, X86.EXCEPTION.DIV_ERR, null, 2);
|
||||
},
|
||||
/**
|
||||
* opHelpFault(nFault, nError, fHalt)
|
||||
|
|
@ -671,27 +528,28 @@ var X86Help = {
|
|||
* @param {number} [nError]
|
||||
* @param {boolean} [fHalt] will halt the CPU if true *and* a Debugger is loaded
|
||||
*/
|
||||
opHelpFault: function(nFault, nError, fHalt) {
|
||||
if (!this.aFlags.fComplete) {
|
||||
// this.messageDebugger("Fault " + str.toHexByte(nFault) + " blocked by Debugger", Debugger.MESSAGE.WARN);
|
||||
opHelpFault: function(nFault, nError, fHalt)
|
||||
{
|
||||
if (!this.bitField.fComplete) {
|
||||
this.messageDebugger("Fault " + str.toHexByte(nFault) + " blocked by Debugger", Debugger.MESSAGE.WARN);
|
||||
this.setIP(this.opEA - this.segCS.base);
|
||||
return;
|
||||
}
|
||||
var fFault = false;
|
||||
var fDispatch = false;
|
||||
if (this.model >= X86.MODEL_80186) {
|
||||
if (this.nFault < 0) {
|
||||
/*
|
||||
* Single-fault (error code is passed through, and the responsible instruction is restartable)
|
||||
*/
|
||||
this.setIP(this.opEA - this.segCS.base);
|
||||
fFault = true;
|
||||
fDispatch = true;
|
||||
} else if (this.nFault != X86.EXCEPTION.DF_FAULT) {
|
||||
/*
|
||||
* Double-fault (error code is always zero, and the responsible instruction is not restartable)
|
||||
*/
|
||||
nError = 0;
|
||||
nFault = X86.EXCEPTION.DF_FAULT;
|
||||
fFault = true;
|
||||
fDispatch = true;
|
||||
} else {
|
||||
/*
|
||||
* Triple-fault (usually referred to in Intel literature as a "shutdown", but at least on the 80286,
|
||||
|
|
@ -703,9 +561,24 @@ var X86Help = {
|
|||
}
|
||||
}
|
||||
if (X86Help.opHelpFaultMessage.call(this, nFault, nError, fHalt)) {
|
||||
fFault = false;
|
||||
fDispatch = false;
|
||||
}
|
||||
if (fDispatch) X86Help.opHelpINT.call(this, this.nFault = nFault, nError, 0);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* As long as we're not using EAFUNCS, this is easy for any EA-based memory accesses: simply set
|
||||
* 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 setting a special
|
||||
* flag and throwing an exception that the CPU must intercept and then quietly ignore.
|
||||
*/
|
||||
if (!EAFUNCS) {
|
||||
this.opFlags &= ~(X86.OPFLAG.NOREAD | X86.OPFLAG.NOWRITE);
|
||||
}
|
||||
if (fFault) X86Help.opHelpINT.call(this, this.nFault = nFault, nError, 0);
|
||||
},
|
||||
/**
|
||||
* opHelpFaultMessage()
|
||||
|
|
@ -714,22 +587,26 @@ var X86Help = {
|
|||
* 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.
|
||||
* continue the dispatch after it has interfered. At the moment, your only option is to single-step over
|
||||
* the offending instruction to allow the fault to be dispatched.
|
||||
*
|
||||
* @this {X86CPU}
|
||||
* @param {number} nFault
|
||||
* @param {number} [nError]
|
||||
* @param {boolean} [fHalt] will halt the CPU if true *and* a Debugger is loaded
|
||||
* @return {boolean} true to halt the CPU, false if not
|
||||
* @return {boolean|undefined} true to block the fault, otherwise dispatch it
|
||||
*/
|
||||
opHelpFaultMessage: function(nFault, nError, fHalt) {
|
||||
/*
|
||||
* TODO: When we're done examining all GP faults, change the following to "fHalt || false"
|
||||
*/
|
||||
fHalt = fHalt || (nFault == X86.EXCEPTION.GP_FAULT);
|
||||
|
||||
opHelpFaultMessage: function(nFault, nError, fHalt)
|
||||
{
|
||||
var bitsMessage = Debugger.MESSAGE.FAULT;
|
||||
var bOpcode = this.bus.getByteDirect(this.regEIP);
|
||||
|
||||
var fDebugger = false;
|
||||
if (DEBUGGER && this.dbg) {
|
||||
fDebugger = true;
|
||||
if (nFault == X86.EXCEPTION.GP_FAULT) 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,
|
||||
|
|
@ -755,14 +632,19 @@ var X86Help = {
|
|||
if (this.regEIP >= 0x0F0000 && this.regEIP <= 0x0FFFFF) {
|
||||
fHalt = false;
|
||||
}
|
||||
var sMessage = "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) + ")";
|
||||
|
||||
if (DEBUGGER && this.dbg) {
|
||||
this.messageDebugger(sMessage, bitsMessage);
|
||||
if (fHalt) this.dbg.stopCPU();
|
||||
} else if (fHalt) {
|
||||
this.notice(sMessage);
|
||||
this.stopCPU();
|
||||
if (fDebugger && this.dbg.messageEnabled(bitsMessage) || !fDebugger && fHalt) {
|
||||
var sMessage = "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) + ")";
|
||||
if (fDebugger) {
|
||||
this.messageDebugger(sMessage, bitsMessage);
|
||||
if (fHalt) {
|
||||
fHalt = this.bitField.fRunning;
|
||||
this.dbg.stopCPU();
|
||||
}
|
||||
} else if (fHalt) {
|
||||
this.notice(sMessage);
|
||||
this.stopCPU();
|
||||
}
|
||||
}
|
||||
return fHalt;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,10 +138,10 @@ var X86Op0F = {
|
|||
this.regDX = this.getWord(0x830);
|
||||
this.regCX = this.getWord(0x832);
|
||||
this.regAX = this.getWord(0x834);
|
||||
this.segES.loadDesc6(this.getWord(0x824), 0x836);
|
||||
this.segCS.loadDesc6(this.getWord(0x822), 0x83C);
|
||||
this.segSS.loadDesc6(this.getWord(0x820), 0x842);
|
||||
this.segDS.loadDesc6(this.getWord(0x81E), 0x848);
|
||||
this.segES.loadDesc6(0x836, this.getWord(0x824));
|
||||
this.segCS.loadDesc6(0x83C, this.getWord(0x822));
|
||||
this.segSS.loadDesc6(0x842, this.getWord(0x820));
|
||||
this.segDS.loadDesc6(0x848, this.getWord(0x81E));
|
||||
this.setPS(this.getWord(0x818));
|
||||
this.setIP(this.getWord(0x81A));
|
||||
/*
|
||||
|
|
@ -149,10 +149,10 @@ var X86Op0F = {
|
|||
*/
|
||||
this.addrGDT = this.getWord(0x84E) | (this.getWord(0x850) << 16);
|
||||
this.addrGDTLimit = this.addrGDT + this.getWord(0x852);
|
||||
this.segLDT.loadDesc6(this.getWord(0x81C), 0x854);
|
||||
this.segLDT.loadDesc6(0x854, this.getWord(0x81C));
|
||||
this.addrIDT = this.getWord(0x85A) | (this.getWord(0x85C) << 16);
|
||||
this.addrIDTLimit = this.addrIDT + this.getWord(0x85E);
|
||||
this.segTSS.loadDesc6(this.getWord(0x816), 0x860);
|
||||
this.segTSS.loadDesc6(0x860, this.getWord(0x816));
|
||||
this.nStepCycles -= 195;
|
||||
/*
|
||||
* TODO: LOADALL operation still needs to be verified in protected mode....
|
||||
|
|
@ -246,7 +246,7 @@ var X86Op0F = {
|
|||
* current privilege level and the selector's RPL.
|
||||
*/
|
||||
if (this.segVER.dpl >= this.segCS.cpl && this.segVER.dpl >= (dst & X86.SEL.RPL) ||
|
||||
(this.segVER.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING_EXECONLY) == X86.DESC.ACC.TYPE.CODE_CONFORMING_EXECONLY) {
|
||||
(this.segVER.acc & X86.DESC.ACC.TYPE.CODE_CONFORMING) == X86.DESC.ACC.TYPE.CODE_CONFORMING) {
|
||||
this.setZF();
|
||||
return dst;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ function X86Seg(cpu, id, sName, fProt)
|
|||
this.awScratch = (this.id == X86Seg.ID.CODE? new Array(32) : []);
|
||||
this.fCall = null;
|
||||
this.fStackSwitch = false;
|
||||
this.updateAccess(fProt);
|
||||
this.updateMode(fProt);
|
||||
}
|
||||
|
||||
X86Seg.ID = {
|
||||
|
|
@ -121,7 +121,7 @@ X86Seg.loadReal = function loadReal(sel, fSuppress)
|
|||
/**
|
||||
* loadProt(sel, fSuppress)
|
||||
*
|
||||
* This replaces the segment's default load() function whenever the segment is notified via updateAccess() by the
|
||||
* This replaces the segment's default load() function whenever the segment is notified via updateMode() by the
|
||||
* CPU's setProtMode() that the processor is now in protected-mode.
|
||||
*
|
||||
* Segments in protected-mode are referenced by selectors, which are indexes into descriptor tables (GDT or LDT)
|
||||
|
|
@ -145,12 +145,14 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
|
|||
{
|
||||
var addrDT;
|
||||
var addrDTLimit;
|
||||
var cpu = this.cpu;
|
||||
|
||||
if (!(sel & X86.SEL.LDT)) {
|
||||
addrDT = this.cpu.addrGDT;
|
||||
addrDTLimit = this.cpu.addrGDTLimit;
|
||||
addrDT = cpu.addrGDT;
|
||||
addrDTLimit = cpu.addrGDTLimit;
|
||||
} else {
|
||||
addrDT = this.cpu.segLDT.base;
|
||||
addrDTLimit = addrDT + this.cpu.segLDT.limit;
|
||||
addrDT = cpu.segLDT.base;
|
||||
addrDTLimit = addrDT + cpu.segLDT.limit;
|
||||
}
|
||||
var addrDesc = addrDT + (sel & X86.SEL.MASK);
|
||||
if (addrDesc + 7 <= addrDTLimit) {
|
||||
|
|
@ -159,8 +161,8 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
|
|||
* 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.
|
||||
*/
|
||||
if (!fSuppress) this.cpu.nStepCycles -= 15;
|
||||
return this.loadDesc8(sel, addrDesc);
|
||||
if (!fSuppress) cpu.nStepCycles -= 15;
|
||||
return this.loadDesc8(addrDesc, sel);
|
||||
}
|
||||
if (!fSuppress) {
|
||||
X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, sel);
|
||||
|
|
@ -177,8 +179,9 @@ X86Seg.loadProt = function loadProt(sel, fSuppress)
|
|||
*/
|
||||
X86Seg.loadRealIDT = function loadRealIDT(nIDT)
|
||||
{
|
||||
var cpu = this.cpu;
|
||||
if (DEBUG) {
|
||||
this.cpu.assert(nIDT >= 0 && nIDT < 256 && !this.cpu.addrIDT && this.cpu.addrIDTLimit == 0x03FF);
|
||||
cpu.assert(nIDT >= 0 && nIDT < 256 && !cpu.addrIDT && cpu.addrIDTLimit == 0x03FF);
|
||||
}
|
||||
/*
|
||||
* Intel documentation for INT/INTO under "REAL ADDRESS MODE EXCEPTIONS" says:
|
||||
|
|
@ -187,10 +190,10 @@ X86Seg.loadRealIDT = function loadRealIDT(nIDT)
|
|||
*
|
||||
* TODO: Verify that 80286 real-mode actually enforces the above. See http://localhost:8088/pubs/pc/reference/intel/80286/progref/#page-260
|
||||
*/
|
||||
var offIDT = this.cpu.addrIDT + (nIDT << 2);
|
||||
this.cpu.regIP = this.cpu.getWord(offIDT);
|
||||
this.sel = this.cpu.getWord(offIDT + 2);
|
||||
this.cpu.regPS &= ~(X86.PS.TF | X86.PS.IF);
|
||||
var offIDT = cpu.addrIDT + (nIDT << 2);
|
||||
cpu.regIP = cpu.getWord(offIDT);
|
||||
this.sel = cpu.getWord(offIDT + 2);
|
||||
cpu.regPS &= ~(X86.PS.TF | X86.PS.IF);
|
||||
return this.base = this.sel << 4;
|
||||
};
|
||||
|
||||
|
|
@ -203,12 +206,13 @@ X86Seg.loadRealIDT = function loadRealIDT(nIDT)
|
|||
*/
|
||||
X86Seg.loadProtIDT = function loadProtIDT(nIDT)
|
||||
{
|
||||
if (DEBUG) this.cpu.assert(nIDT >= 0 && nIDT < 256);
|
||||
var cpu = this.cpu;
|
||||
if (DEBUG) cpu.assert(nIDT >= 0 && nIDT < 256);
|
||||
|
||||
nIDT <<= 3;
|
||||
var addrDesc = this.cpu.addrIDT + nIDT;
|
||||
if (addrDesc + 7 <= this.cpu.addrIDTLimit) {
|
||||
return this.loadDesc8(nIDT, addrDesc);
|
||||
var addrDesc = cpu.addrIDT + nIDT;
|
||||
if (addrDesc + 7 <= cpu.addrIDTLimit) {
|
||||
return this.loadDesc8(addrDesc, nIDT);
|
||||
}
|
||||
X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, nIDT | X86.ERRCODE.IDT | X86.ERRCODE.EXT, true);
|
||||
return null;
|
||||
|
|
@ -350,12 +354,123 @@ X86Seg.checkWriteProtDisallowed = function checkWriteProtDisallowed(off, cb, fSu
|
|||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* switchTSS(selNew, fNest)
|
||||
*
|
||||
* Implements TSS (Task State Segment) task switching.
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} selNew
|
||||
* @param {boolean} fNest is true if nesting, false if un-nesting
|
||||
* @return {boolean} true if successful, false if error
|
||||
*/
|
||||
X86Seg.switchTSS = function switchTSS(selNew, fNest)
|
||||
{
|
||||
var cpu = this.cpu;
|
||||
if (DEBUG) cpu.assert(this === cpu.segCS);
|
||||
|
||||
var addrOld = cpu.segTSS.base;
|
||||
var cplOld = this.cpl;
|
||||
var selOld = cpu.segTSS.sel;
|
||||
if (!fNest) {
|
||||
if (cpu.segTSS.type != X86.DESC.ACC.TYPE.TSS_BUSY) {
|
||||
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.TS_FAULT, selNew, true);
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
var addrNew = cpu.segTSS.base;
|
||||
if (DEBUG) {
|
||||
cpu.messageDebugger((fNest? "Task switch" : "Task return") + ": TR " + str.toHexWord(selOld) + " (%" + str.toHex(addrOld, 6) + "), new TR " + str.toHexWord(selNew) + " (%" + str.toHex(addrNew, 6) + ")", Debugger.MESSAGE.TSS);
|
||||
}
|
||||
if (fNest) {
|
||||
if (cpu.segTSS.type == X86.DESC.ACC.TYPE.TSS_BUSY) {
|
||||
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, selNew, true);
|
||||
return false;
|
||||
}
|
||||
cpu.setWord(cpu.segTSS.addrDesc + X86.DESC.ACC.OFFSET, cpu.segTSS.acc |= X86.DESC.ACC.TYPE.TSS_BUSY);
|
||||
cpu.segTSS.type = X86.DESC.ACC.TYPE.TSS_BUSY;
|
||||
}
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_IP, cpu.regIP);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_PS, cpu.getPS());
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_AX, cpu.regAX);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_CX, cpu.regCX);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_DX, cpu.regDX);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_BX, cpu.regBX);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_SP, cpu.regSP);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_BP, cpu.regBP);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_SI, cpu.regSI);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_DI, cpu.regDI);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_ES, cpu.segES.sel);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_CS, cpu.segCS.sel);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_SS, cpu.segSS.sel);
|
||||
cpu.setWord(addrOld + X86.TSS.TASK_DS, cpu.segDS.sel);
|
||||
var offSS = X86.TSS.TASK_SS;
|
||||
var offSP = X86.TSS.TASK_SP;
|
||||
cpu.setPS(cpu.getWord(addrNew + X86.TSS.TASK_PS) | (fNest? X86.PS.NT : 0));
|
||||
if (DEBUG) cpu.assert(!fNest || !!(cpu.regPS & X86.PS.NT));
|
||||
cpu.regAX = cpu.getWord(addrNew + X86.TSS.TASK_AX);
|
||||
cpu.regCX = cpu.getWord(addrNew + X86.TSS.TASK_CX);
|
||||
cpu.regDX = cpu.getWord(addrNew + X86.TSS.TASK_DX);
|
||||
cpu.regBX = cpu.getWord(addrNew + X86.TSS.TASK_BX);
|
||||
cpu.regBP = cpu.getWord(addrNew + X86.TSS.TASK_BP);
|
||||
cpu.regSI = cpu.getWord(addrNew + X86.TSS.TASK_SI);
|
||||
cpu.regDI = cpu.getWord(addrNew + X86.TSS.TASK_DI);
|
||||
cpu.segES.load(cpu.getWord(addrNew + X86.TSS.TASK_ES));
|
||||
cpu.segDS.load(cpu.getWord(addrNew + X86.TSS.TASK_DS));
|
||||
cpu.setCSIP(cpu.getWord(addrNew + X86.TSS.TASK_IP), cpu.getWord(addrNew + X86.TSS.TASK_CS));
|
||||
if (this.cpl < cplOld) {
|
||||
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
|
||||
offSS = offSP + 2;
|
||||
}
|
||||
cpu.regSP = cpu.getWord(addrNew + offSP);
|
||||
cpu.segSS.load(cpu.getWord(addrNew + offSS));
|
||||
cpu.segLDT.load(cpu.getWord(addrNew + X86.TSS.TASK_LDT));
|
||||
if (fNest) cpu.setWord(addrNew + X86.TSS.PREV_TSS, selOld);
|
||||
cpu.regMSW |= X86.MSW.TS;
|
||||
return true;
|
||||
};
|
||||
|
||||
/*
|
||||
* Object methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* loadDesc6(sel, addrDesc)
|
||||
* loadAcc(sel, fGDT)
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
X86Seg.prototype.loadAcc = function(sel, fGDT)
|
||||
{
|
||||
var addrDT;
|
||||
var addrDTLimit;
|
||||
var cpu = this.cpu;
|
||||
|
||||
if (!(sel & X86.SEL.LDT)) {
|
||||
addrDT = cpu.addrGDT;
|
||||
addrDTLimit = cpu.addrGDTLimit;
|
||||
} else if (!fGDT) {
|
||||
addrDT = cpu.segLDT.base;
|
||||
addrDTLimit = addrDT + cpu.segLDT.limit;
|
||||
}
|
||||
if (addrDT !== undefined) {
|
||||
var addrDesc = addrDT + (sel & X86.SEL.MASK);
|
||||
if (addrDesc + 7 <= addrDTLimit) {
|
||||
return cpu.getWord(addrDesc + X86.DESC.ACC.OFFSET);
|
||||
}
|
||||
}
|
||||
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel);
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* loadDesc6(addrDesc, sel)
|
||||
*
|
||||
* Used to load a protected-mode selector that refers to a 6-byte "descriptor cache" (aka LOADALL) entry:
|
||||
*
|
||||
|
|
@ -364,22 +479,23 @@ X86Seg.checkWriteProtDisallowed = function checkWriteProtDisallowed(off, cb, fSu
|
|||
* word 2: segment limit (0-15)
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} sel is the selector
|
||||
* @param {number} addrDesc is the offset
|
||||
* @param {number} addrDesc is the descriptor address
|
||||
* @param {number} sel is the associated selector
|
||||
* @return {number} base address of selected segment
|
||||
*/
|
||||
X86Seg.prototype.loadDesc6 = function(sel, addrDesc)
|
||||
X86Seg.prototype.loadDesc6 = function(addrDesc, sel)
|
||||
{
|
||||
var acc = this.cpu.getWord(addrDesc + 2);
|
||||
var base = this.cpu.getWord(addrDesc + 0) | ((acc & 0xff) << 16);
|
||||
var limit = this.cpu.getWord(addrDesc + 4);
|
||||
var cpu = this.cpu;
|
||||
var acc = cpu.getWord(addrDesc + 2);
|
||||
var base = cpu.getWord(addrDesc + 0) | ((acc & 0xff) << 16);
|
||||
var limit = cpu.getWord(addrDesc + 4);
|
||||
|
||||
this.sel = sel;
|
||||
this.base = base;
|
||||
this.limit = limit;
|
||||
this.acc = acc & X86.DESC.ACC.MASK;
|
||||
this.addrDesc = addrDesc;
|
||||
this.updateAccess();
|
||||
this.updateMode();
|
||||
|
||||
this.messageDebugger(sel, base, limit, acc);
|
||||
|
||||
|
|
@ -387,7 +503,7 @@ X86Seg.prototype.loadDesc6 = function(sel, addrDesc)
|
|||
};
|
||||
|
||||
/**
|
||||
* loadDesc8(sel, addrDesc)
|
||||
* loadDesc8(addrDesc, sel)
|
||||
*
|
||||
* Used to load a protected-mode selector that refers to an 8-byte "descriptor table" (GDT, LDT, IDT) entry:
|
||||
*
|
||||
|
|
@ -399,22 +515,23 @@ X86Seg.prototype.loadDesc6 = function(sel, addrDesc)
|
|||
* See X86.DESC for offset and bit definitions.
|
||||
*
|
||||
* @this {X86Seg}
|
||||
* @param {number} sel is the selector
|
||||
* @param {number} addrDesc is the offset
|
||||
* @param {number} addrDesc is the descriptor address
|
||||
* @param {number} sel is the associated selector
|
||||
* @return {number|null} base address of selected segment, or null if error
|
||||
*/
|
||||
X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
|
||||
X86Seg.prototype.loadDesc8 = function(addrDesc, sel)
|
||||
{
|
||||
var limit = this.cpu.getWord(addrDesc + X86.DESC.LIMIT.OFFSET);
|
||||
var acc = this.cpu.getWord(addrDesc + X86.DESC.ACC.OFFSET);
|
||||
var cpu = this.cpu;
|
||||
var limit = cpu.getWord(addrDesc + X86.DESC.LIMIT.OFFSET);
|
||||
var acc = cpu.getWord(addrDesc + X86.DESC.ACC.OFFSET);
|
||||
var type = (acc & X86.DESC.ACC.TYPE.MASK);
|
||||
var base = this.cpu.getWord(addrDesc + X86.DESC.BASE.OFFSET) | ((acc & X86.DESC.ACC.BASE1623) << 16);
|
||||
var ext = (DEBUG? this.cpu.getWord(addrDesc + X86.DESC.EXT.OFFSET) : 0);
|
||||
var base = cpu.getWord(addrDesc + X86.DESC.BASE.OFFSET) | ((acc & X86.DESC.ACC.BASE1623) << 16);
|
||||
var ext = (DEBUG? cpu.getWord(addrDesc + X86.DESC.EXT.OFFSET) : 0);
|
||||
var selMasked = sel & X86.SEL.MASK;
|
||||
|
||||
while (true) {
|
||||
|
||||
var cplPrev, addrTSS, offSP, offSS, regSPPrev, regSSPrev;
|
||||
var accCode, selCode, cplPrev, addrTSS, offSP, offSS, regSPPrev, regSSPrev;
|
||||
|
||||
if (this.id == X86Seg.ID.CODE) {
|
||||
this.fStackSwitch = false;
|
||||
|
|
@ -422,93 +539,128 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
|
|||
var rpl = sel & X86.SEL.RPL;
|
||||
var dpl = (acc & X86.DESC.ACC.DPL.MASK) >> X86.DESC.ACC.DPL.SHIFT;
|
||||
var regSP;
|
||||
if (type == X86.DESC.ACC.TYPE.GATE_CALL) {
|
||||
/*
|
||||
* Since we are X86Seg.ID.CODE, we can use this.cpl instead of the more generic this.cpu.segCS.cpl
|
||||
*/
|
||||
if (rpl < this.cpl) rpl = this.cpl;
|
||||
if (rpl <= dpl) {
|
||||
cplPrev = this.cpl;
|
||||
if (this.load(base & 0xffff, true) != null) {
|
||||
this.cpu.regIP = limit;
|
||||
if (this.cpl < cplPrev) {
|
||||
if (fCall !== true) {
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
regSP = this.cpu.regSP;
|
||||
var i = 0, nWords = (acc & 0x1f);
|
||||
while (nWords--) {
|
||||
this.awScratch[i++] = this.cpu.getSOWord(this.cpu.segSS, regSP);
|
||||
regSP += 2;
|
||||
}
|
||||
addrTSS = this.cpu.segTSS.base;
|
||||
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
|
||||
offSS = offSP + 2;
|
||||
regSPPrev = this.cpu.regSP;
|
||||
regSSPrev = this.cpu.segSS.sel;
|
||||
this.cpu.regSP = this.cpu.getWord(addrTSS + offSP);
|
||||
this.cpu.segSS.load(this.cpu.getWord(addrTSS + offSS));
|
||||
this.cpu.pushWord(regSSPrev);
|
||||
this.cpu.pushWord(regSPPrev);
|
||||
while (i) this.cpu.pushWord(this.awScratch[--i]);
|
||||
this.fStackSwitch = true;
|
||||
}
|
||||
return this.base;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE_INT || type == X86.DESC.ACC.TYPE.GATE_TRAP) {
|
||||
if (rpl < this.cpl) rpl = this.cpl;
|
||||
if (rpl <= dpl) {
|
||||
cplPrev = this.cpl;
|
||||
if (this.load(base & 0xffff, true) != null) {
|
||||
this.cpu.regIP = limit;
|
||||
if (this.cpl < cplPrev) {
|
||||
if (fCall !== true) {
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
regSP = this.cpu.regSP;
|
||||
addrTSS = this.cpu.segTSS.base;
|
||||
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
|
||||
offSS = offSP + 2;
|
||||
regSPPrev = this.cpu.regSP;
|
||||
regSSPrev = this.cpu.segSS.sel;
|
||||
this.cpu.regSP = this.cpu.getWord(addrTSS + offSP);
|
||||
this.cpu.segSS.load(this.cpu.getWord(addrTSS + offSS));
|
||||
this.cpu.pushWord(regSSPrev);
|
||||
this.cpu.pushWord(regSPPrev);
|
||||
this.fStackSwitch = true;
|
||||
}
|
||||
if (type == X86.DESC.ACC.TYPE.GATE_INT) {
|
||||
this.cpu.regPS &= ~(X86.PS.NT | X86.PS.TF | X86.PS.IF);
|
||||
} else {
|
||||
this.cpu.regPS &= ~(X86.PS.NT | X86.PS.TF);
|
||||
}
|
||||
return this.base;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (type >= X86.DESC.ACC.TYPE.CODE_EXECONLY /* || dpl > this.cpu.segCS.cpl */) {
|
||||
if (type >= X86.DESC.ACC.TYPE.CODE_EXECONLY /* || dpl > cpu.segCS.cpl */) {
|
||||
rpl = sel & X86.SEL.RPL;
|
||||
if (rpl > this.cpl) {
|
||||
if (fCall !== false) {
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
regSP = this.cpu.popWord();
|
||||
this.cpu.segSS.load(this.cpu.popWord());
|
||||
this.cpu.regSP = regSP;
|
||||
regSP = cpu.popWord();
|
||||
cpu.segSS.load(cpu.popWord());
|
||||
cpu.regSP = regSP;
|
||||
this.fStackSwitch = true;
|
||||
}
|
||||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE_CALL) {
|
||||
/*
|
||||
* Since we are X86Seg.ID.CODE, we can use this.cpl instead of the more generic cpu.segCS.cpl
|
||||
*/
|
||||
selCode = base & 0xffff;
|
||||
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) {
|
||||
rpl = dpl;
|
||||
}
|
||||
}
|
||||
if (rpl <= dpl) {
|
||||
cplPrev = this.cpl;
|
||||
if (this.load(selCode, true) == null) {
|
||||
if (DEBUG) cpu.assert(false);
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
cpu.regIP = limit;
|
||||
if (this.cpl < cplPrev) {
|
||||
if (fCall !== true) {
|
||||
if (DEBUG) cpu.assert(false);
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
regSP = cpu.regSP;
|
||||
var i = 0, nWords = (acc & 0x1f);
|
||||
while (nWords--) {
|
||||
this.awScratch[i++] = cpu.getSOWord(cpu.segSS, regSP);
|
||||
regSP += 2;
|
||||
}
|
||||
addrTSS = cpu.segTSS.base;
|
||||
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
|
||||
offSS = offSP + 2;
|
||||
regSPPrev = cpu.regSP;
|
||||
regSSPrev = cpu.segSS.sel;
|
||||
cpu.regSP = cpu.getWord(addrTSS + offSP);
|
||||
cpu.segSS.load(cpu.getWord(addrTSS + offSS));
|
||||
cpu.pushWord(regSSPrev);
|
||||
cpu.pushWord(regSPPrev);
|
||||
while (i) cpu.pushWord(this.awScratch[--i]);
|
||||
this.fStackSwitch = true;
|
||||
}
|
||||
return this.base;
|
||||
}
|
||||
if (DEBUG) cpu.assert(false);
|
||||
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel, true);
|
||||
base = null;
|
||||
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) {
|
||||
dpl = this.cpl;
|
||||
}
|
||||
}
|
||||
if (dpl <= this.cpl) {
|
||||
cplPrev = this.cpl;
|
||||
if (this.load(selCode, true) == null) {
|
||||
if (DEBUG) cpu.assert(false);
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
cpu.regIP = limit;
|
||||
if (this.cpl < cplPrev) {
|
||||
if (fCall !== true) {
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
regSP = cpu.regSP;
|
||||
addrTSS = cpu.segTSS.base;
|
||||
offSP = (this.cpl << 2) + X86.TSS.CPL0_SP;
|
||||
offSS = offSP + 2;
|
||||
regSPPrev = cpu.regSP;
|
||||
regSSPrev = cpu.segSS.sel;
|
||||
cpu.regSP = cpu.getWord(addrTSS + offSP);
|
||||
cpu.segSS.load(cpu.getWord(addrTSS + offSS));
|
||||
cpu.pushWord(regSSPrev);
|
||||
cpu.pushWord(regSPPrev);
|
||||
this.fStackSwitch = true;
|
||||
}
|
||||
if (type == X86.DESC.ACC.TYPE.GATE_INT) {
|
||||
cpu.regPS &= ~(X86.PS.NT | X86.PS.TF | X86.PS.IF);
|
||||
} else {
|
||||
cpu.regPS &= ~(X86.PS.NT | X86.PS.TF);
|
||||
}
|
||||
return this.base;
|
||||
}
|
||||
if (DEBUG) cpu.assert(false);
|
||||
X86Help.opHelpFault.call(cpu, X86.EXCEPTION.GP_FAULT, sel | X86.ERRCODE.EXT, true);
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
else if (type == X86.DESC.ACC.TYPE.GATE_TASK) {
|
||||
if (!X86Seg.switchTSS.call(this, base & 0xffff, true)) {
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
return this.base;
|
||||
}
|
||||
else {
|
||||
X86Help.opHelpFault.call(this.cpu, X86.EXCEPTION.GP_FAULT, sel, true);
|
||||
base = null;
|
||||
break;
|
||||
}
|
||||
if (DEBUG) this.cpu.assert(!!selMasked); // a null CS selector should be caught by the final preceding check
|
||||
if (DEBUG) cpu.assert(!!selMasked); // a null CS selector should be caught by the final preceding check
|
||||
}
|
||||
else if (this.id == X86Seg.ID.DATA) {
|
||||
if (selMasked) {
|
||||
|
|
@ -548,7 +700,7 @@ X86Seg.prototype.loadDesc8 = function(sel, addrDesc)
|
|||
this.acc = acc;
|
||||
this.type = type;
|
||||
this.addrDesc = addrDesc;
|
||||
this.updateAccess();
|
||||
this.updateMode();
|
||||
break;
|
||||
}
|
||||
this.messageDebugger(sel, base, limit, acc, ext);
|
||||
|
|
@ -612,7 +764,7 @@ X86Seg.prototype.restore = function(a)
|
|||
};
|
||||
|
||||
/**
|
||||
* updateAccess(fProt)
|
||||
* updateMode(fProt)
|
||||
*
|
||||
* Ensures that the segment register's access (ie, load and check methods) matches the specified (or current)
|
||||
* operating mode (real or protected).
|
||||
|
|
@ -621,7 +773,7 @@ X86Seg.prototype.restore = function(a)
|
|||
* @param {boolean} [fProt] true for protected-mode access, false for real-mode access, undefined for current mode
|
||||
* @return {boolean}
|
||||
*/
|
||||
X86Seg.prototype.updateAccess = function(fProt)
|
||||
X86Seg.prototype.updateMode = function(fProt)
|
||||
{
|
||||
if (fProt === undefined) {
|
||||
fProt = !!(this.cpu.regMSW & X86.MSW.PE);
|
||||
|
|
@ -684,7 +836,7 @@ X86Seg.prototype.messageDebugger = function(sel, base, limit, acc, ext)
|
|||
if (this.id == X86Seg.ID.CODE) sDPL += " cpl=" + this.cpl;
|
||||
this.cpu.messageDebugger("loadSeg(" + this.sName + "):" + ch + "sel=" + str.toHexWord(sel) + " base=" + str.toHex(base) + " limit=" + str.toHexWord(limit) + " acc=" + str.toHexWord(acc) + sDPL, Debugger.MESSAGE.SEG);
|
||||
}
|
||||
this.cpu.assert(base != null && (!ext || ext == X86.DESC.EXT.AVAIL));
|
||||
this.cpu.assert(/* base != null && */ (!ext || ext == X86.DESC.EXT.AVAIL));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ function Component(type, parms, constructor)
|
|||
* Gather all the various component flags (booleans) into a single "flags" object, and encourage
|
||||
* subclasses to do the same, to reduce the property clutter we have to wade through while debugging.
|
||||
*/
|
||||
this.aFlags = {
|
||||
this.bitField = {
|
||||
fReady: false,
|
||||
fBusy: false,
|
||||
fBusyCancel: false,
|
||||
|
|
@ -784,7 +784,7 @@ Component.prototype = {
|
|||
* @param {string} s describes a fatal error condition
|
||||
*/
|
||||
setError: function(s) {
|
||||
this.aFlags.fError = true;
|
||||
this.bitField.fError = true;
|
||||
this.notice("Fatal error: " + s);
|
||||
},
|
||||
/**
|
||||
|
|
@ -795,7 +795,7 @@ Component.prototype = {
|
|||
* @this {Component}
|
||||
*/
|
||||
clearError: function() {
|
||||
this.aFlags.fError = false;
|
||||
this.bitField.fError = false;
|
||||
},
|
||||
/**
|
||||
* isError()
|
||||
|
|
@ -806,7 +806,7 @@ Component.prototype = {
|
|||
* @return {boolean} true if a fatal error condition exists, false if not
|
||||
*/
|
||||
isError: function() {
|
||||
if (this.aFlags.fError) {
|
||||
if (this.bitField.fError) {
|
||||
this.println(this.toString() + " error");
|
||||
return true;
|
||||
}
|
||||
|
|
@ -827,14 +827,14 @@ Component.prototype = {
|
|||
*/
|
||||
isReady: function(fnReady) {
|
||||
if (fnReady) {
|
||||
if (this.aFlags.fReady) {
|
||||
if (this.bitField.fReady) {
|
||||
fnReady();
|
||||
} else {
|
||||
if (DEBUG) this.log("NOT ready");
|
||||
this.fnReady = fnReady;
|
||||
}
|
||||
}
|
||||
return this.aFlags.fReady;
|
||||
return this.bitField.fReady;
|
||||
},
|
||||
/**
|
||||
* setReady(fReady)
|
||||
|
|
@ -845,9 +845,9 @@ Component.prototype = {
|
|||
* @param {boolean} [fReady] is assumed to indicate "ready" unless EXPLICITLY set to false
|
||||
*/
|
||||
setReady: function(fReady) {
|
||||
if (!this.aFlags.fError) {
|
||||
this.aFlags.fReady = (fReady !== false);
|
||||
if (this.aFlags.fReady) {
|
||||
if (!this.bitField.fError) {
|
||||
this.bitField.fReady = (fReady !== false);
|
||||
if (this.bitField.fReady) {
|
||||
if (DEBUG || this.name) this.log("ready");
|
||||
var fnReady = this.fnReady;
|
||||
this.fnReady = null;
|
||||
|
|
@ -865,14 +865,14 @@ Component.prototype = {
|
|||
* @return {boolean} true if "busy", false if not
|
||||
*/
|
||||
isBusy: function(fCancel) {
|
||||
if (this.aFlags.fBusy) {
|
||||
if (this.bitField.fBusy) {
|
||||
if (fCancel) {
|
||||
this.aFlags.fBusyCancel = true;
|
||||
this.bitField.fBusyCancel = true;
|
||||
} else if (fCancel === undefined) {
|
||||
this.println(this.toString() + " busy");
|
||||
}
|
||||
}
|
||||
return this.aFlags.fBusy;
|
||||
return this.bitField.fBusy;
|
||||
},
|
||||
/**
|
||||
* setBusy(fBusy)
|
||||
|
|
@ -884,19 +884,19 @@ Component.prototype = {
|
|||
* @return {boolean}
|
||||
*/
|
||||
setBusy: function(fBusy) {
|
||||
if (this.aFlags.fBusyCancel) {
|
||||
if (this.aFlags.fBusy) {
|
||||
this.aFlags.fBusy = false;
|
||||
if (this.bitField.fBusyCancel) {
|
||||
if (this.bitField.fBusy) {
|
||||
this.bitField.fBusy = false;
|
||||
}
|
||||
this.aFlags.fBusyCancel = false;
|
||||
this.bitField.fBusyCancel = false;
|
||||
return false;
|
||||
}
|
||||
if (this.aFlags.fError) {
|
||||
if (this.bitField.fError) {
|
||||
this.println(this.toString() + " error");
|
||||
return false;
|
||||
}
|
||||
this.aFlags.fBusy = fBusy;
|
||||
return this.aFlags.fBusy;
|
||||
this.bitField.fBusy = fBusy;
|
||||
return this.bitField.fBusy;
|
||||
},
|
||||
/**
|
||||
* powerUp(fSave)
|
||||
|
|
@ -907,7 +907,7 @@ Component.prototype = {
|
|||
* @return {boolean} true if successful, false if failure
|
||||
*/
|
||||
powerUp: function(data, fRepower) {
|
||||
this.aFlags.fPowered = true;
|
||||
this.bitField.fPowered = true;
|
||||
return true;
|
||||
},
|
||||
/**
|
||||
|
|
@ -919,7 +919,7 @@ Component.prototype = {
|
|||
* @return {Object|boolean} component state if fSave; otherwise, true if successful, false if failure
|
||||
*/
|
||||
powerDown: function(fSave, fShutdown) {
|
||||
if (fShutdown) this.aFlags.fPowered = false;
|
||||
if (fShutdown) this.bitField.fPowered = false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue