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:
parent
d8804faba0
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
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue