Updated more PCx86 components to use the CPU's timer functions to do callbacks

This commit is contained in:
Jeff Parsons 2017-08-05 11:01:28 -07:00 committed by Jeff Parsons
commit f71b4d648c
14 changed files with 3136 additions and 3184 deletions

View file

@ -571,19 +571,14 @@ class CPU extends Component {
* is driven by the following values:
*
* CPU.YIELDS_PER_SECOND (eg, 30)
* CPU.VIDEO_UPDATES_PER_SECOND (eg, 60)
* CPU.STATUS_UPDATES_PER_SECOND (eg, 5)
*
* 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 burst cycles from yield, video, and status cycle "threshold"
* counters. 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().
*
* Similarly, whenever the "next video update" cycle counter goes to (or below) zero, we call updateVideo(),
* and whenever the "next status update" cycle counter goes to (or below) zero, we call updateStatus().
* 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().
*
* @this {CPU}
* @param {boolean} [fRecalc] is true if the caller wants to recalculate thresholds based on the most recent
@ -591,36 +586,19 @@ class CPU extends Component {
*/
calcCycles(fRecalc)
{
/*
* Calculate the most cycles we're allowed to execute in a single "burst"
*/
var nMostUpdatesPerSecond = CPU.YIELDS_PER_SECOND;
if (nMostUpdatesPerSecond < CPU.VIDEO_UPDATES_PER_SECOND) nMostUpdatesPerSecond = CPU.VIDEO_UPDATES_PER_SECOND;
if (nMostUpdatesPerSecond < CPU.STATUS_UPDATES_PER_SECOND) nMostUpdatesPerSecond = CPU.STATUS_UPDATES_PER_SECOND;
/*
* Calculate cycle "per" values for the yield, video update, and status update cycle counters
*/
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.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 / CPU.VIDEO_UPDATES_PER_SECOND * vMultiplier);
this.aCounts.nCyclesPerStatusUpdate = Math.floor(this.aCounts.nCyclesPerSecond / CPU.STATUS_UPDATES_PER_SECOND * vMultiplier);
/*
* And initialize "next" yield, video update, and status update cycle "threshold" counters to those "per" values
* Initialize "next" yield 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.aCounts.nCyclesRecalc = 0;
}
@ -938,15 +916,21 @@ class CPU extends Component {
}
/**
* addTimer(callBack)
* addTimer(callBack, ms)
*
* 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
* 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
* value (in milliseconds) to have the timer fire automatically at regular intervals. 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.
* Internally, each timer entry is a preallocated Array with three entries:
*
* [0]: countdown value, in cycles
* [1]: automatic setTimer value, if any, in milliseconds
* [2]: callback function
*
* 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.
*
@ -957,12 +941,14 @@ class CPU extends Component {
*
* @this {CPU}
* @param {function()} callBack
* @param {number} [ms] (if set, enables automatic setTimer calls)
* @return {number} timer index
*/
addTimer(callBack)
addTimer(callBack, ms = -1)
{
var iTimer = this.aTimers.length;
this.aTimers.push([-1, callBack]);
this.aTimers.push([-1, ms, callBack]);
if (ms >= 0) this.setTimer(iTimer, ms);
return iTimer;
}
@ -991,7 +977,8 @@ class CPU extends Component {
{
var nCycles = -1;
if (iTimer >= 0 && iTimer < this.aTimers.length) {
if (fReset || this.aTimers[iTimer][0] < 0) {
var timer = this.aTimers[iTimer];
if (fReset || timer[0] < 0) {
nCycles = this.getMSCycles(ms);
/*
* We must now confront the following problem: if the CPU is currently executing a burst of cycles,
@ -1002,7 +989,7 @@ class CPU extends Component {
if (this.flags.running) {
nCycles += this.endBurst();
}
this.aTimers[iTimer][0] = nCycles;
timer[0] = nCycles;
}
}
return nCycles;
@ -1031,8 +1018,8 @@ class CPU extends Component {
*/
getBurstCycles(nCycles)
{
for (var i = this.aTimers.length - 1; i >= 0; i--) {
var timer = this.aTimers[i];
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]) {
@ -1051,9 +1038,9 @@ class CPU extends Component {
saveTimers()
{
var aTimerCycles = [];
for (var i = 0; i < this.aTimers.length; i++) {
var timer = this.aTimers[i];
aTimerCycles.push(timer[0]);
for (var iTimer = 0; iTimer < this.aTimers.length; iTimer++) {
var timer = this.aTimers[iTimer];
aTimerCycles.push([timer[0], timer[1]]);
}
return aTimerCycles;
}
@ -1067,9 +1054,10 @@ class CPU extends Component {
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];
for (var iTimer = 0; iTimer < this.aTimers.length && iTimer < aTimerCycles.length; iTimer++) {
var timer = this.aTimers[iTimer];
timer[0] = aTimerCycles[iTimer][0];
timer[1] = aTimerCycles[iTimer][1];
}
}
@ -1085,14 +1073,15 @@ class CPU extends Component {
*/
updateTimers(nCycles)
{
for (var i = this.aTimers.length - 1; i >= 0; i--) {
var timer = this.aTimers[i];
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[1](); // safe to invoke the callback function now
timer[2](); // safe to invoke the callback function now
if (timer[1] >= 0) this.setTimer(iTimer, timer[1]);
}
}
}
@ -1133,7 +1122,7 @@ class CPU extends Component {
* 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);
var nCycles = this.getBurstCycles(this.flags.checksum? 1 : this.aCounts.nCyclesPerYield);
if (this.chipset) {
this.chipset.updateAllTimers();
@ -1177,18 +1166,6 @@ class CPU extends Component {
*/
this.updateTimers(nCycles);
this.aCounts.nCyclesNextVideoUpdate -= nCycles;
if (this.aCounts.nCyclesNextVideoUpdate <= 0) {
this.aCounts.nCyclesNextVideoUpdate += this.aCounts.nCyclesPerVideoUpdate;
if (this.cmp) this.cmp.updateVideo();
}
this.aCounts.nCyclesNextStatusUpdate -= nCycles;
if (this.aCounts.nCyclesNextStatusUpdate <= 0) {
this.aCounts.nCyclesNextStatusUpdate += this.aCounts.nCyclesPerStatusUpdate;
if (this.cmp) this.cmp.updateStatus();
}
this.aCounts.nCyclesNextYield -= nCycles;
if (this.aCounts.nCyclesNextYield <= 0) {
this.aCounts.nCyclesNextYield += this.aCounts.nCyclesPerYield;
@ -1302,12 +1279,11 @@ class CPU extends Component {
* provides the old behavior.
*
* @this {CPU}
* @param {boolean} [fForce] (true to force a video update; used by the Debugger)
* @param {boolean} [fForce] (true to force a Computer update; used by the Debugger)
*/
updateCPU(fForce)
{
if (this.cmp) {
this.cmp.updateVideo(fForce);
this.cmp.updateStatus(fForce);
}
}
@ -1341,20 +1317,14 @@ class CPU extends Component {
* 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)
* this.aCounts.nCyclesPerVideoUpdate (this.aCounts.nCyclesPerSecond / CPU.VIDEO_UPDATES_PER_SECOND)
* this.aCounts.nCyclesPerStatusUpdate (this.aCounts.nCyclesPerSecond / CPU.STATUS_UPDATES_PER_SECOND)
* 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
* this.aCounts.nCyclesNextVideoUpdate <= this.aCounts.nCyclesPerVideoUpdate
* this.aCounts.nCyclesNextStatusUpdate <= this.aCounts.nCyclesPerStatusUpdate
* this.aCounts.nCyclesNextYield <= this.aCounts.nCyclesPerYield
*/
CPU.YIELDS_PER_SECOND = 30;
CPU.VIDEO_UPDATES_PER_SECOND = 60; // WARNING: if you change this, beware of side-effects in the Video component
CPU.STATUS_UPDATES_PER_SECOND = 2;
CPU.YIELDS_PER_SECOND = 30;
CPU.BUTTONS = ["power", "reset"];