Resolved a problem with the original timer implementation, where a call to setTimer() made in the middle of a CPU burst would not properly account for cycles already executed as part of the current burst

This commit is contained in:
Jeff Parsons 2016-10-12 22:08:45 -07:00 committed by Jeff Parsons
commit 8bb978d7a9
7 changed files with 423 additions and 406 deletions

View file

@ -973,7 +973,13 @@ CPU8080.prototype.setTimer = function(iTimer, ms, fReset)
if (iTimer >= 0 && iTimer < this.aTimers.length) {
if (fReset || this.aTimers[iTimer][0] < 0) {
nCycles = this.getMSCycles(ms);
this.aTimers[iTimer][0] = nCycles;
/*
* 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 above cycle timeout by the number of cycles that the burst executed.
*/
this.aTimers[iTimer][0] = nCycles + this.endBurst();
}
}
return nCycles;
@ -1036,14 +1042,18 @@ CPU8080.prototype.updateTimers = function(nCycles)
};
/**
* endBurst()
* endBurst(fReset)
*
* @this {CPU8080}
* @param {boolean} [fReset]
* @return {number} (number of cycles executed in the most recent burst)
*/
CPU8080.prototype.endBurst = function()
CPU8080.prototype.endBurst = function(fReset)
{
this.nBurstCycles -= this.nStepCycles;
var nCycles = this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
if (fReset) this.nBurstCycles = 0;
return nCycles;
};
/**
@ -1071,35 +1081,34 @@ CPU8080.prototype.runCPU = function(fUpdateFocus)
try {
do {
/*
* nCyclesPerBurst 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
* nCyclesPerBurst downward if any CPU timers need to fire during the next burst.
* 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 nCyclesPerBurst = this.getBurstCycles(this.flags.checksum? 1 : this.nCyclesPerYield);
var nCycles = this.getBurstCycles(this.flags.checksum? 1 : this.nCyclesPerYield);
/*
* Execute the burst.
*/
this.stepCPU(nCyclesPerBurst);
this.stepCPU(nCycles);
/*
* nCycles is how many cycles stepCPU() actually ran (nBurstCycles less any remaining nStepCycles).
* Terminate the burst, returning the number of cycles that stepCPU() actually ran.
*/
var nCycles = this.nBurstCycles - this.nStepCycles;
/*
* Update any/all timers, firing those whose cycle countdowns have reached (or dropped below) zero.
*/
this.updateTimers(nCycles);
nCycles = this.endBurst(true);
/*
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU first started).
*/
this.nCyclesThisRun += nCycles;
this.nRunCycles += nCycles;
this.addCycles(0, true);
this.updateChecksum(nCycles);
/*
* Update any/all timers, firing those whose cycle countdowns have reached (or dropped below) zero.
*/
this.updateTimers(nCycles);
this.nCyclesNextYield -= nCycles;
if (this.nCyclesNextYield <= 0) {
this.nCyclesNextYield += this.nCyclesPerYield;

View file

@ -936,7 +936,13 @@ CPUPDP11.prototype.setTimer = function(iTimer, ms, fReset)
if (iTimer >= 0 && iTimer < this.aTimers.length) {
if (fReset || this.aTimers[iTimer][0] < 0) {
nCycles = this.getMSCycles(ms);
this.aTimers[iTimer][0] = nCycles;
/*
* 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 above cycle timeout by the number of cycles that the burst executed.
*/
this.aTimers[iTimer][0] = nCycles + this.endBurst();
}
}
return nCycles;
@ -999,14 +1005,18 @@ CPUPDP11.prototype.updateTimers = function(nCycles)
};
/**
* endBurst()
* endBurst(fReset)
*
* @this {CPUPDP11}
* @param {boolean} [fReset]
* @return {number} (number of cycles executed in the most recent burst)
*/
CPUPDP11.prototype.endBurst = function()
CPUPDP11.prototype.endBurst = function(fReset)
{
this.nBurstCycles -= this.nStepCycles;
var nCycles = this.nBurstCycles -= this.nStepCycles;
this.nStepCycles = 0;
if (fReset) this.nBurstCycles = 0;
return nCycles;
};
/**
@ -1027,17 +1037,17 @@ CPUPDP11.prototype.runCPU = function()
try {
do {
/*
* nCyclesPerBurst 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
* nCyclesPerBurst downward if any CPU timers need to fire during the next burst.
* 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 nCyclesPerBurst = this.getBurstCycles(this.flags.checksum? 1 : this.nCyclesPerYield);
var nCycles = this.getBurstCycles(this.flags.checksum? 1 : this.nCyclesPerYield);
/*
* Execute the burst.
*/
try {
this.stepCPU(nCyclesPerBurst);
this.stepCPU(nCycles);
}
catch(exception) {
/*
@ -1049,25 +1059,22 @@ CPUPDP11.prototype.runCPU = function()
}
/*
* nCycles is how many cycles stepCPU() actually ran (nBurstCycles less any remaining nStepCycles);
* that calculation matches the return value from stepCPU(), but since it may have thrown an exception,
* we can't rely on it.
* Terminate the burst, returning the number of cycles that stepCPU() actually ran.
*/
var nCycles = this.nBurstCycles - this.nStepCycles;
/*
* Update any/all timers, firing those whose cycle countdowns have reached (or dropped below) zero.
*/
this.updateTimers(nCycles);
nCycles = this.endBurst(true);
/*
* Add nCycles to nCyclesThisRun, as well as nRunCycles (the cycle count since the CPU first started).
*/
this.nCyclesThisRun += nCycles;
this.nRunCycles += nCycles;
this.addCycles(0, true);
this.updateChecksum(nCycles);
/*
* Update any/all timers, firing those whose cycle countdowns have reached (or dropped below) zero.
*/
this.updateTimers(nCycles);
this.nCyclesNextYield -= nCycles;
if (this.nCyclesNextYield <= 0) {
this.nCyclesNextYield += this.nCyclesPerYield;

View file

@ -262,7 +262,7 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
this.triggerReceiveInterrupt = this.cpu.addTrigger(PDP11.DL11.RVEC, PDP11.DL11.PRI);
this.timerReceiveInterrupt = this.cpu.addTimer(function() {
this.timerReceiveInterrupt = this.cpu.addTimer(function readyReceiver() {
if (!(serial.rcsr & PDP11.DL11.RCSR.RD)) {
if (serial.abReceive.length) {
serial.rbuf = serial.abReceive.shift();
@ -276,7 +276,7 @@ SerialPortPDP11.prototype.initBus = function(cmp, bus, cpu, dbg)
this.triggerTransmitInterrupt = this.cpu.addTrigger(PDP11.DL11.XVEC, PDP11.DL11.PRI);
this.timerTransmitInterrupt = this.cpu.addTimer(function() {
this.timerTransmitInterrupt = this.cpu.addTimer(function readyTransmitter() {
serial.xcsr |= PDP11.DL11.XCSR.READY;
if (serial.xcsr & PDP11.DL11.XCSR.TIE) {
serial.cpu.setTrigger(serial.triggerTransmitInterrupt);