Updated addTimer() interface to include an ID; cleaned up how the CPU tracks the base speed, current speed, and target speed
This commit is contained in:
parent
f71b4d648c
commit
e5a9b01dde
18 changed files with 2403 additions and 2387 deletions
|
|
@ -11,6 +11,7 @@
|
|||
<cpu id="cpu8088" model="8088" autostart="true" pos="left" padLeft="8px" padBottom="8px">
|
||||
<control type="button" binding="run">Run</control>
|
||||
<control type="button" binding="reset">Reset</control>
|
||||
<control type="button" binding="setSpeed">Speed</control>
|
||||
</cpu>
|
||||
<keyboard ref="/devices/pcx86/keyboard/us83-buttons-minimal.xml"/>
|
||||
<chipset id="chipset" model="5150" sw1="01000001" sw2="10011000"/>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<cpu id="cpu8088" model="8088" autostart="true" pos="left" padLeft="8px" padBottom="8px">
|
||||
<control type="button" binding="run">Run</control>
|
||||
<control type="button" binding="reset">Reset</control>
|
||||
<control type="button" binding="setSpeed">Speed</control>
|
||||
</cpu>
|
||||
<keyboard ref="/devices/pcx86/keyboard/us83-buttons-arrows.xml"/>
|
||||
<chipset id="chipset" model="5150" sw1="01000001" sw2="11111000"/>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -76,21 +76,21 @@ function CPU(parmsCPU, nCyclesDefault)
|
|||
|
||||
var nMultiplier = parmsCPU['multiplier'] || 1;
|
||||
|
||||
this.aCounts = {};
|
||||
this.aCounts.nCyclesPerSecond = nCycles;
|
||||
this.aCounts.nVideoUpdates = 0;
|
||||
this.counts = {};
|
||||
this.counts.nCyclesPerSecond = nCycles;
|
||||
this.counts.nVideoUpdates = 0;
|
||||
|
||||
/*
|
||||
* nCyclesMultiplier replaces the old "speed" variable (0, 1, 2) and eliminates the need for
|
||||
* the constants (SPEED_SLOW, SPEED_FAST and SPEED_MAX). The UI simply doubles the multiplier
|
||||
* until we've exceeded the host's speed limit and then starts the multiplier over at 1.
|
||||
*/
|
||||
this.aCounts.nCyclesMultiplier = nMultiplier;
|
||||
this.aCounts.mhzDefault = Math.round(this.aCounts.nCyclesPerSecond / 10000) / 100;
|
||||
this.counts.nCyclesMultiplier = nMultiplier;
|
||||
this.counts.mhzDefault = Math.round(this.counts.nCyclesPerSecond / 10000) / 100;
|
||||
/*
|
||||
* TODO: Take care of this with an initial setSpeed() call instead?
|
||||
*/
|
||||
this.aCounts.mhzTarget = this.aCounts.mhzDefault * this.aCounts.nCyclesMultiplier;
|
||||
this.counts.mhzTarget = this.counts.mhzDefault * this.counts.nCyclesMultiplier;
|
||||
|
||||
/*
|
||||
* We add a number of flags to the set initialized by Component
|
||||
|
|
@ -114,10 +114,10 @@ function CPU(parmsCPU, nCyclesDefault)
|
|||
* and call resetChecksum().
|
||||
*/
|
||||
this.flags.checksum = false;
|
||||
this.aCounts.nChecksum = this.aCounts.nCyclesChecksumNext = 0;
|
||||
this.aCounts.nCyclesChecksumStart = parmsCPU["csStart"];
|
||||
this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"];
|
||||
this.aCounts.nCyclesChecksumStop = parmsCPU["csStop"];
|
||||
this.counts.nChecksum = this.counts.nCyclesChecksumNext = 0;
|
||||
this.counts.nCyclesChecksumStart = parmsCPU["csStart"];
|
||||
this.counts.nCyclesChecksumInterval = parmsCPU["csInterval"];
|
||||
this.counts.nCyclesChecksumStop = parmsCPU["csStop"];
|
||||
|
||||
this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); };
|
||||
|
||||
|
|
@ -199,7 +199,7 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
*/
|
||||
CPU.prototype.reset = function()
|
||||
{
|
||||
this.aCounts.nVideoUpdates = 0;
|
||||
this.counts.nVideoUpdates = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -367,13 +367,13 @@ CPU.prototype.getChecksum = function()
|
|||
*/
|
||||
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.flags.checksum = (this.aCounts.nCyclesChecksumStart >= 0 && this.aCounts.nCyclesChecksumInterval > 0);
|
||||
if (this.counts.nCyclesChecksumStart === undefined) this.counts.nCyclesChecksumStart = 0;
|
||||
if (this.counts.nCyclesChecksumInterval === undefined) this.counts.nCyclesChecksumInterval = -1;
|
||||
if (this.counts.nCyclesChecksumStop === undefined) this.counts.nCyclesChecksumStop = -1;
|
||||
this.flags.checksum = (this.counts.nCyclesChecksumStart >= 0 && this.counts.nCyclesChecksumInterval > 0);
|
||||
if (this.flags.checksum) {
|
||||
this.aCounts.nChecksum = 0;
|
||||
this.aCounts.nCyclesChecksumNext = this.aCounts.nCyclesChecksumStart - this.nTotalCycles;
|
||||
this.counts.nChecksum = 0;
|
||||
this.counts.nCyclesChecksumNext = this.counts.nCyclesChecksumStart - this.nTotalCycles;
|
||||
/*
|
||||
* this.aCounts.nCyclesChecksumNext = this.aCounts.nCyclesChecksumStart + this.aCounts.nCyclesChecksumInterval -
|
||||
* (this.nTotalCycles % this.aCounts.nCyclesChecksumInterval);
|
||||
|
|
@ -401,15 +401,15 @@ CPU.prototype.updateChecksum = function(nCycles)
|
|||
* Get a 32-bit summation of the current CPU state and add it to our running 32-bit checksum
|
||||
*/
|
||||
var fDisplay = false;
|
||||
this.aCounts.nChecksum = (this.aCounts.nChecksum + this.getChecksum())|0;
|
||||
this.aCounts.nCyclesChecksumNext -= nCycles;
|
||||
if (this.aCounts.nCyclesChecksumNext <= 0) {
|
||||
this.aCounts.nCyclesChecksumNext += this.aCounts.nCyclesChecksumInterval;
|
||||
this.counts.nChecksum = (this.counts.nChecksum + this.getChecksum())|0;
|
||||
this.counts.nCyclesChecksumNext -= nCycles;
|
||||
if (this.counts.nCyclesChecksumNext <= 0) {
|
||||
this.counts.nCyclesChecksumNext += this.counts.nCyclesChecksumInterval;
|
||||
fDisplay = true;
|
||||
}
|
||||
if (this.aCounts.nCyclesChecksumStop >= 0) {
|
||||
if (this.aCounts.nCyclesChecksumStop <= this.getCycles()) {
|
||||
this.aCounts.nCyclesChecksumInterval = this.aCounts.nCyclesChecksumStop = -1;
|
||||
if (this.counts.nCyclesChecksumStop >= 0) {
|
||||
if (this.counts.nCyclesChecksumStop <= this.getCycles()) {
|
||||
this.counts.nCyclesChecksumInterval = this.counts.nCyclesChecksumStop = -1;
|
||||
this.resetChecksum();
|
||||
this.stopCPU();
|
||||
fDisplay = true;
|
||||
|
|
@ -430,7 +430,7 @@ CPU.prototype.updateChecksum = function(nCycles)
|
|||
*/
|
||||
CPU.prototype.displayChecksum = function()
|
||||
{
|
||||
this.println(this.getCycles() + " cycles: " + "checksum=" + str.toHex(this.aCounts.nChecksum));
|
||||
this.println(this.getCycles() + " cycles: " + "checksum=" + str.toHex(this.counts.nChecksum));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -514,7 +514,7 @@ CPU.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
case "setSpeed":
|
||||
this.bindings[sBinding] = control;
|
||||
control.onclick = function onClickSetSpeed() {
|
||||
cpu.setSpeed(cpu.aCounts.nCyclesMultiplier << 1, true);
|
||||
cpu.setSpeed(cpu.counts.nCyclesMultiplier << 1, true);
|
||||
};
|
||||
control.textContent = this.getSpeedTarget();
|
||||
fBound = true;
|
||||
|
|
@ -611,26 +611,26 @@ CPU.prototype.calcCycles = function(fRecalc)
|
|||
*/
|
||||
var vMultiplier = 1;
|
||||
if (fRecalc) {
|
||||
if (this.aCounts.nCyclesMultiplier > 1 && this.aCounts.mhz) {
|
||||
vMultiplier = (this.aCounts.mhz / this.aCounts.mhzDefault);
|
||||
if (this.counts.nCyclesMultiplier > 1 && this.counts.mhz) {
|
||||
vMultiplier = (this.counts.mhz / this.counts.mhzDefault);
|
||||
}
|
||||
}
|
||||
|
||||
this.aCounts.msPerYield = Math.round(1000 / CPU.YIELDS_PER_SECOND);
|
||||
this.aCounts.nCyclesPerBurst = Math.floor(this.aCounts.nCyclesPerSecond / nMostUpdatesPerSecond * vMultiplier);
|
||||
this.aCounts.nCyclesPerYield = Math.floor(this.aCounts.nCyclesPerSecond / CPU.YIELDS_PER_SECOND * vMultiplier);
|
||||
this.aCounts.nCyclesPerVideoUpdate = Math.floor(this.aCounts.nCyclesPerSecond / this.refreshRate * vMultiplier);
|
||||
this.aCounts.nCyclesPerStatusUpdate = Math.floor(this.aCounts.nCyclesPerSecond / CPU.STATUS_UPDATES_PER_SECOND * vMultiplier);
|
||||
this.counts.msPerYield = Math.round(1000 / CPU.YIELDS_PER_SECOND);
|
||||
this.counts.nCyclesPerBurst = Math.floor(this.counts.nCyclesPerSecond / nMostUpdatesPerSecond * vMultiplier);
|
||||
this.counts.nCyclesPerYield = Math.floor(this.counts.nCyclesPerSecond / CPU.YIELDS_PER_SECOND * vMultiplier);
|
||||
this.counts.nCyclesPerVideoUpdate = Math.floor(this.counts.nCyclesPerSecond / this.refreshRate * vMultiplier);
|
||||
this.counts.nCyclesPerStatusUpdate = Math.floor(this.counts.nCyclesPerSecond / CPU.STATUS_UPDATES_PER_SECOND * vMultiplier);
|
||||
|
||||
/*
|
||||
* And initialize "next" yield, video update, and status update cycle "threshold" counters to those "per" values
|
||||
*/
|
||||
if (!fRecalc) {
|
||||
this.aCounts.nCyclesNextYield = this.aCounts.nCyclesPerYield;
|
||||
this.aCounts.nCyclesNextVideoUpdate = this.aCounts.nCyclesPerVideoUpdate;
|
||||
this.aCounts.nCyclesNextStatusUpdate = this.aCounts.nCyclesPerStatusUpdate;
|
||||
this.counts.nCyclesNextYield = this.counts.nCyclesPerYield;
|
||||
this.counts.nCyclesNextVideoUpdate = this.counts.nCyclesPerVideoUpdate;
|
||||
this.counts.nCyclesNextStatusUpdate = this.counts.nCyclesPerStatusUpdate;
|
||||
}
|
||||
this.aCounts.nCyclesRecalc = 0;
|
||||
this.counts.nCyclesRecalc = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -653,7 +653,7 @@ CPU.prototype.calcCycles = function(fRecalc)
|
|||
CPU.prototype.getCycles = function(fScaled)
|
||||
{
|
||||
var nCycles = this.nTotalCycles + this.nRunCycles + this.nBurstCycles - this.nStepCycles;
|
||||
if (fScaled && this.aCounts.nCyclesMultiplier > 1 && this.aCounts.mhz > this.aCounts.mhzDefault) {
|
||||
if (fScaled && this.counts.nCyclesMultiplier > 1 && this.counts.mhz > this.counts.mhzDefault) {
|
||||
/*
|
||||
* We could scale the current cycle count by the current effective speed (this.aCounts.mhz); eg:
|
||||
*
|
||||
|
|
@ -672,7 +672,7 @@ CPU.prototype.getCycles = function(fScaled)
|
|||
* interface allows any value, as does the CPU "multiplier" parmsCPU property (from the machine's
|
||||
* XML file).
|
||||
*/
|
||||
nCycles = Math.round(nCycles / this.aCounts.nCyclesMultiplier);
|
||||
nCycles = Math.round(nCycles / this.counts.nCyclesMultiplier);
|
||||
}
|
||||
return nCycles;
|
||||
};
|
||||
|
|
@ -687,7 +687,7 @@ CPU.prototype.getCycles = function(fScaled)
|
|||
*/
|
||||
CPU.prototype.getCyclesPerSecond = function()
|
||||
{
|
||||
return this.aCounts.nCyclesPerSecond;
|
||||
return this.counts.nCyclesPerSecond;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -701,7 +701,7 @@ CPU.prototype.getCyclesPerSecond = function()
|
|||
*/
|
||||
CPU.prototype.resetCycles = function()
|
||||
{
|
||||
this.aCounts.mhz = 0;
|
||||
this.counts.mhz = 0;
|
||||
this.nTotalCycles = this.nRunCycles = this.nBurstCycles = this.nStepCycles = 0;
|
||||
this.resetChecksum();
|
||||
this.setSpeed(1);
|
||||
|
|
@ -715,7 +715,7 @@ CPU.prototype.resetCycles = function()
|
|||
*/
|
||||
CPU.prototype.getSpeed = function()
|
||||
{
|
||||
return this.aCounts.nCyclesMultiplier;
|
||||
return this.counts.nCyclesMultiplier;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -729,7 +729,7 @@ CPU.prototype.getSpeedCurrent = function()
|
|||
/*
|
||||
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
|
||||
*/
|
||||
return ((this.flags.running && this.aCounts.mhz)? (this.aCounts.mhz.toFixed(2) + "Mhz") : "Stopped");
|
||||
return ((this.flags.running && this.counts.mhz)? (this.counts.mhz.toFixed(2) + "Mhz") : "Stopped");
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -743,7 +743,7 @@ CPU.prototype.getSpeedTarget = function()
|
|||
/*
|
||||
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
|
||||
*/
|
||||
return this.aCounts.mhzTarget.toFixed(2) + "Mhz";
|
||||
return this.counts.mhzTarget.toFixed(2) + "Mhz";
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -767,15 +767,15 @@ CPU.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
|
|||
/*
|
||||
* If we haven't reached 80% (0.8) of the current target speed, revert to a multiplier of one (1).
|
||||
*/
|
||||
if (this.aCounts.mhz / this.aCounts.mhzTarget < 0.8) {
|
||||
if (this.counts.mhz / this.counts.mhzTarget < 0.8) {
|
||||
nMultiplier = 1;
|
||||
} else {
|
||||
fSuccess = true;
|
||||
}
|
||||
this.aCounts.nCyclesMultiplier = nMultiplier;
|
||||
var mhz = this.aCounts.mhzDefault * this.aCounts.nCyclesMultiplier;
|
||||
if (this.aCounts.mhzTarget != mhz) {
|
||||
this.aCounts.mhzTarget = mhz;
|
||||
this.counts.nCyclesMultiplier = nMultiplier;
|
||||
var mhz = this.counts.mhzDefault * this.counts.nCyclesMultiplier;
|
||||
if (this.counts.mhzTarget != mhz) {
|
||||
this.counts.mhzTarget = mhz;
|
||||
var sSpeed = this.getSpeedTarget();
|
||||
var controlSpeed = this.bindings["setSpeed"];
|
||||
if (controlSpeed) controlSpeed.textContent = sSpeed;
|
||||
|
|
@ -785,8 +785,8 @@ CPU.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
|
|||
}
|
||||
this.addCycles(this.nRunCycles);
|
||||
this.nRunCycles = 0;
|
||||
this.aCounts.msStartRun = usr.getTime();
|
||||
this.aCounts.msEndThisRun = 0;
|
||||
this.counts.msStartRun = usr.getTime();
|
||||
this.counts.msEndThisRun = 0;
|
||||
this.calcCycles();
|
||||
return fSuccess;
|
||||
};
|
||||
|
|
@ -801,7 +801,7 @@ CPU.prototype.setSpeed = function(nMultiplier, fUpdateFocus)
|
|||
CPU.prototype.calcSpeed = function(nCycles, msElapsed)
|
||||
{
|
||||
if (msElapsed) {
|
||||
this.aCounts.mhz = Math.round(nCycles / (msElapsed * 10)) / 100;
|
||||
this.counts.mhz = Math.round(nCycles / (msElapsed * 10)) / 100;
|
||||
if (msElapsed >= 86400000) {
|
||||
this.nTotalCycles = 0;
|
||||
this.setSpeed(); // reset all counters once per day so that we never have to worry about overflow
|
||||
|
|
@ -816,11 +816,11 @@ CPU.prototype.calcSpeed = function(nCycles, msElapsed)
|
|||
*/
|
||||
CPU.prototype.calcStartTime = function()
|
||||
{
|
||||
if (this.aCounts.nCyclesRecalc >= this.aCounts.nCyclesPerSecond) {
|
||||
if (this.counts.nCyclesRecalc >= this.counts.nCyclesPerSecond) {
|
||||
this.calcCycles(true);
|
||||
}
|
||||
this.aCounts.nCyclesThisRun = 0;
|
||||
this.aCounts.msStartThisRun = usr.getTime();
|
||||
this.counts.nCyclesThisRun = 0;
|
||||
this.counts.msStartThisRun = usr.getTime();
|
||||
|
||||
/*
|
||||
* Try to detect situations where the browser may have throttled us, such as when the user switches
|
||||
|
|
@ -847,19 +847,19 @@ CPU.prototype.calcStartTime = function()
|
|||
* to hit its target speed, since you would expect any instruction that displays a message to be an
|
||||
* EXTREMELY slow instruction.
|
||||
*/
|
||||
if (this.aCounts.msEndThisRun) {
|
||||
var msDelta = this.aCounts.msStartThisRun - this.aCounts.msEndThisRun;
|
||||
if (msDelta > this.aCounts.msPerYield) {
|
||||
if (this.counts.msEndThisRun) {
|
||||
var msDelta = this.counts.msStartThisRun - this.counts.msEndThisRun;
|
||||
if (msDelta > this.counts.msPerYield) {
|
||||
if (MAXDEBUG) this.println("large time delay: " + msDelta + "ms");
|
||||
this.aCounts.msStartRun += msDelta;
|
||||
this.counts.msStartRun += msDelta;
|
||||
/*
|
||||
* Bumping msStartRun forward should NEVER cause it to exceed msStartThisRun; however, just
|
||||
* in case, I make absolutely sure it cannot happen, since doing so could result in negative
|
||||
* speed calculations.
|
||||
*/
|
||||
this.assert(this.aCounts.msStartRun <= this.aCounts.msStartThisRun);
|
||||
if (this.aCounts.msStartRun > this.aCounts.msStartThisRun) {
|
||||
this.aCounts.msStartRun = this.aCounts.msStartThisRun;
|
||||
this.assert(this.counts.msStartRun <= this.counts.msStartThisRun);
|
||||
if (this.counts.msStartRun > this.counts.msStartThisRun) {
|
||||
this.counts.msStartRun = this.counts.msStartThisRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -873,20 +873,20 @@ CPU.prototype.calcStartTime = function()
|
|||
*/
|
||||
CPU.prototype.calcRemainingTime = function()
|
||||
{
|
||||
this.aCounts.msEndThisRun = usr.getTime();
|
||||
this.counts.msEndThisRun = usr.getTime();
|
||||
|
||||
var msYield = this.aCounts.msPerYield;
|
||||
if (this.aCounts.nCyclesThisRun) {
|
||||
var msYield = this.counts.msPerYield;
|
||||
if (this.counts.nCyclesThisRun) {
|
||||
/*
|
||||
* Normally, we would assume we executed a full quota of work over msPerYield, but since the CPU
|
||||
* now has the option of calling yieldCPU(), that might not be true. If nCyclesThisRun is correct, then
|
||||
* the ratio of nCyclesThisRun/nCyclesPerYield should represent the percentage of work we performed,
|
||||
* and so applying that percentage to msPerYield should give us a better estimate of work vs. time.
|
||||
*/
|
||||
msYield = Math.round(msYield * this.aCounts.nCyclesThisRun / this.aCounts.nCyclesPerYield);
|
||||
msYield = Math.round(msYield * this.counts.nCyclesThisRun / this.counts.nCyclesPerYield);
|
||||
}
|
||||
|
||||
var msElapsedThisRun = this.aCounts.msEndThisRun - this.aCounts.msStartThisRun;
|
||||
var msElapsedThisRun = this.counts.msEndThisRun - this.counts.msStartThisRun;
|
||||
var msRemainsThisRun = msYield - msElapsedThisRun;
|
||||
|
||||
/*
|
||||
|
|
@ -905,15 +905,15 @@ CPU.prototype.calcRemainingTime = function()
|
|||
* to insure that we display a smooth, constant N Mhz. But for now, I prefer seeing any fluctuations.
|
||||
*/
|
||||
var nCycles = this.nRunCycles;
|
||||
var msElapsed = this.aCounts.msEndThisRun - this.aCounts.msStartRun;
|
||||
var msElapsed = this.counts.msEndThisRun - this.counts.msStartRun;
|
||||
|
||||
if (MAXDEBUG && msRemainsThisRun < 0 && this.aCounts.nCyclesMultiplier > 1) {
|
||||
if (MAXDEBUG && msRemainsThisRun < 0 && this.counts.nCyclesMultiplier > 1) {
|
||||
this.println("warning: updates @" + msElapsedThisRun + "ms (prefer " + Math.round(msYield) + "ms)");
|
||||
}
|
||||
|
||||
this.calcSpeed(nCycles, msElapsed);
|
||||
|
||||
if (msRemainsThisRun < 0 || this.aCounts.mhz < this.aCounts.mhzTarget) {
|
||||
if (msRemainsThisRun < 0 || this.counts.mhz < this.counts.mhzTarget) {
|
||||
/*
|
||||
* If the last burst took MORE time than we allotted (ie, it's taking more than 1 second to simulate
|
||||
* nCyclesPerSecond), all we can do is yield for as little time as possible (ie, 0ms) and hope that the
|
||||
|
|
@ -926,13 +926,13 @@ CPU.prototype.calcRemainingTime = function()
|
|||
* Last but not least, update nCyclesRecalc, so that when runCPU() starts up again and calls calcStartTime(),
|
||||
* it'll be ready to decide if calcCycles() should be called again.
|
||||
*/
|
||||
this.aCounts.nCyclesRecalc += this.aCounts.nCyclesThisRun;
|
||||
this.counts.nCyclesRecalc += this.counts.nCyclesThisRun;
|
||||
|
||||
if (DEBUG && this.messageEnabled(Messages.LOG) && msRemainsThisRun) {
|
||||
this.log("calcRemainingTime: " + msRemainsThisRun + "ms to sleep after " + this.aCounts.msEndThisRun + "ms");
|
||||
this.log("calcRemainingTime: " + msRemainsThisRun + "ms to sleep after " + this.counts.msEndThisRun + "ms");
|
||||
}
|
||||
|
||||
this.aCounts.msEndThisRun += msRemainsThisRun;
|
||||
this.counts.msEndThisRun += msRemainsThisRun;
|
||||
return msRemainsThisRun;
|
||||
};
|
||||
|
||||
|
|
@ -959,7 +959,7 @@ CPU.prototype.runCPU = function(fUpdateFocus)
|
|||
this.calcStartTime();
|
||||
try {
|
||||
do {
|
||||
var nCyclesPerBurst = (this.flags.checksum? 1 : this.aCounts.nCyclesPerBurst);
|
||||
var nCyclesPerBurst = (this.flags.checksum? 1 : this.counts.nCyclesPerBurst);
|
||||
|
||||
/*
|
||||
* nCyclesPerBurst is how many cycles we WANT to run on each iteration of stepCPU(), but it may run
|
||||
|
|
@ -974,26 +974,26 @@ CPU.prototype.runCPU = function(fUpdateFocus)
|
|||
*/
|
||||
var nCycles = this.nBurstCycles - this.nStepCycles;
|
||||
this.nRunCycles += nCycles;
|
||||
this.aCounts.nCyclesThisRun += nCycles;
|
||||
this.counts.nCyclesThisRun += nCycles;
|
||||
this.addCycles(0, true);
|
||||
this.updateChecksum(nCycles);
|
||||
|
||||
this.aCounts.nCyclesNextVideoUpdate -= nCycles;
|
||||
if (this.aCounts.nCyclesNextVideoUpdate <= 0) {
|
||||
this.aCounts.nCyclesNextVideoUpdate += this.aCounts.nCyclesPerVideoUpdate;
|
||||
if (this.cmp) this.cmp.updateVideo(this.aCounts.nVideoUpdates++);
|
||||
if (this.aCounts.nVideoUpdates > this.refreshRate) this.aCounts.nVideoUpdates = 0;
|
||||
this.counts.nCyclesNextVideoUpdate -= nCycles;
|
||||
if (this.counts.nCyclesNextVideoUpdate <= 0) {
|
||||
this.counts.nCyclesNextVideoUpdate += this.counts.nCyclesPerVideoUpdate;
|
||||
if (this.cmp) this.cmp.updateVideo(this.counts.nVideoUpdates++);
|
||||
if (this.counts.nVideoUpdates > this.refreshRate) this.counts.nVideoUpdates = 0;
|
||||
}
|
||||
|
||||
this.aCounts.nCyclesNextStatusUpdate -= nCycles;
|
||||
if (this.aCounts.nCyclesNextStatusUpdate <= 0) {
|
||||
this.aCounts.nCyclesNextStatusUpdate += this.aCounts.nCyclesPerStatusUpdate;
|
||||
this.counts.nCyclesNextStatusUpdate -= nCycles;
|
||||
if (this.counts.nCyclesNextStatusUpdate <= 0) {
|
||||
this.counts.nCyclesNextStatusUpdate += this.counts.nCyclesPerStatusUpdate;
|
||||
if (this.cmp) this.cmp.updateStatus();
|
||||
}
|
||||
|
||||
this.aCounts.nCyclesNextYield -= nCycles;
|
||||
if (this.aCounts.nCyclesNextYield <= 0) {
|
||||
this.aCounts.nCyclesNextYield += this.aCounts.nCyclesPerYield;
|
||||
this.counts.nCyclesNextYield -= nCycles;
|
||||
if (this.counts.nCyclesNextYield <= 0) {
|
||||
this.counts.nCyclesNextYield += this.counts.nCyclesPerYield;
|
||||
break;
|
||||
}
|
||||
} while (this.flags.running);
|
||||
|
|
@ -1026,7 +1026,7 @@ CPU.prototype.startCPU = function(fUpdateFocus)
|
|||
* threshold counter.
|
||||
*/
|
||||
this.setSpeed();
|
||||
if (this.cmp) this.cmp.start(this.aCounts.msStartRun, this.getCycles());
|
||||
if (this.cmp) this.cmp.start(this.counts.msStartRun, this.getCycles());
|
||||
this.flags.running = true;
|
||||
this.flags.starting = true;
|
||||
if (this.chipset) this.chipset.start();
|
||||
|
|
@ -1109,7 +1109,7 @@ CPU.prototype.updateCPU = function(fForce)
|
|||
*/
|
||||
CPU.prototype.yieldCPU = function()
|
||||
{
|
||||
this.aCounts.nCyclesNextYield = 0; // this will break us out of runCPU(), once we break out of stepCPU()
|
||||
this.counts.nCyclesNextYield = 0; // this will break us out of runCPU(), once we break out of stepCPU()
|
||||
this.nBurstCycles -= this.nStepCycles;
|
||||
this.nStepCycles = 0; // this will break us out of stepCPU()
|
||||
// if (DEBUG) this.nSnapCycles = this.nBurstCycles;
|
||||
|
|
|
|||
|
|
@ -2447,7 +2447,7 @@ if (DEBUGGER) {
|
|||
sLine += (nSequence != null? '=' + nSequence.toString() : "");
|
||||
} else {
|
||||
var nCycles = this.cpu.getCycles();
|
||||
sLine += "cycles=" + nCycles.toString() + " cs=" + str.toHex(this.cpu.aCounts.nChecksum);
|
||||
sLine += "cycles=" + nCycles.toString() + " cs=" + str.toHex(this.cpu.counts.nChecksum);
|
||||
}
|
||||
}
|
||||
return sLine;
|
||||
|
|
@ -3328,11 +3328,11 @@ if (DEBUGGER) {
|
|||
Debugger6502.prototype.doInfo = function(asArgs)
|
||||
{
|
||||
if (DEBUG) {
|
||||
this.println("msPerYield: " + this.cpu.aCounts.msPerYield);
|
||||
this.println("nCyclesPerBurst: " + this.cpu.aCounts.nCyclesPerBurst);
|
||||
this.println("nCyclesPerYield: " + this.cpu.aCounts.nCyclesPerYield);
|
||||
this.println("nCyclesPerVideoUpdate: " + this.cpu.aCounts.nCyclesPerVideoUpdate);
|
||||
this.println("nCyclesPerStatusUpdate: " + this.cpu.aCounts.nCyclesPerStatusUpdate);
|
||||
this.println("msPerYield: " + this.cpu.counts.msPerYield);
|
||||
this.println("nCyclesPerBurst: " + this.cpu.counts.nCyclesPerBurst);
|
||||
this.println("nCyclesPerYield: " + this.cpu.counts.nCyclesPerYield);
|
||||
this.println("nCyclesPerVideoUpdate: " + this.cpu.counts.nCyclesPerVideoUpdate);
|
||||
this.println("nCyclesPerStatusUpdate: " + this.cpu.counts.nCyclesPerStatusUpdate);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -3557,13 +3557,13 @@ if (DEBUGGER) {
|
|||
if (asArgs[3] !== undefined) nCycles = +asArgs[3]; // warning: decimal instead of hex conversion
|
||||
switch (asArgs[2]) {
|
||||
case "int":
|
||||
this.cpu.aCounts.nCyclesChecksumInterval = nCycles;
|
||||
this.cpu.counts.nCyclesChecksumInterval = nCycles;
|
||||
break;
|
||||
case "start":
|
||||
this.cpu.aCounts.nCyclesChecksumStart = nCycles;
|
||||
this.cpu.counts.nCyclesChecksumStart = nCycles;
|
||||
break;
|
||||
case "stop":
|
||||
this.cpu.aCounts.nCyclesChecksumStop = nCycles;
|
||||
this.cpu.counts.nCyclesChecksumStop = nCycles;
|
||||
break;
|
||||
default:
|
||||
this.println("unknown cs option");
|
||||
|
|
|
|||
|
|
@ -180,7 +180,73 @@ class ChipSet extends Component {
|
|||
* HDC calls setCMOSDriveType() or RAM calls addCMOSMemory()), the CMOS will be ready to take their calls.
|
||||
*/
|
||||
this.reset(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.cmp = cmp;
|
||||
|
||||
this.fpu = cmp.getMachineComponent("FPU");
|
||||
this.setDIPSwitches(ChipSet.SWITCH_TYPE.FPU, this.fpu?1:0, true);
|
||||
|
||||
this.kbd = cmp.getMachineComponent("Keyboard");
|
||||
|
||||
/*
|
||||
* This divisor is invariant, so we calculate it as soon as we're able to query the CPU's base speed.
|
||||
*/
|
||||
this.nTicksDivisor = (cpu.getBaseCyclesPerSecond() / ChipSet.TIMER_TICKS_PER_SEC);
|
||||
|
||||
bus.addPortInputTable(this, ChipSet.aPortInput);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutput);
|
||||
|
||||
if (this.model < ChipSet.MODEL_5170) {
|
||||
if (this.model != ChipSet.MODEL_ATT_6300) {
|
||||
bus.addPortInputTable(this, ChipSet.aPortInput5150);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutput5150);
|
||||
} else {
|
||||
bus.addPortInputTable(this, ChipSet.aPortInput6300);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutput6300);
|
||||
}
|
||||
} else {
|
||||
bus.addPortInputTable(this, ChipSet.aPortInput5170);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutput5170);
|
||||
if (DESKPRO386 && (this.model|0) == ChipSet.MODEL_COMPAQ_DESKPRO386) {
|
||||
bus.addPortInputTable(this, ChipSet.aPortInputDeskPro386);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutputDeskPro386);
|
||||
}
|
||||
}
|
||||
if (DEBUGGER) {
|
||||
if (dbg) {
|
||||
var chipset = this;
|
||||
/*
|
||||
* TODO: Add more "dumpers" (eg, for DMA, RTC, 8042, etc)
|
||||
*/
|
||||
dbg.messageDump(Messages.PIC, function onDumpPIC() {
|
||||
chipset.dumpPIC();
|
||||
});
|
||||
dbg.messageDump(Messages.TIMER, function onDumpTimer(asArgs) {
|
||||
chipset.dumpTimer(asArgs);
|
||||
});
|
||||
if (this.model >= ChipSet.MODEL_5170) {
|
||||
dbg.messageDump(Messages.CMOS, function onDumpCMOS() {
|
||||
chipset.dumpCMOS();
|
||||
});
|
||||
}
|
||||
}
|
||||
cpu.addIntNotify(Interrupts.RTC, this.intBIOSRTC.bind(this));
|
||||
}
|
||||
this.setReady();
|
||||
}
|
||||
|
||||
|
|
@ -221,70 +287,6 @@ class ChipSet extends Component {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
initBus(cmp, bus, cpu, dbg)
|
||||
{
|
||||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
this.cmp = cmp;
|
||||
|
||||
this.fpu = cmp.getMachineComponent("FPU");
|
||||
this.setDIPSwitches(ChipSet.SWITCH_TYPE.FPU, this.fpu?1:0, true);
|
||||
|
||||
this.kbd = cmp.getMachineComponent("Keyboard");
|
||||
|
||||
/*
|
||||
* This divisor is invariant, so we calculate it as soon as we're able to query the CPU's base speed.
|
||||
*/
|
||||
this.nTicksDivisor = (cpu.getCyclesPerSecond() / ChipSet.TIMER_TICKS_PER_SEC);
|
||||
|
||||
bus.addPortInputTable(this, ChipSet.aPortInput);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutput);
|
||||
if (this.model < ChipSet.MODEL_5170) {
|
||||
if (this.model != ChipSet.MODEL_ATT_6300) {
|
||||
bus.addPortInputTable(this, ChipSet.aPortInput5150);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutput5150);
|
||||
} else {
|
||||
bus.addPortInputTable(this, ChipSet.aPortInput6300);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutput6300);
|
||||
}
|
||||
} else {
|
||||
bus.addPortInputTable(this, ChipSet.aPortInput5170);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutput5170);
|
||||
if (DESKPRO386 && (this.model|0) == ChipSet.MODEL_COMPAQ_DESKPRO386) {
|
||||
bus.addPortInputTable(this, ChipSet.aPortInputDeskPro386);
|
||||
bus.addPortOutputTable(this, ChipSet.aPortOutputDeskPro386);
|
||||
}
|
||||
}
|
||||
if (DEBUGGER) {
|
||||
if (dbg) {
|
||||
var chipset = this;
|
||||
/*
|
||||
* TODO: Add more "dumpers" (eg, for DMA, RTC, 8042, etc)
|
||||
*/
|
||||
dbg.messageDump(Messages.PIC, function onDumpPIC() {
|
||||
chipset.dumpPIC();
|
||||
});
|
||||
dbg.messageDump(Messages.TIMER, function onDumpTimer(asArgs) {
|
||||
chipset.dumpTimer(asArgs);
|
||||
});
|
||||
dbg.messageDump(Messages.CMOS, function onDumpCMOS() {
|
||||
chipset.dumpCMOS();
|
||||
});
|
||||
}
|
||||
cpu.addIntNotify(Interrupts.RTC, this.intBIOSRTC.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* powerUp(data, fRepower)
|
||||
*
|
||||
|
|
@ -634,7 +636,7 @@ class ChipSet extends Component {
|
|||
{
|
||||
this.nRTCCyclesLastUpdate = this.cpu.getCycles(this.fScaleTimers);
|
||||
this.nRTCPeriodsPerSecond = 1024;
|
||||
this.nRTCCyclesPerPeriod = Math.floor(this.cpu.getCyclesPerSecond() / this.nRTCPeriodsPerSecond);
|
||||
this.nRTCCyclesPerPeriod = Math.floor(this.cpu.getBaseCyclesPerSecond() / this.nRTCPeriodsPerSecond);
|
||||
this.setRTCCycleLimit();
|
||||
}
|
||||
|
||||
|
|
@ -672,16 +674,15 @@ class ChipSet extends Component {
|
|||
}
|
||||
|
||||
/**
|
||||
* setRTCCycleLimit(nCycles)
|
||||
* setRTCCycleLimit()
|
||||
*
|
||||
* This should be called when PIE becomes set in STATUSB (and whenever PF is cleared in STATUSC while PIE is still set).
|
||||
*
|
||||
* @this {ChipSet}
|
||||
* @param {number} [nCycles]
|
||||
*/
|
||||
setRTCCycleLimit(nCycles)
|
||||
setRTCCycleLimit()
|
||||
{
|
||||
if (nCycles === undefined) nCycles = this.nRTCCyclesPerPeriod;
|
||||
var nCycles = this.nRTCCyclesPerPeriod;
|
||||
this.nRTCCyclesNextUpdate = this.cpu.getCycles(this.fScaleTimers) + nCycles;
|
||||
if (this.abCMOSData[ChipSet.CMOS.ADDR.STATUSB] & ChipSet.CMOS.STATUSB.PIE) {
|
||||
this.cpu.setBurstCycles(nCycles);
|
||||
|
|
@ -695,7 +696,7 @@ class ChipSet extends Component {
|
|||
*/
|
||||
updateRTCTime()
|
||||
{
|
||||
var nCyclesPerSecond = this.cpu.getCyclesPerSecond();
|
||||
var nCyclesPerSecond = this.cpu.getBaseCyclesPerSecond();
|
||||
var nCyclesUpdate = this.cpu.getCycles(this.fScaleTimers);
|
||||
|
||||
/*
|
||||
|
|
@ -3412,7 +3413,7 @@ class ChipSet extends Component {
|
|||
* For the original MODEL_5170, the number of cycles per tick is approximately 6,000,000 / 1,193,181,
|
||||
* or 5.028575, so we can no longer always divide cycles by 4 with a simple right-shift by 2. The proper
|
||||
* divisor (eg, 4 for MODEL_5150 and MODEL_5160, 5 for MODEL_5170, etc) is nTicksDivisor, which initBus()
|
||||
* calculates using the base CPU speed returned by cpu.getCyclesPerSecond().
|
||||
* calculates using the base CPU speed returned by cpu.getBaseCyclesPerSecond().
|
||||
*/
|
||||
var ticksElapsed = ((nCycles - timer.nCyclesStart) / this.nTicksDivisor) | 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ class Computer extends Component {
|
|||
* This timer replaces the CPU's old dedicated STATUS_UPDATES_PER_SECOND logic; periodic updateStatus()
|
||||
* calls are now our own responsibility.
|
||||
*/
|
||||
this.cpu.addTimer(function() { cmp.updateStatus(); }, 1000 / Computer.UPDATES_PER_SECOND);
|
||||
this.cpu.addTimer(this.id, function() { cmp.updateStatus(); }, 1000 / Computer.UPDATES_PER_SECOND);
|
||||
|
||||
var sStatePath = null;
|
||||
var sResume = this.getMachineParm('resume');
|
||||
|
|
|
|||
|
|
@ -82,26 +82,27 @@ class CPU extends Component {
|
|||
|
||||
var nMultiplier = parmsCPU['multiplier'] || 1;
|
||||
|
||||
this.aCounts = {};
|
||||
this.aCounts.nCyclesPerSecond = nCycles;
|
||||
this.counts = {};
|
||||
this.counts.nBaseCyclesPerSecond = nCycles;
|
||||
|
||||
/*
|
||||
* nCyclesMultiplier replaces the old "speed" variable (0, 1, 2) and eliminates the need for
|
||||
* the constants (SPEED_SLOW, SPEED_FAST and SPEED_MAX). The UI simply doubles the multiplier
|
||||
* until we've exceeded the host's speed limit and then starts the multiplier over at 1.
|
||||
* nTargetMultiplier replaces the old "speed" variable (0, 1, 2) and eliminates the need for
|
||||
* the constants (SPEED_SLOW, SPEED_FAST and SPEED_MAX). The UI simply doubles the target multiplier
|
||||
* until we've exceeded the host's speed limit (ie, the current value is unable to reach the target),
|
||||
* at which point we reset the target back to the default.
|
||||
*/
|
||||
this.aCounts.nCyclesMultiplier = nMultiplier;
|
||||
this.aCounts.mhzDefault = Math.round(this.aCounts.nCyclesPerSecond / 10000) / 100;
|
||||
this.counts.nBaseMultiplier = this.counts.nCurrentMultiplier = this.counts.nTargetMultiplier = nMultiplier;
|
||||
|
||||
/*
|
||||
* TODO: Take care of this with an initial setSpeed() call instead?
|
||||
*/
|
||||
this.aCounts.mhzTarget = this.aCounts.mhzDefault * this.aCounts.nCyclesMultiplier;
|
||||
this.counts.mhzBase = Math.round(this.counts.nBaseCyclesPerSecond / 10000) / 100;
|
||||
this.counts.mhzCurrent = this.counts.mhzTarget = this.counts.mhzBase * this.counts.nTargetMultiplier;
|
||||
|
||||
/*
|
||||
* We add a number of flags to the set initialized by Component
|
||||
* We add a number of flags to those initialized by Component.
|
||||
*/
|
||||
this.flags.running = false;
|
||||
this.flags.starting = false;
|
||||
this.flags.starting = this.flags.running = this.flags.yield = false;
|
||||
this.flags.autoStart = parmsCPU['autoStart'];
|
||||
|
||||
/*
|
||||
|
|
@ -119,10 +120,10 @@ class CPU extends Component {
|
|||
* and call resetChecksum().
|
||||
*/
|
||||
this.flags.checksum = false;
|
||||
this.aCounts.nChecksum = this.aCounts.nCyclesChecksumNext = 0;
|
||||
this.aCounts.nCyclesChecksumStart = parmsCPU["csStart"];
|
||||
this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"];
|
||||
this.aCounts.nCyclesChecksumStop = parmsCPU["csStop"];
|
||||
this.counts.nChecksum = this.counts.nCyclesChecksumNext = 0;
|
||||
this.counts.nCyclesChecksumStart = parmsCPU["csStart"];
|
||||
this.counts.nCyclesChecksumInterval = parmsCPU["csInterval"];
|
||||
this.counts.nCyclesChecksumStop = parmsCPU["csStop"];
|
||||
|
||||
/*
|
||||
* Array of countdown timers managed by addTimer() and setTimer().
|
||||
|
|
@ -132,8 +133,6 @@ class CPU extends Component {
|
|||
this.aTimers = [];
|
||||
|
||||
this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); };
|
||||
|
||||
this.setReady();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -174,6 +173,10 @@ class CPU extends Component {
|
|||
this.flags.autoStart = (sAutoStart == "true"? true : (sAutoStart == "false"? false : !!sAutoStart));
|
||||
}
|
||||
|
||||
this.timerYield = cpu.addTimer(this.id, function() {
|
||||
cpu.flags.yield = true;
|
||||
}, 1000 / CPU.YIELDS_PER_SECOND);
|
||||
|
||||
this.setReady();
|
||||
}
|
||||
|
||||
|
|
@ -350,13 +353,13 @@ class CPU extends Component {
|
|||
*/
|
||||
resetChecksum()
|
||||
{
|
||||
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.flags.checksum = (this.aCounts.nCyclesChecksumStart >= 0 && this.aCounts.nCyclesChecksumInterval > 0);
|
||||
if (this.counts.nCyclesChecksumStart === undefined) this.counts.nCyclesChecksumStart = 0;
|
||||
if (this.counts.nCyclesChecksumInterval === undefined) this.counts.nCyclesChecksumInterval = -1;
|
||||
if (this.counts.nCyclesChecksumStop === undefined) this.counts.nCyclesChecksumStop = -1;
|
||||
this.flags.checksum = (this.counts.nCyclesChecksumStart >= 0 && this.counts.nCyclesChecksumInterval > 0);
|
||||
if (this.flags.checksum) {
|
||||
this.aCounts.nChecksum = 0;
|
||||
this.aCounts.nCyclesChecksumNext = this.aCounts.nCyclesChecksumStart - this.nTotalCycles;
|
||||
this.counts.nChecksum = 0;
|
||||
this.counts.nCyclesChecksumNext = this.counts.nCyclesChecksumStart - this.nTotalCycles;
|
||||
/*
|
||||
* this.aCounts.nCyclesChecksumNext = this.aCounts.nCyclesChecksumStart + this.aCounts.nCyclesChecksumInterval -
|
||||
* (this.nTotalCycles % this.aCounts.nCyclesChecksumInterval);
|
||||
|
|
@ -384,15 +387,15 @@ class CPU extends Component {
|
|||
* Get a 32-bit summation of the current CPU state and add it to our running 32-bit checksum
|
||||
*/
|
||||
var fDisplay = false;
|
||||
this.aCounts.nChecksum = (this.aCounts.nChecksum + this.getChecksum())|0;
|
||||
this.aCounts.nCyclesChecksumNext -= nCycles;
|
||||
if (this.aCounts.nCyclesChecksumNext <= 0) {
|
||||
this.aCounts.nCyclesChecksumNext += this.aCounts.nCyclesChecksumInterval;
|
||||
this.counts.nChecksum = (this.counts.nChecksum + this.getChecksum())|0;
|
||||
this.counts.nCyclesChecksumNext -= nCycles;
|
||||
if (this.counts.nCyclesChecksumNext <= 0) {
|
||||
this.counts.nCyclesChecksumNext += this.counts.nCyclesChecksumInterval;
|
||||
fDisplay = true;
|
||||
}
|
||||
if (this.aCounts.nCyclesChecksumStop >= 0) {
|
||||
if (this.aCounts.nCyclesChecksumStop <= this.getCycles()) {
|
||||
this.aCounts.nCyclesChecksumInterval = this.aCounts.nCyclesChecksumStop = -1;
|
||||
if (this.counts.nCyclesChecksumStop >= 0) {
|
||||
if (this.counts.nCyclesChecksumStop <= this.getCycles()) {
|
||||
this.counts.nCyclesChecksumInterval = this.counts.nCyclesChecksumStop = -1;
|
||||
this.resetChecksum();
|
||||
this.stopCPU();
|
||||
fDisplay = true;
|
||||
|
|
@ -413,7 +416,7 @@ class CPU extends Component {
|
|||
*/
|
||||
displayChecksum()
|
||||
{
|
||||
this.println(this.getCycles() + " cycles: " + "checksum=" + Str.toHex(this.aCounts.nChecksum));
|
||||
this.println(this.getCycles() + " cycles: " + "checksum=" + Str.toHex(this.counts.nChecksum));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -506,7 +509,7 @@ class CPU extends Component {
|
|||
case "setSpeed":
|
||||
this.bindings[sBinding] = control;
|
||||
control.onclick = function onClickSetSpeed() {
|
||||
cpu.setSpeed(cpu.aCounts.nCyclesMultiplier << 1, true);
|
||||
cpu.setSpeed(cpu.counts.nTargetMultiplier << 1, true);
|
||||
};
|
||||
control.textContent = this.getSpeedTarget();
|
||||
fBound = true;
|
||||
|
|
@ -521,9 +524,7 @@ class CPU extends Component {
|
|||
/**
|
||||
* setBurstCycles(nCycles)
|
||||
*
|
||||
* This function is used by the ChipSet component whenever a very low timer count is set,
|
||||
* in anticipation of the timer requiring an update sooner than the normal nCyclesPerYield
|
||||
* period in runCPU() would normally provide.
|
||||
* This function is used by the ChipSet component whenever a very low timer count is set.
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {number} nCycles is the target number of cycles to drop the current burst to
|
||||
|
|
@ -541,7 +542,6 @@ class CPU extends Component {
|
|||
* TODO: If the delta is negative, we could simply ignore the request, but we must first carefully
|
||||
* consider the impact on the ChipSet timers.
|
||||
*/
|
||||
// if (DEBUG) this.nSnapCycles -= nDelta;
|
||||
this.nStepCycles -= nDelta;
|
||||
this.nBurstCycles -= nDelta;
|
||||
return true;
|
||||
|
|
@ -565,42 +565,17 @@ class CPU extends Component {
|
|||
}
|
||||
|
||||
/**
|
||||
* calcCycles(fRecalc)
|
||||
* calcCycles()
|
||||
*
|
||||
* Calculate the number of cycles to process for each "burst" of CPU activity. The size of a burst
|
||||
* is driven by the following values:
|
||||
*
|
||||
* CPU.YIELDS_PER_SECOND (eg, 30)
|
||||
*
|
||||
* The largest of the above values forces the size of the burst to its smallest value. Let's say that
|
||||
* largest value is 30. Assuming nCyclesPerSecond is 1,000,000, that results in bursts of 33,333 cycles.
|
||||
*
|
||||
* At the end of each burst, we subtract the burst cycle counter from the yield cycle "threshold" counter.
|
||||
* Whenever the "next yield" cycle counter goes to (or below) zero, we compare elapsed time to the time we
|
||||
* expected the virtual hardware to take (eg, 1000ms/50 or 20ms), and if we still have time remaining,
|
||||
* we sleep the remaining time (or 0ms if there's no remaining time), and then restart runCPU().
|
||||
* Calculate the maximum number of cycles we should attempt to process before the next yield.
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {boolean} [fRecalc] is true if the caller wants to recalculate thresholds based on the most recent
|
||||
* speed calculation (see calcSpeed).
|
||||
*/
|
||||
calcCycles(fRecalc)
|
||||
calcCycles()
|
||||
{
|
||||
var vMultiplier = 1;
|
||||
if (fRecalc) {
|
||||
if (this.aCounts.nCyclesMultiplier > 1 && this.aCounts.mhz) {
|
||||
vMultiplier = (this.aCounts.mhz / this.aCounts.mhzDefault);
|
||||
}
|
||||
}
|
||||
this.aCounts.msPerYield = Math.round(1000 / CPU.YIELDS_PER_SECOND);
|
||||
this.aCounts.nCyclesPerYield = Math.floor(this.aCounts.nCyclesPerSecond / CPU.YIELDS_PER_SECOND * vMultiplier);
|
||||
/*
|
||||
* Initialize "next" yield update cycle threshold counters to those "per" values
|
||||
*/
|
||||
if (!fRecalc) {
|
||||
this.aCounts.nCyclesNextYield = this.aCounts.nCyclesPerYield;
|
||||
}
|
||||
this.aCounts.nCyclesRecalc = 0;
|
||||
this.counts.nCurrentMultiplier = ((this.counts.mhzCurrent / this.counts.mhzBase)|0) || this.counts.nTargetMultiplier;
|
||||
this.counts.msPerYield = Math.round(1000 / CPU.YIELDS_PER_SECOND);
|
||||
this.counts.nCyclesPerYield = Math.floor(this.counts.nBaseCyclesPerSecond / CPU.YIELDS_PER_SECOND * this.counts.nCurrentMultiplier);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -623,11 +598,11 @@ class CPU extends Component {
|
|||
getCycles(fScaled)
|
||||
{
|
||||
var nCycles = this.nTotalCycles + this.nRunCycles + this.nBurstCycles - this.nStepCycles;
|
||||
if (fScaled && this.aCounts.nCyclesMultiplier > 1 && this.aCounts.mhz > this.aCounts.mhzDefault) {
|
||||
if (fScaled && this.counts.nTargetMultiplier > 1 && this.counts.mhzCurrent > this.counts.mhzBase) {
|
||||
/*
|
||||
* We could scale the current cycle count by the current effective speed (this.aCounts.mhz); eg:
|
||||
*
|
||||
* nCycles = Math.round(nCycles / (this.aCounts.mhz / this.aCounts.mhzDefault));
|
||||
* nCycles = Math.round(nCycles / (this.aCounts.mhz / this.aCounts.mhzBase));
|
||||
*
|
||||
* but that speed will fluctuate somewhat: large fluctuations at first, but increasingly smaller
|
||||
* fluctuations after each burst of instructions that runCPU() executes.
|
||||
|
|
@ -642,22 +617,35 @@ class CPU extends Component {
|
|||
* interface allows any value, as does the CPU "multiplier" parmsCPU property (from the machine's
|
||||
* XML file).
|
||||
*/
|
||||
nCycles = Math.round(nCycles / this.aCounts.nCyclesMultiplier);
|
||||
nCycles = Math.round(nCycles / this.counts.nTargetMultiplier);
|
||||
}
|
||||
return nCycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* getCyclesPerSecond()
|
||||
* getBaseCyclesPerSecond()
|
||||
*
|
||||
* This returns the CPU's "base" speed (ie, the original cycles per second defined for the machine)
|
||||
* This returns the CPU's base speed (ie, the original cycles per second defined for the machine)
|
||||
*
|
||||
* @this {CPU}
|
||||
* @return {number}
|
||||
*/
|
||||
getCyclesPerSecond()
|
||||
getBaseCyclesPerSecond()
|
||||
{
|
||||
return this.aCounts.nCyclesPerSecond;
|
||||
return this.counts.nBaseCyclesPerSecond;
|
||||
}
|
||||
|
||||
/**
|
||||
* getCurrentCyclesPerSecond()
|
||||
*
|
||||
* This returns the CPU's current speed (ie, the actual cycles per second, according the current multiplier)
|
||||
*
|
||||
* @this {CPU}
|
||||
* @return {number}
|
||||
*/
|
||||
getCurrentCyclesPerSecond()
|
||||
{
|
||||
return (this.counts.nBaseCyclesPerSecond * this.counts.nCurrentMultiplier)|0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -671,10 +659,10 @@ class CPU extends Component {
|
|||
*/
|
||||
resetCycles()
|
||||
{
|
||||
this.aCounts.mhz = 0;
|
||||
this.counts.mhzCurrent = 0;
|
||||
this.nTotalCycles = this.nRunCycles = this.nBurstCycles = this.nStepCycles = 0;
|
||||
this.resetChecksum();
|
||||
this.setSpeed(1);
|
||||
this.setSpeed(this.counts.nBaseMultiplier);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -685,7 +673,7 @@ class CPU extends Component {
|
|||
*/
|
||||
getSpeed()
|
||||
{
|
||||
return this.aCounts.nCyclesMultiplier;
|
||||
return this.counts.nTargetMultiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -696,10 +684,7 @@ class CPU extends Component {
|
|||
*/
|
||||
getSpeedCurrent()
|
||||
{
|
||||
/*
|
||||
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
|
||||
*/
|
||||
return ((this.flags.running && this.aCounts.mhz)? (this.aCounts.mhz.toFixed(2) + "Mhz") : "Stopped");
|
||||
return ((this.flags.running && this.counts.mhzCurrent)? (this.counts.mhzCurrent.toFixed(2) + "Mhz") : "Stopped");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -710,19 +695,14 @@ class CPU extends Component {
|
|||
*/
|
||||
getSpeedTarget()
|
||||
{
|
||||
/*
|
||||
* TODO: Has toFixed() been "fixed" in all browsers (eg, IE) to return a rounded value now?
|
||||
*/
|
||||
return this.aCounts.mhzTarget.toFixed(2) + "Mhz";
|
||||
return this.counts.mhzTarget.toFixed(2) + "Mhz";
|
||||
}
|
||||
|
||||
/**
|
||||
* setSpeed(nMultiplier, fUpdateFocus)
|
||||
*
|
||||
* NOTE: This used to return the target speed, in mhz, but no callers appear to care at this point.
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {number} [nMultiplier] is the new proposed multiplier (reverts to 1 if the target was too high)
|
||||
* @param {number} [nMultiplier] is the new proposed multiplier (reverts to default if target was too high)
|
||||
* @param {boolean} [fUpdateFocus] is true to update Computer focus
|
||||
* @return {boolean} true if successful, false if not
|
||||
*
|
||||
|
|
@ -735,17 +715,18 @@ class CPU extends Component {
|
|||
var fSuccess = false;
|
||||
if (nMultiplier !== undefined) {
|
||||
/*
|
||||
* If we haven't reached 80% (0.8) of the current target speed, revert to a multiplier of one (1).
|
||||
* If we haven't reached 80% (0.8) of the current target speed, revert to the default multiplier.
|
||||
*/
|
||||
if ((fUpdateFocus || this.flags.running) && this.aCounts.mhz / this.aCounts.mhzTarget < 0.8) {
|
||||
nMultiplier = 1;
|
||||
if ((fUpdateFocus || this.flags.running) && this.counts.mhzCurrent / this.counts.mhzTarget < 0.8) {
|
||||
this.counts.mhzCurrent = 0;
|
||||
nMultiplier = this.counts.nBaseMultiplier;
|
||||
} else {
|
||||
fSuccess = true;
|
||||
}
|
||||
this.aCounts.nCyclesMultiplier = nMultiplier;
|
||||
var mhz = this.aCounts.mhzDefault * this.aCounts.nCyclesMultiplier;
|
||||
if (this.aCounts.mhzTarget != mhz) {
|
||||
this.aCounts.mhzTarget = mhz;
|
||||
this.counts.nTargetMultiplier = nMultiplier;
|
||||
var mhzTarget = this.counts.mhzBase * this.counts.nTargetMultiplier;
|
||||
if (this.counts.mhzTarget != mhzTarget) {
|
||||
this.counts.mhzTarget = mhzTarget;
|
||||
var sSpeed = this.getSpeedTarget();
|
||||
var controlSpeed = this.bindings["setSpeed"];
|
||||
if (controlSpeed) controlSpeed.textContent = sSpeed;
|
||||
|
|
@ -755,9 +736,10 @@ class CPU extends Component {
|
|||
}
|
||||
this.addCycles(this.nRunCycles);
|
||||
this.nRunCycles = 0;
|
||||
this.aCounts.msStartRun = Usr.getTime();
|
||||
this.aCounts.msEndThisRun = 0;
|
||||
this.calcCycles();
|
||||
this.counts.msStartRun = Usr.getTime();
|
||||
this.counts.msEndThisRun = 0;
|
||||
this.calcCycles(); // calculate a new value for the current cycle multiplier
|
||||
this.resetTimers(); // and then update all the fixed-period timers using the new cycle multiplier
|
||||
return fSuccess;
|
||||
}
|
||||
|
||||
|
|
@ -771,7 +753,7 @@ class CPU extends Component {
|
|||
calcSpeed(nCycles, msElapsed)
|
||||
{
|
||||
if (msElapsed) {
|
||||
this.aCounts.mhz = Math.round(nCycles / (msElapsed * 10)) / 100;
|
||||
this.counts.mhzCurrent = Math.round(nCycles / (msElapsed * 10)) / 100;
|
||||
if (msElapsed >= 86400000) {
|
||||
this.nTotalCycles = 0;
|
||||
if (this.chipset) this.chipset.updateAllTimers(true);
|
||||
|
|
@ -787,11 +769,10 @@ class CPU extends Component {
|
|||
*/
|
||||
calcStartTime()
|
||||
{
|
||||
if (this.aCounts.nCyclesRecalc >= this.aCounts.nCyclesPerSecond) {
|
||||
this.calcCycles(true);
|
||||
}
|
||||
this.aCounts.nCyclesThisRun = 0;
|
||||
this.aCounts.msStartThisRun = Usr.getTime();
|
||||
this.calcCycles();
|
||||
|
||||
this.counts.nCyclesThisRun = 0;
|
||||
this.counts.msStartThisRun = Usr.getTime();
|
||||
|
||||
/*
|
||||
* Try to detect situations where the browser may have throttled us, such as when the user switches
|
||||
|
|
@ -818,19 +799,19 @@ class CPU extends Component {
|
|||
* to hit its target speed, since you would expect any instruction that displays a message to be an
|
||||
* EXTREMELY slow instruction.
|
||||
*/
|
||||
if (this.aCounts.msEndThisRun) {
|
||||
var msDelta = this.aCounts.msStartThisRun - this.aCounts.msEndThisRun;
|
||||
if (msDelta > this.aCounts.msPerYield) {
|
||||
if (this.counts.msEndThisRun) {
|
||||
var msDelta = this.counts.msStartThisRun - this.counts.msEndThisRun;
|
||||
if (msDelta > this.counts.msPerYield) {
|
||||
if (MAXDEBUG) this.println("large time delay: " + msDelta + "ms");
|
||||
this.aCounts.msStartRun += msDelta;
|
||||
this.counts.msStartRun += msDelta;
|
||||
/*
|
||||
* Bumping msStartRun forward should NEVER cause it to exceed msStartThisRun; however, just
|
||||
* in case, I make absolutely sure it cannot happen, since doing so could result in negative
|
||||
* speed calculations.
|
||||
*/
|
||||
this.assert(this.aCounts.msStartRun <= this.aCounts.msStartThisRun);
|
||||
if (this.aCounts.msStartRun > this.aCounts.msStartThisRun) {
|
||||
this.aCounts.msStartRun = this.aCounts.msStartThisRun;
|
||||
this.assert(this.counts.msStartRun <= this.counts.msStartThisRun);
|
||||
if (this.counts.msStartRun > this.counts.msStartThisRun) {
|
||||
this.counts.msStartRun = this.counts.msStartThisRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -844,20 +825,20 @@ class CPU extends Component {
|
|||
*/
|
||||
calcRemainingTime()
|
||||
{
|
||||
this.aCounts.msEndThisRun = Usr.getTime();
|
||||
this.counts.msEndThisRun = Usr.getTime();
|
||||
|
||||
var msYield = this.aCounts.msPerYield;
|
||||
if (this.aCounts.nCyclesThisRun) {
|
||||
var msYield = this.counts.msPerYield;
|
||||
if (this.counts.nCyclesThisRun) {
|
||||
/*
|
||||
* Normally, we would assume we executed a full quota of work over msPerYield, but since the CPU
|
||||
* now has the option of calling yieldCPU(), that might not be true. If nCyclesThisRun is correct, then
|
||||
* the ratio of nCyclesThisRun/nCyclesPerYield should represent the percentage of work we performed,
|
||||
* and so applying that percentage to msPerYield should give us a better estimate of work vs. time.
|
||||
*/
|
||||
msYield = Math.round(msYield * this.aCounts.nCyclesThisRun / this.aCounts.nCyclesPerYield);
|
||||
msYield = Math.round(msYield * this.counts.nCyclesThisRun / this.counts.nCyclesPerYield);
|
||||
}
|
||||
|
||||
var msElapsedThisRun = this.aCounts.msEndThisRun - this.aCounts.msStartThisRun;
|
||||
var msElapsedThisRun = this.counts.msEndThisRun - this.counts.msStartThisRun;
|
||||
var msRemainsThisRun = msYield - msElapsedThisRun;
|
||||
|
||||
/*
|
||||
|
|
@ -867,56 +848,43 @@ class CPU extends Component {
|
|||
* msElapsed = msElapsedThisRun;
|
||||
*
|
||||
* but it seems preferable to use longer time periods and hopefully get a more accurate speed.
|
||||
*
|
||||
* Also, if msRemainsThisRun >= 0 && this.aCounts.nCyclesMultiplier == 1, we could pass these results instead:
|
||||
*
|
||||
* nCycles = this.aCounts.nCyclesThisRun;
|
||||
* msElapsed = this.aCounts.msPerYield;
|
||||
*
|
||||
* to insure that we display a smooth, constant N Mhz. But for now, I prefer seeing any fluctuations.
|
||||
*/
|
||||
var nCycles = this.nRunCycles;
|
||||
var msElapsed = this.aCounts.msEndThisRun - this.aCounts.msStartRun;
|
||||
var msElapsed = this.counts.msEndThisRun - this.counts.msStartRun;
|
||||
|
||||
if (MAXDEBUG && msRemainsThisRun < 0 && this.aCounts.nCyclesMultiplier > 1) {
|
||||
if (MAXDEBUG && msRemainsThisRun < 0 && this.counts.nTargetMultiplier > 1) {
|
||||
this.println("warning: updates @" + msElapsedThisRun + "ms (prefer " + Math.round(msYield) + "ms)");
|
||||
}
|
||||
|
||||
this.calcSpeed(nCycles, msElapsed);
|
||||
|
||||
if (msRemainsThisRun < 0 || this.aCounts.mhz < this.aCounts.mhzTarget) {
|
||||
if (msRemainsThisRun < 0 || this.counts.mhzCurrent < this.counts.mhzTarget) {
|
||||
/*
|
||||
* Try "throwing out" the effects of large anomalies, by moving the overall run start time up;
|
||||
* ordinarily, this should only happen when the someone is using an external Debugger or some other
|
||||
* tool or feature that is interfering with our overall execution.
|
||||
*/
|
||||
if (msRemainsThisRun < -1000) {
|
||||
this.aCounts.msStartRun -= msRemainsThisRun;
|
||||
this.counts.msStartRun -= msRemainsThisRun;
|
||||
}
|
||||
/*
|
||||
* If the last burst took MORE time than we allotted (ie, it's taking more than 1 second to simulate
|
||||
* nCyclesPerSecond), all we can do is yield for as little time as possible (ie, 0ms) and hope that the
|
||||
* nCyclesActual), all we can do is yield for as little time as possible (ie, 0ms) and hope that the
|
||||
* simulation is at least usable.
|
||||
*/
|
||||
msRemainsThisRun = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Last but not least, update nCyclesRecalc, so that when runCPU() starts up again and calls calcStartTime(),
|
||||
* it'll be ready to decide if calcCycles() should be called again.
|
||||
*/
|
||||
this.aCounts.nCyclesRecalc += this.aCounts.nCyclesThisRun;
|
||||
|
||||
if (DEBUG && this.messageEnabled(Messages.LOG) && msRemainsThisRun) {
|
||||
this.log("calcRemainingTime: " + msRemainsThisRun + "ms to sleep after " + this.aCounts.msEndThisRun + "ms");
|
||||
this.log("calcRemainingTime: " + msRemainsThisRun + "ms to sleep after " + this.counts.msEndThisRun + "ms");
|
||||
}
|
||||
|
||||
this.aCounts.msEndThisRun += msRemainsThisRun;
|
||||
this.counts.msEndThisRun += msRemainsThisRun;
|
||||
return msRemainsThisRun;
|
||||
}
|
||||
|
||||
/**
|
||||
* addTimer(callBack, ms)
|
||||
* addTimer(id, callBack, ms)
|
||||
*
|
||||
* Components that want to have timers that fire after some number of milliseconds call addTimer() to create
|
||||
* the timer, and then setTimer() when they want to arm it. Alternatively, they can specify an automatic timeout
|
||||
|
|
@ -934,24 +902,36 @@ class CPU extends Component {
|
|||
*
|
||||
* Why not use JavaScript's setTimeout() instead? Good question. For a good answer, see setTimer() below.
|
||||
*
|
||||
* TODO: Consider making the addTimer() and setTimer() interfaces more like the addIRQ() and setIRQ()
|
||||
* interfaces (which return the underlying object instead of an array index) and maintaining a separate list
|
||||
* of active timers, in order of highest to lowest cycle countdown values, as this could speed up
|
||||
* getBurstCycles() and updateTimers() functions ever so slightly.
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {string} id
|
||||
* @param {function()} callBack
|
||||
* @param {number} [ms] (if set, enables automatic setTimer calls)
|
||||
* @return {number} timer index
|
||||
*/
|
||||
addTimer(callBack, ms = -1)
|
||||
addTimer(id, callBack, ms = -1)
|
||||
{
|
||||
var iTimer = this.aTimers.length;
|
||||
this.aTimers.push([-1, ms, callBack]);
|
||||
this.aTimers.push([id, -1, ms, callBack]);
|
||||
if (ms >= 0) this.setTimer(iTimer, ms);
|
||||
return iTimer;
|
||||
}
|
||||
|
||||
/**
|
||||
* findTimer(id)
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {string} id
|
||||
* @return {Array|null}
|
||||
*/
|
||||
findTimer(id)
|
||||
{
|
||||
for (var iTimer = 0; iTimer < this.aTimers.length; iTimer++) {
|
||||
var timer = this.aTimers[iTimer];
|
||||
if (timer[0] == id) return timer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* setTimer(iTimer, ms, fReset)
|
||||
*
|
||||
|
|
@ -978,7 +958,7 @@ class CPU extends Component {
|
|||
var nCycles = -1;
|
||||
if (iTimer >= 0 && iTimer < this.aTimers.length) {
|
||||
var timer = this.aTimers[iTimer];
|
||||
if (fReset || timer[0] < 0) {
|
||||
if (fReset || timer[1] < 0) {
|
||||
nCycles = this.getMSCycles(ms);
|
||||
/*
|
||||
* We must now confront the following problem: if the CPU is currently executing a burst of cycles,
|
||||
|
|
@ -989,12 +969,41 @@ class CPU extends Component {
|
|||
if (this.flags.running) {
|
||||
nCycles += this.endBurst();
|
||||
}
|
||||
timer[0] = nCycles;
|
||||
timer[1] = nCycles;
|
||||
}
|
||||
}
|
||||
return nCycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* setTimerCycles(iTimer, nCycles)
|
||||
*
|
||||
* A cycle-based version of setTimer(), used to help wean components off of functions like setBurstCycles().
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {number} iTimer
|
||||
* @param {number} nCycles
|
||||
* @return {boolean}
|
||||
*/
|
||||
setTimerCycles(iTimer, nCycles)
|
||||
{
|
||||
if (iTimer >= 0 && iTimer < this.aTimers.length) {
|
||||
var timer = this.aTimers[iTimer];
|
||||
/*
|
||||
* We must now confront the following problem: if the CPU is currently executing a burst of cycles,
|
||||
* the number of cycles it has executed in that burst so far must NOT be charged against the cycle
|
||||
* timeout we're about to set. The simplest way to resolve that is to immediately call endBurst()
|
||||
* and bias the cycle timeout by the number of cycles that the burst executed.
|
||||
*/
|
||||
if (this.flags.running) {
|
||||
nCycles += this.endBurst();
|
||||
}
|
||||
timer[1] = nCycles;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* getMSCycles(ms)
|
||||
*
|
||||
|
|
@ -1004,26 +1013,24 @@ class CPU extends Component {
|
|||
*/
|
||||
getMSCycles(ms)
|
||||
{
|
||||
return ((this.aCounts.nCyclesPerSecond * this.aCounts.nCyclesMultiplier) / 1000 * ms)|0;
|
||||
return ((this.counts.nBaseCyclesPerSecond * this.counts.nCurrentMultiplier) / 1000 * ms)|0;
|
||||
}
|
||||
|
||||
/**
|
||||
* getBurstCycles(nCycles)
|
||||
*
|
||||
* Used by runCPU() to get min(nCycles,[timer cycle counts])
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {number} nCycles (number of cycles about to execute)
|
||||
* @return {number} (either nCycles or less if a timer needs to fire)
|
||||
* @param {number} nCycles (maximum number of cycles to execute)
|
||||
* @return {number}
|
||||
*/
|
||||
getBurstCycles(nCycles)
|
||||
{
|
||||
for (var iTimer = this.aTimers.length - 1; iTimer >= 0; iTimer--) {
|
||||
var timer = this.aTimers[iTimer];
|
||||
this.assert(!isNaN(timer[0]));
|
||||
if (timer[0] < 0) continue;
|
||||
if (nCycles > timer[0]) {
|
||||
nCycles = timer[0];
|
||||
this.assert(!isNaN(timer[1]));
|
||||
if (timer[1] < 0) continue;
|
||||
if (nCycles > timer[1]) {
|
||||
nCycles = timer[1];
|
||||
}
|
||||
}
|
||||
return nCycles;
|
||||
|
|
@ -1033,31 +1040,50 @@ class CPU extends Component {
|
|||
* saveTimers()
|
||||
*
|
||||
* @this {CPU}
|
||||
* @return {Array.<number>}
|
||||
* @return {Array}
|
||||
*/
|
||||
saveTimers()
|
||||
{
|
||||
var aTimerCycles = [];
|
||||
var aTimerStates = [];
|
||||
for (var iTimer = 0; iTimer < this.aTimers.length; iTimer++) {
|
||||
var timer = this.aTimers[iTimer];
|
||||
aTimerCycles.push([timer[0], timer[1]]);
|
||||
aTimerStates.push([timer[0], timer[1], timer[2]]);
|
||||
}
|
||||
return aTimerCycles;
|
||||
return aTimerStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* restoreTimers(aTimerCycles)
|
||||
* restoreTimers(aTimerStates)
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {Array.<number>} aTimerCycles
|
||||
* @param {Array} aTimerStates
|
||||
*/
|
||||
restoreTimers(aTimerCycles)
|
||||
restoreTimers(aTimerStates)
|
||||
{
|
||||
this.assert(aTimerCycles.length === this.aTimers.length);
|
||||
for (var iTimer = 0; iTimer < this.aTimers.length && iTimer < aTimerCycles.length; iTimer++) {
|
||||
for (var iTimerState = 0; iTimerState < aTimerStates.length; iTimerState++) {
|
||||
var state = aTimerStates[iTimerState];
|
||||
var timer = this.findTimer(state[0]);
|
||||
if (timer) {
|
||||
timer[1] = state[1];
|
||||
timer[2] = state[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* resetTimers()
|
||||
*
|
||||
* When the target CPU speed multiplier is altered, it's a good idea to run through all the timers that
|
||||
* have a fixed millisecond period and re-arm them, because the timers are using cycle counts that were based
|
||||
* on a previous multiplier.
|
||||
*
|
||||
* @this {CPU}
|
||||
*/
|
||||
resetTimers()
|
||||
{
|
||||
for (var iTimer = this.aTimers.length - 1; iTimer >= 0; iTimer--) {
|
||||
var timer = this.aTimers[iTimer];
|
||||
timer[0] = aTimerCycles[iTimer][0];
|
||||
timer[1] = aTimerCycles[iTimer][1];
|
||||
if (timer[2] >= 0) this.setTimer(iTimer, timer[2], true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1075,13 +1101,13 @@ class CPU extends Component {
|
|||
{
|
||||
for (var iTimer = this.aTimers.length - 1; iTimer >= 0; iTimer--) {
|
||||
var timer = this.aTimers[iTimer];
|
||||
this.assert(!isNaN(timer[0]));
|
||||
if (timer[0] < 0) continue;
|
||||
timer[0] -= nCycles;
|
||||
if (timer[0] <= 0) {
|
||||
timer[0] = -1; // zero is technically an "active" value, so ensure the timer is dormant now
|
||||
timer[2](); // safe to invoke the callback function now
|
||||
if (timer[1] >= 0) this.setTimer(iTimer, timer[1]);
|
||||
this.assert(!isNaN(timer[1]));
|
||||
if (timer[1] < 0) continue;
|
||||
timer[1] -= nCycles;
|
||||
if (timer[1] <= 0) {
|
||||
timer[1] = -1; // zero is technically an "active" value, so ensure the timer is dormant now
|
||||
timer[3](); // safe to invoke the callback function now
|
||||
if (timer[2] >= 0) this.setTimer(iTimer, timer[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1111,18 +1137,19 @@ class CPU extends Component {
|
|||
if (!this.flags.running) return;
|
||||
|
||||
/*
|
||||
* calcStartTime() initializes the cycle counter and timestamp for this runCPU() invocation, and optionally
|
||||
* recalculates the the maximum number of cycles for each burst if the nCyclesRecalc threshold has been reached.
|
||||
* calcStartTime() initializes the cycle counter and timestamp for this runCPU() invocation.
|
||||
*/
|
||||
this.calcStartTime();
|
||||
|
||||
try {
|
||||
this.flags.yield = false;
|
||||
do {
|
||||
/*
|
||||
* nCycles is how many cycles we WANT to run on each iteration of stepCPU(), and may be as
|
||||
* HIGH as nCyclesPerYield, but it may be significantly less. getBurstCycles() will adjust
|
||||
* nCycles downward if any CPU timers need to fire during the next burst.
|
||||
* getBurstCycles() tells us how many cycles to execute as a burst. The answer will always
|
||||
* be less than getCurrentCyclesPerSecond(), because at the very least, our own timer fires more than
|
||||
* once per second.
|
||||
*/
|
||||
var nCycles = this.getBurstCycles(this.flags.checksum? 1 : this.aCounts.nCyclesPerYield);
|
||||
var nCycles = this.getBurstCycles(this.flags.checksum? 1 : this.getCurrentCyclesPerSecond());
|
||||
|
||||
if (this.chipset) {
|
||||
this.chipset.updateAllTimers();
|
||||
|
|
@ -1154,24 +1181,19 @@ class CPU extends Component {
|
|||
*/
|
||||
nCycles = this.endBurst(true);
|
||||
|
||||
/*
|
||||
/*z
|
||||
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU started).
|
||||
*/
|
||||
this.aCounts.nCyclesThisRun += nCycles;
|
||||
this.counts.nCyclesThisRun += nCycles;
|
||||
this.nRunCycles += nCycles;
|
||||
this.updateChecksum(nCycles);
|
||||
|
||||
/*
|
||||
* Update any/all timers, firing those whose cycle countdowns have reached (or dropped below) zero.
|
||||
* Update all timers, firing those whose cycle countdowns have reached (or dropped below) zero.
|
||||
*/
|
||||
this.updateTimers(nCycles);
|
||||
|
||||
this.aCounts.nCyclesNextYield -= nCycles;
|
||||
if (this.aCounts.nCyclesNextYield <= 0) {
|
||||
this.aCounts.nCyclesNextYield += this.aCounts.nCyclesPerYield;
|
||||
break;
|
||||
}
|
||||
} while (this.flags.running);
|
||||
} while (this.flags.running && !this.flags.yield);
|
||||
}
|
||||
catch (e) {
|
||||
this.stopCPU();
|
||||
|
|
@ -1204,9 +1226,8 @@ class CPU extends Component {
|
|||
}
|
||||
/*
|
||||
* 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
|
||||
* of cycles for each burst based on the last known effective CPU speed, and resets the nCyclesRecalc
|
||||
* threshold counter.
|
||||
* cycle counter and timestamp for the current series of runCPU() calls, and calculates the maximum number
|
||||
* of cycles for each burst based on the last known effective CPU speed.
|
||||
*/
|
||||
this.setSpeed();
|
||||
this.flags.running = true;
|
||||
|
|
@ -1217,7 +1238,7 @@ class CPU extends Component {
|
|||
if (this.cmp) {
|
||||
this.cmp.updateStatus(true);
|
||||
if (fUpdateFocus) this.cmp.updateFocus(true);
|
||||
this.cmp.start(this.aCounts.msStartRun, this.getCycles());
|
||||
this.cmp.start(this.counts.msStartRun, this.getCycles());
|
||||
}
|
||||
setTimeout(this.onRunTimeout, 0);
|
||||
return true;
|
||||
|
|
@ -1242,9 +1263,6 @@ class CPU extends Component {
|
|||
*
|
||||
* For use by any component that wants to stop the CPU.
|
||||
*
|
||||
* This similar to yieldCPU(), but it doesn't need to zero nCyclesNextYield to break out of runCPU();
|
||||
* it simply needs to clear fRunning (well, "simply" may be oversimplifying a bit....)
|
||||
*
|
||||
* @this {CPU}
|
||||
* @param {boolean} [fComplete]
|
||||
* @return {boolean} true if the CPU was stopped, false if it was already stopped
|
||||
|
|
@ -1298,9 +1316,8 @@ class CPU extends Component {
|
|||
*/
|
||||
yieldCPU()
|
||||
{
|
||||
this.endBurst(); // this will break us out of stepCPU()
|
||||
this.aCounts.nCyclesNextYield = 0; // this will break us out of runCPU(), once we break out of stepCPU()
|
||||
// if (DEBUG) this.nSnapCycles = this.nBurstCycles;
|
||||
this.endBurst();
|
||||
this.flags.yield = true;
|
||||
/*
|
||||
* The Debugger calls yieldCPU() after every message() to ensure browser responsiveness, but it looks
|
||||
* odd for those messages to show CPU state changes but for the CPU's own status display to not (ditto
|
||||
|
|
@ -1310,20 +1327,6 @@ class CPU extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Constants that control the frequency at which various updates should occur.
|
||||
*
|
||||
* These values do NOT control the simulation directly. Instead, they are used by
|
||||
* calcCycles(), which uses the nCyclesPerSecond passed to the constructor as a starting
|
||||
* point and computes the following variables:
|
||||
*
|
||||
* this.aCounts.nCyclesPerYield = (this.aCounts.nCyclesPerSecond / CPU.YIELDS_PER_SECOND)
|
||||
*
|
||||
* The above variables are also multiplied by any cycle multiplier in effect, via setSpeed(),
|
||||
* and then they're used to initialize another set of variables for each runCPU() iteration:
|
||||
*
|
||||
* this.aCounts.nCyclesNextYield <= this.aCounts.nCyclesPerYield
|
||||
*/
|
||||
CPU.YIELDS_PER_SECOND = 30;
|
||||
|
||||
CPU.BUTTONS = ["power", "reset"];
|
||||
|
|
|
|||
|
|
@ -3793,7 +3793,7 @@ class DebuggerX86 extends Debugger {
|
|||
sLine += (nSequence != null? '=' + nSequence.toString() : "");
|
||||
} else {
|
||||
var nCycles = this.cpu.getCycles();
|
||||
sLine += "cycles=" + nCycles.toString() + " cs=" + Str.toHex(this.cpu.aCounts.nChecksum);
|
||||
sLine += "cycles=" + nCycles.toString() + " cs=" + Str.toHex(this.cpu.counts.nChecksum);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5133,8 +5133,8 @@ class DebuggerX86 extends Debugger {
|
|||
doInfo(asArgs)
|
||||
{
|
||||
if (DEBUG) {
|
||||
this.println("msPerYield: " + this.cpu.aCounts.msPerYield);
|
||||
this.println("nCyclesPerYield: " + this.cpu.aCounts.nCyclesPerYield);
|
||||
this.println("msPerYield: " + this.cpu.counts.msPerYield);
|
||||
this.println("nCyclesPerYield: " + this.cpu.counts.nCyclesPerYield);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -5543,13 +5543,13 @@ class DebuggerX86 extends Debugger {
|
|||
if (asArgs[3] !== undefined) nCycles = +asArgs[3]; // warning: decimal instead of hex conversion
|
||||
switch (asArgs[2]) {
|
||||
case "int":
|
||||
this.cpu.aCounts.nCyclesChecksumInterval = nCycles;
|
||||
this.cpu.counts.nCyclesChecksumInterval = nCycles;
|
||||
break;
|
||||
case "start":
|
||||
this.cpu.aCounts.nCyclesChecksumStart = nCycles;
|
||||
this.cpu.counts.nCyclesChecksumStart = nCycles;
|
||||
break;
|
||||
case "stop":
|
||||
this.cpu.aCounts.nCyclesChecksumStop = nCycles;
|
||||
this.cpu.counts.nCyclesChecksumStop = nCycles;
|
||||
break;
|
||||
default:
|
||||
this.println("unknown cs option");
|
||||
|
|
|
|||
|
|
@ -342,18 +342,17 @@ class Panel extends Component {
|
|||
/**
|
||||
* startTimer()
|
||||
*
|
||||
* This timer replaces the CPU's old dedicated VIDEO_UPDATES_PER_SECOND logic, which periodically called
|
||||
* the Computer's updateVideo() function, which in turn called us; periodic updateAnimation() calls are now
|
||||
* our own responsibility.
|
||||
*
|
||||
* @this {Panel}
|
||||
*/
|
||||
startTimer()
|
||||
{
|
||||
/*
|
||||
* This timer replaces the CPU's old dedicated VIDEO_UPDATES_PER_SECOND logic, which periodically called
|
||||
* the Computer's updateVideo() function, which in turn called us; periodic updateAnimation() calls are now
|
||||
* our own responsibility.
|
||||
*/
|
||||
if (this.timer < 0 && this.canvas && this.cpu) {
|
||||
var panel = this;
|
||||
this.timer = this.cpu.addTimer(function() {
|
||||
this.timer = this.cpu.addTimer(this.id, function() {
|
||||
panel.updateAnimation();
|
||||
}, 1000 / Panel.UPDATES_PER_SECOND);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -375,8 +375,8 @@ class Card {
|
|||
|
||||
var monitorSpecs = Video.monitorSpecs[nMonitorType] || Video.monitorSpecs[ChipSet.MONITOR.MONO];
|
||||
|
||||
var nCyclesPerSecond = video.cpu.getCyclesPerSecond(); // eg, 4772727
|
||||
this.nCyclesHorzPeriod = (nCyclesPerSecond / monitorSpecs.nHorzPeriodsPerSec)|0;
|
||||
var nCyclesDefault = video.cpu.getBaseCyclesPerSecond(); // eg, 4772727
|
||||
this.nCyclesHorzPeriod = (nCyclesDefault / monitorSpecs.nHorzPeriodsPerSec)|0;
|
||||
this.nCyclesHorzActive = (this.nCyclesHorzPeriod * monitorSpecs.percentHorzActive / 100)|0;
|
||||
this.nCyclesVertPeriod = (this.nCyclesHorzPeriod * monitorSpecs.nHorzPeriodsPerFrame)|0;
|
||||
this.nCyclesVertActive = (this.nCyclesVertPeriod * monitorSpecs.percentVertActive / 100)|0;
|
||||
|
|
@ -2507,7 +2507,9 @@ class Video extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
this.cpu.addTimer(function() { video.updateScreen(); }, 1000 / Video.UPDATES_PER_SECOND);
|
||||
this.cpu.addTimer(this.id, function() {
|
||||
video.updateScreen();
|
||||
}, 1000 / Video.UPDATES_PER_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -7077,8 +7079,8 @@ Video.MODEL = {
|
|||
*
|
||||
* From these monitor specs, we calculate the following values for a given Card:
|
||||
*
|
||||
* nCyclesPerSecond = cpu.getCyclesPerSecond(); // eg, 4772727
|
||||
* nCyclesHorzPeriod = (nCyclesPerSecond / monitorSpecs.nHorzPeriodsPerSec) | 0;
|
||||
* nCyclesDefault = cpu.getBaseCyclesPerSecond(); // eg, 4772727
|
||||
* nCyclesHorzPeriod = (nCyclesDefault / monitorSpecs.nHorzPeriodsPerSec) | 0;
|
||||
* nCyclesHorzActive = (nCyclesHorzPeriod * monitorSpecs.percentHorzActive / 100) | 0;
|
||||
* nCyclesVertPeriod = nCyclesHorzPeriod * monitorSpecs.nHorzPeriodsPerFrame;
|
||||
* nCyclesVertActive = (nCyclesVertPeriod * monitorSpecs.percentVertActive / 100) | 0;
|
||||
|
|
|
|||
|
|
@ -86,9 +86,9 @@ class X86CPU extends CPU {
|
|||
*/
|
||||
constructor(parmsCPU)
|
||||
{
|
||||
var nCyclesDefault;
|
||||
var model = +parmsCPU['model'] || X86.MODEL_8088;
|
||||
|
||||
var nCyclesDefault = 0;
|
||||
switch(model) {
|
||||
case X86.MODEL_8088:
|
||||
default:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue