Laid the groundwork for bring PCx86 up-to-date with timer functionality matching PC8080 and PDPjs (new timer services added)

This commit is contained in:
Jeff Parsons 2017-08-01 14:26:52 -07:00 committed by Jeff Parsons
commit 072b8fa2df
8 changed files with 1693 additions and 1493 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -956,7 +956,10 @@ class CPU8080 extends Component {
* timeout we're about to set. The simplest way to resolve that is to immediately call endBurst() * timeout we're about to set. The simplest way to resolve that is to immediately call endBurst()
* and bias the above cycle timeout by the number of cycles that the burst executed. * and bias the above cycle timeout by the number of cycles that the burst executed.
*/ */
this.aTimers[iTimer][0] = nCycles + this.endBurst(); if (this.flags.running) {
nCycles += this.endBurst();
}
this.aTimers[iTimer][0] = nCycles;
} }
} }
return nCycles; return nCycles;
@ -971,7 +974,7 @@ class CPU8080 extends Component {
*/ */
getMSCycles(ms) getMSCycles(ms)
{ {
return (this.nCyclesPerSecond * this.nCyclesMultiplier) / 1000 * ms; return ((this.nCyclesPerSecond * this.nCyclesMultiplier) / 1000 * ms)|0;
} }
/** /**
@ -987,6 +990,7 @@ class CPU8080 extends Component {
{ {
for (var i = this.aTimers.length - 1; i >= 0; i--) { for (var i = this.aTimers.length - 1; i >= 0; i--) {
var timer = this.aTimers[i]; var timer = this.aTimers[i];
this.assert(!isNaN(timer[0]));
if (timer[0] < 0) continue; if (timer[0] < 0) continue;
if (nCycles > timer[0]) { if (nCycles > timer[0]) {
nCycles = timer[0]; nCycles = timer[0];
@ -1009,6 +1013,7 @@ class CPU8080 extends Component {
{ {
for (var i = this.aTimers.length - 1; i >= 0; i--) { for (var i = this.aTimers.length - 1; i >= 0; i--) {
var timer = this.aTimers[i]; var timer = this.aTimers[i];
this.assert(!isNaN(timer[0]));
if (timer[0] < 0) continue; if (timer[0] < 0) continue;
timer[0] -= nCycles; timer[0] -= nCycles;
if (timer[0] <= 0) { if (timer[0] <= 0) {

View file

@ -1160,7 +1160,7 @@ class Computer extends Component {
* *
* Notify all (other) components with a start() method that the CPU has started. * Notify all (other) components with a start() method that the CPU has started.
* *
* Note that we're called by runCPU(), which is why we exclude the CPU component, * Note that we're called by startCPU(), which is why we exclude the CPU component,
* as well as ourselves. * as well as ourselves.
* *
* @this {Computer} * @this {Computer}
@ -1184,7 +1184,7 @@ class Computer extends Component {
* *
* Notify all (other) components with a stop() method that the CPU has stopped. * Notify all (other) components with a stop() method that the CPU has stopped.
* *
* Note that we're called by runCPU(), which is why we exclude the CPU component, * Note that we're called by stopCPU(), which is why we exclude the CPU component,
* as well as ourselves. * as well as ourselves.
* *
* @this {Computer} * @this {Computer}

View file

@ -48,26 +48,25 @@ class CPU extends Component {
* *
* The CPU class supports the following (parmsCPU) properties: * The CPU class supports the following (parmsCPU) properties:
* *
* cycles: the machine's base cycles per second; the X86CPU constructor will * cycles: the machine's base cycles per second; the X86CPU constructor will provide us with a default
* provide us with a default (based on the CPU model) to use as a fallback. * (based on the CPU model) to use as a fallback.
* *
* multiplier: base cycle multiplier; default is 1. * multiplier: base cycle multiplier; default is 1.
* *
* autoStart: true to automatically start, false to not, or null if "it depends"; * autoStart: true to automatically start, false to not, or null if "it depends"; null is the default,
* null is the default, which means do not autostart UNLESS there is no Debugger * which means do not autostart UNLESS there is no Debugger and no "Run" button (ie, no way to manually
* and no "Run" button (ie, no way to manually start the machine). * start the machine).
* *
* csStart: the number of cycles that runCPU() must wait before generating * csStart: the number of cycles that runCPU() must wait before generating checksum records;
* checksum records; -1 if disabled. checksum records are a diagnostic aid * -1 if disabled. checksum records are a diagnostic aid used to help compare one CPU run to another.
* used to help compare one CPU run to another.
* *
* csInterval: the number of cycles that runCPU() must execute before * csInterval: the number of cycles that runCPU() must execute before generating a checksum record;
* generating a checksum record; -1 if disabled. * -1 if disabled.
* *
* csStop: the number of cycles to stop generating checksum records. * csStop: the number of cycles to stop generating checksum records.
* *
* This component is primarily responsible for interfacing the CPU with the outside * This component is primarily responsible for interfacing the CPU with the outside world (eg, Panel and Debugger
* world (eg, Panel and Debugger components), and managing overall CPU operation. * components), and managing overall CPU operation.
* *
* It is extended by the X86CPU component, where all the x86-specific logic resides. * It is extended by the X86CPU component, where all the x86-specific logic resides.
* *
@ -125,6 +124,13 @@ class CPU extends Component {
this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"]; this.aCounts.nCyclesChecksumInterval = parmsCPU["csInterval"];
this.aCounts.nCyclesChecksumStop = parmsCPU["csStop"]; this.aCounts.nCyclesChecksumStop = parmsCPU["csStop"];
/*
* Array of countdown timers managed by addTimer() and setTimer().
*
* See also: getMSCycles(), getBurstCycles(), saveTimers(), restoreTimers(), and updateTimers()
*/
this.aTimers = [];
this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); }; this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); };
this.setReady(); this.setReady();
@ -276,17 +282,19 @@ class CPU extends Component {
*/ */
autoStart() autoStart()
{ {
if (this.flags.running) {
return true;
}
/* /*
* Start running automatically on power-up, assuming there's no Debugger and no "Run" button * Start running automatically on power-up, assuming there's no Debugger and no "Run" button.
*/ */
if (this.flags.autoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) { if (this.flags.autoStart || (!DEBUGGER || !this.dbg) && this.bindings["run"] === undefined) {
/* /*
* We used to also set fUpdateFocus when calling runCPU(), on the assumption that in the "auto-starting" * We used to also set fUpdateFocus when calling startCPU(), on the assumption that in the "auto-starting"
* context, a machine without focus is like a day without sunshine, but in reality, focus should only be * context, a machine without focus is like a day without sunshine, but in reality, focus should only be
* forced when the user takes some other machine-related action. * forced when the user takes some other machine-related action.
*/ */
this.runCPU(); return this.startCPU();
return true;
} }
return false; return false;
} }
@ -481,7 +489,7 @@ class CPU extends Component {
*/ */
if (fRunning == cpu.flags.running) { if (fRunning == cpu.flags.running) {
if (!cpu.flags.running) { if (!cpu.flags.running) {
cpu.runCPU(true); cpu.startCPU(true);
} else { } else {
cpu.stopCPU(true); cpu.stopCPU(true);
} }
@ -930,31 +938,188 @@ class CPU extends Component {
} }
/** /**
* endBurst() * addTimer(callBack)
*
* Components that want to have timers that periodically fire after some number of milliseconds call
* addTimer() to create the timer, and then setTimer() every time they want to arm it. There is currently
* no removeTimer() because these are generally used for the entire lifetime of a component.
*
* Internally, each timer entry is a preallocated Array with two entries: a cycle countdown in element [0]
* and a callback function in element [1]. A timer is initially dormant; dormant timers have a countdown
* value of -1 (although any negative number will suffice) and active timers have a non-negative value.
*
* 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} * @this {CPU}
* @param {function()} callBack
* @return {number} timer index
*/ */
endBurst() addTimer(callBack)
{ {
this.nBurstCycles -= this.nStepCycles; var iTimer = this.aTimers.length;
this.nStepCycles = 0; this.aTimers.push([-1, callBack]);
return iTimer;
} }
/** /**
* runCPU(fUpdateFocus) * setTimer(iTimer, ms, fReset)
*
* Using the timer index from a previous addTimer() call, this sets that timer to fire after the
* specified number of milliseconds.
*
* This is preferred over JavaScript's setTimeout(), because all our timers are effectively paused when
* the CPU is paused (eg, when the Debugger halts execution). Moreover, setTimeout() handlers only run after
* runCPU() yields, which is far too granular for some components (eg, when the SerialPort tries to simulate
* interrupts at 9600 baud).
*
* Ideally, the only function that would use setTimeout() is runCPU(), while the rest of the components
* use setTimer(); however, due to legacy code (ie, code that predates these functions) and/or laziness,
* that may not be the case.
* *
* @this {CPU} * @this {CPU}
* @param {boolean} [fUpdateFocus] is true to update Computer focus * @param {number} iTimer
* @param {number} ms (converted into a cycle countdown internally)
* @param {boolean} [fReset] (true if the timer should be reset even if already armed)
* @return {number} (number of cycles used to arm timer, or -1 if error)
*/ */
runCPU(fUpdateFocus) setTimer(iTimer, ms, fReset)
{ {
if (!this.setBusy(true)) { var nCycles = -1;
this.updateCPU(); if (iTimer >= 0 && iTimer < this.aTimers.length) {
if (this.cmp) this.cmp.stop(Usr.getTime(), this.getCycles()); if (fReset || this.aTimers[iTimer][0] < 0) {
return; nCycles = this.getMSCycles(ms);
/*
* 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();
}
this.aTimers[iTimer][0] = nCycles;
}
} }
return nCycles;
}
this.startCPU(fUpdateFocus); /**
* getMSCycles(ms)
*
* @this {CPU}
* @param {number} ms
* @return {number} number of corresponding cycles
*/
getMSCycles(ms)
{
return ((this.aCounts.nCyclesPerSecond * this.aCounts.nCyclesMultiplier) / 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)
*/
getBurstCycles(nCycles)
{
for (var i = this.aTimers.length - 1; i >= 0; i--) {
var timer = this.aTimers[i];
this.assert(!isNaN(timer[0]));
if (timer[0] < 0) continue;
if (nCycles > timer[0]) {
nCycles = timer[0];
}
}
return nCycles;
}
/**
* saveTimers()
*
* @this {CPU}
* @return {Array.<number>}
*/
saveTimers()
{
var aTimerCycles = [];
for (var i = 0; i < this.aTimers.length; i++) {
var timer = this.aTimers[i];
aTimerCycles.push(timer[0]);
}
return aTimerCycles;
}
/**
* restoreTimers(aTimerCycles)
*
* @this {CPU}
* @param {Array.<number>} aTimerCycles
*/
restoreTimers(aTimerCycles)
{
this.assert(aTimerCycles.length === this.aTimers.length);
for (var i = 0; i < this.aTimers.length && i < aTimerCycles.length; i++) {
var timer = this.aTimers[i];
timer[0] = aTimerCycles[i];
}
}
/**
* updateTimers(nCycles)
*
* Used by runCPU() to reduce all active timer countdown values by the number of cycles just executed;
* this is the function that actually "fires" any timer(s) whose countdown has reached (or dropped below)
* zero, invoking their callback function.
*
* @this {CPU}
* @param {number} nCycles (number of cycles actually executed)
*/
updateTimers(nCycles)
{
for (var i = this.aTimers.length - 1; i >= 0; i--) {
var timer = this.aTimers[i];
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[1](); // safe to invoke the callback function now
}
}
}
/**
* endBurst(fReset)
*
* @this {CPU}
* @param {boolean} [fReset]
* @return {number} (number of cycles executed in the most recent burst)
*/
endBurst(fReset)
{
var nCycles = this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
if (fReset) this.nBurstCycles = 0;
return nCycles;
}
/**
* runCPU()
*
* @this {CPU}
*/
runCPU()
{
if (!this.flags.running) return;
/* /*
* calcStartTime() initializes the cycle counter and timestamp for this runCPU() invocation, and optionally * calcStartTime() initializes the cycle counter and timestamp for this runCPU() invocation, and optionally
@ -963,20 +1128,24 @@ class CPU extends Component {
this.calcStartTime(); this.calcStartTime();
try { try {
do { do {
var nCyclesPerBurst = (this.flags.checksum? 1 : this.aCounts.nCyclesPerBurst); /*
* 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.
*/
var nCycles = (this.flags.checksum? 1 : this.aCounts.nCyclesPerBurst);
if (this.chipset) { if (this.chipset) {
this.chipset.updateAllTimers(); this.chipset.updateAllTimers();
nCyclesPerBurst = this.chipset.getTimerCycleLimit(0, nCyclesPerBurst); nCycles = this.chipset.getTimerCycleLimit(0, nCycles);
nCyclesPerBurst = this.chipset.getRTCCycleLimit(nCyclesPerBurst); nCycles = this.chipset.getRTCCycleLimit(nCycles);
} }
/* /*
* nCyclesPerBurst is how many cycles we WANT to run on each iteration of stepCPU(), but it may run * Execute the burst.
* significantly less (or slightly more, since we can't execute partial instructions).
*/ */
try { try {
this.stepCPU(nCyclesPerBurst); this.stepCPU(nCycles);
} }
catch(exception) { catch(exception) {
if (typeof exception != "number") throw exception; if (typeof exception != "number") throw exception;
@ -992,16 +1161,22 @@ class CPU extends Component {
} }
/* /*
* nBurstCycles, less any remaining nStepCycles, is how many cycles stepCPU() ACTUALLY ran (nCycles). * Terminate the burst, returning the number of cycles that stepCPU() actually ran.
* We add that to nCyclesThisRun, as well as nRunCycles, which is the cycle count since the CPU first */
* started running. nCycles = this.endBurst(true);
/*
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU started).
*/ */
var nCycles = this.nBurstCycles - this.nStepCycles;
this.nRunCycles += nCycles;
this.aCounts.nCyclesThisRun += nCycles; this.aCounts.nCyclesThisRun += nCycles;
this.addCycles(0, true); this.nRunCycles += nCycles;
this.updateChecksum(nCycles); this.updateChecksum(nCycles);
/*
* Update any/all timers, firing those whose cycle countdowns have reached (or dropped below) zero.
*/
this.updateTimers(nCycles);
this.aCounts.nCyclesNextVideoUpdate -= nCycles; this.aCounts.nCyclesNextVideoUpdate -= nCycles;
if (this.aCounts.nCyclesNextVideoUpdate <= 0) { if (this.aCounts.nCyclesNextVideoUpdate <= 0) {
this.aCounts.nCyclesNextVideoUpdate += this.aCounts.nCyclesPerVideoUpdate; this.aCounts.nCyclesNextVideoUpdate += this.aCounts.nCyclesPerVideoUpdate;
@ -1025,41 +1200,49 @@ class CPU extends Component {
this.stopCPU(); this.stopCPU();
this.updateCPU(); this.updateCPU();
if (this.cmp) this.cmp.stop(Usr.getTime(), this.getCycles()); if (this.cmp) this.cmp.stop(Usr.getTime(), this.getCycles());
this.setBusy(false);
this.setError(e.stack || e.message); this.setError(e.stack || e.message);
return; return;
} }
setTimeout(this.onRunTimeout, this.calcRemainingTime());
if (this.flags.running) setTimeout(this.onRunTimeout, this.calcRemainingTime());
} }
/** /**
* startCPU(fUpdateFocus) * startCPU(fUpdateFocus)
* *
* WARNING: Other components must use runCPU() to get the CPU running; this is a runCPU() helper function only. * For use by any component that wants to start the CPU.
* *
* @param {boolean} [fUpdateFocus] * @param {boolean} [fUpdateFocus]
* @return {boolean}
*/ */
startCPU(fUpdateFocus) startCPU(fUpdateFocus)
{ {
if (!this.flags.running) { if (this.isError()) {
/* return false;
* 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.
*/
this.setSpeed();
if (this.cmp) this.cmp.start(this.aCounts.msStartRun, this.getCycles());
this.flags.running = true;
this.flags.starting = true;
if (this.chipset) this.chipset.start();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
if (this.cmp) {
this.cmp.updateStatus(true);
if (fUpdateFocus) this.cmp.updateFocus(true);
}
} }
if (this.flags.running) {
this.println(this.toString() + " busy");
return false;
}
/*
* 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.
*/
this.setSpeed();
this.flags.running = true;
this.flags.starting = true;
if (this.chipset) this.chipset.start();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
if (this.cmp) {
this.cmp.updateStatus(true);
if (fUpdateFocus) this.cmp.updateFocus(true);
this.cmp.start(this.aCounts.msStartRun, this.getCycles());
}
setTimeout(this.onRunTimeout, 0);
return true;
} }
/** /**
@ -1086,20 +1269,27 @@ class CPU extends Component {
* *
* @this {CPU} * @this {CPU}
* @param {boolean} [fComplete] * @param {boolean} [fComplete]
* @return {boolean} true if the CPU was stopped, false if it was already stopped
*/ */
stopCPU(fComplete) stopCPU(fComplete)
{ {
this.isBusy(true); var fStopped = false;
this.endBurst();
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
if (this.flags.running) { if (this.flags.running) {
this.endBurst();
this.addCycles(this.nRunCycles);
this.nRunCycles = 0;
this.flags.running = false; this.flags.running = false;
if (this.chipset) this.chipset.stop(); if (this.chipset) this.chipset.stop();
var controlRun = this.bindings["run"]; var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Run"; if (controlRun) controlRun.textContent = "Run";
if (this.cmp) {
this.cmp.stop(Component.getTime(), this.getCycles());
}
if (!this.dbg) this.status("Stopped");
fStopped = true;
} }
this.flags.complete = fComplete; this.flags.complete = fComplete;
return fStopped;
} }
/** /**

View file

@ -2695,16 +2695,17 @@ class DebuggerX86 extends Debugger {
} }
/** /**
* runCPU(fUpdateFocus) * startCPU(fUpdateFocus, fQuiet)
* *
* @this {DebuggerX86} * @this {DebuggerX86}
* @param {boolean} [fUpdateFocus] is true to update focus * @param {boolean} [fUpdateFocus] is true to update focus
* @param {boolean} [fQuiet]
* @return {boolean} true if run request successful, false if not * @return {boolean} true if run request successful, false if not
*/ */
runCPU(fUpdateFocus) startCPU(fUpdateFocus, fQuiet)
{ {
if (!this.isCPUAvail()) return false; if (!this.checkCPU(fQuiet)) return false;
this.cpu.runCPU(fUpdateFocus); this.cpu.startCPU(fUpdateFocus);
return true; return true;
} }
@ -2719,7 +2720,7 @@ class DebuggerX86 extends Debugger {
*/ */
stepCPU(nCycles, fRegs, fUpdateCPU) stepCPU(nCycles, fRegs, fUpdateCPU)
{ {
if (!this.isCPUAvail()) return false; if (!this.checkCPU()) return false;
this.nCycles = 0; this.nCycles = 0;
do { do {
@ -2731,6 +2732,9 @@ class DebuggerX86 extends Debugger {
*/ */
if (this.checksEnabled()) this.checkInstruction(this.cpu.regLIP, 0); if (this.checksEnabled()) this.checkInstruction(this.cpu.regLIP, 0);
} }
/*
* For our typically tiny bursts (usually single instructions), mimic what runCPU() does.
*/
try { try {
var nCyclesStep = this.cpu.stepCPU(nCycles); var nCyclesStep = this.cpu.stepCPU(nCycles);
if (nCyclesStep > 0) { if (nCyclesStep > 0) {
@ -2750,7 +2754,7 @@ class DebuggerX86 extends Debugger {
} while (this.cpu.opFlags & X86.OPFLAG_PREFIXES); } while (this.cpu.opFlags & X86.OPFLAG_PREFIXES);
/* /*
* Because we called cpu.stepCPU() and not cpu.runCPU(), we must nudge the cpu's update code, * Because we called cpu.stepCPU() and not cpu.startCPU(), we must nudge the cpu's update code,
* and then update our own state. Normally, the only time fUpdateCPU will be false is when doTrace() * and then update our own state. Normally, the only time fUpdateCPU will be false is when doTrace()
* is calling us in a loop, in which case it will perform its own updateCPU() when it's done. * is calling us in a loop, in which case it will perform its own updateCPU() when it's done.
*/ */
@ -2795,23 +2799,20 @@ class DebuggerX86 extends Debugger {
} }
/** /**
* isCPUAvail() * checkCPU(fQuiet)
* *
* Make sure the CPU is ready (finished initializing), not busy (already running), and not in an error state. * Make sure the CPU is ready (finished initializing), not busy (already running), and not in an error state.
* *
* @this {DebuggerX86} * @this {DebuggerX86}
* @param {boolean} [fQuiet]
* @return {boolean} * @return {boolean}
*/ */
isCPUAvail() checkCPU(fQuiet)
{ {
if (!this.cpu) if (!this.cpu || !this.cpu.isReady() || !this.cpu.isPowered() || this.cpu.isRunning()) {
return false; if (!fQuiet) this.println("cpu busy or unavailable, command ignored");
if (!this.cpu.isReady())
return false;
if (!this.cpu.isPowered())
return false;
if (this.cpu.isBusy())
return false; return false;
}
return !this.cpu.isError(); return !this.cpu.isError();
} }
@ -5928,7 +5929,7 @@ class DebuggerX86 extends Debugger {
this.parseAddrOptions(dbgAddr, sOptions); this.parseAddrOptions(dbgAddr, sOptions);
this.setTempBreakpoint(dbgAddr); this.setTempBreakpoint(dbgAddr);
} }
if (!this.runCPU(true)) { if (!this.startCPU(true)) {
if (!fQuiet) this.println("cpu busy or unavailable, run command ignored"); if (!fQuiet) this.println("cpu busy or unavailable, run command ignored");
} }
} }
@ -6052,7 +6053,7 @@ class DebuggerX86 extends Debugger {
if (this.nStep) { if (this.nStep) {
this.setTempBreakpoint(dbgAddr); this.setTempBreakpoint(dbgAddr);
if (!this.runCPU()) { if (!this.startCPU()) {
if (this.cmp) this.cmp.updateFocus(); if (this.cmp) this.cmp.updateFocus();
this.nStep = 0; this.nStep = 0;
} }

View file

@ -1847,7 +1847,7 @@ class X86CPU extends CPU {
} }
state.set(1, a); state.set(1, a);
state.set(2, [this.segData.sName, this.segStack.sName, this.opFlags, this.opPrefixes, this.intFlags, this.regEA, this.regEAWrite]); state.set(2, [this.segData.sName, this.segStack.sName, this.opFlags, this.opPrefixes, this.intFlags, this.regEA, this.regEAWrite]);
state.set(3, [0, this.nTotalCycles, this.getSpeed(), fRunning]); state.set(3, [0, this.nTotalCycles, this.getSpeed(), fRunning, this.saveTimers()]);
state.set(4, this.bus.saveMemory(this.isPagingEnabled())); state.set(4, this.bus.saveMemory(this.isPagingEnabled()));
return state.data(); return state.data();
} }
@ -1914,16 +1914,18 @@ class X86CPU extends CPU {
this.opFlags = a[2]; this.opFlags = a[2];
this.opPrefixes = a[3]; this.opPrefixes = a[3];
this.intFlags = a[4]; this.intFlags = a[4];
this.regEA = a[5]; this.regEA = a[5]; // save/restore of last EA calculation(s) isn't strictly necessary,
this.regEAWrite = a[6]; // save/restore of last EA calculation(s) isn't strictly necessary, but they may be of some interest to, say, the Debugger this.regEAWrite = a[6]; // but they may be of some interest to, say, the Debugger
a = data[3]; // a[0] was previously nBurstDivisor (no longer used) a = data[3];
this.nTotalCycles = a[1]; this.nTotalCycles = a[1]; // a[0] was previously nBurstDivisor (no longer used)
this.setSpeed(a[2]); // old states didn't contain a value from getSpeed(), but setSpeed() checks this.setSpeed(a[2]); // old states didn't contain a value from getSpeed(), but setSpeed() checks
if (a[3] != null) { // less old states didn't preserve the original running state, so we must check it if (a[3] != null) { // less old states didn't preserve the original running state, so we must check it
this.flags.autoStart = a[3]; this.flags.autoStart = a[3];
} }
if (a[4] != null) {
this.restoreTimers(a[4]);
}
return fRestored; return fRestored;
} }

View file

@ -273,8 +273,7 @@ class CPUPDP11 extends Component {
* context, a machine without focus is like a day without sunshine, but in reality, focus should only be * context, a machine without focus is like a day without sunshine, but in reality, focus should only be
* forced when the user takes some other machine-related action. * forced when the user takes some other machine-related action.
*/ */
this.startCPU(); return this.startCPU();
return true;
} }
return false; return false;
} }