v1.37.1: Fixed a CPU bug wherein resetting a running CPU could result in multiple runCPU() timeout handlers being created (there should never be more than one outstanding runCPU() setTimeout request); also modified the Keyboard component to use a CPU-driven timer instead of setTimeout() for keystroke injection, so that stopping/starting the CPU won't interfere with the injection process
This commit is contained in:
parent
f874063366
commit
22728fb7e9
289 changed files with 1354 additions and 1176 deletions
|
|
@ -114,3 +114,19 @@ the drive controller (or DMA controller, if used) to create a BackTrack object r
|
|||
adding that object to the global BackTrack object array, and then associating the corresponding BackTrack index with
|
||||
the first byte of RAM where the sector was loaded. Subsequent bytes of RAM containing the rest of the sector will refer
|
||||
to the same BackTrack object, using BackTrack indexes containing offsets 1-511.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
### Microsoft Bus Mouse
|
||||
|
||||
See this [Microsoft Bus Mouse implementation](https://www.virtualbox.org/svn/vbox/trunk/src/VBox/ExtPacks/BusMouseSample/BusMouse.cpp),
|
||||
written by [Michal Necasek](http://www.os2museum.com) for Oracle's [VirtualBox](https://www.virtualbox.org).
|
||||
|
||||
It references two Microsoft KnowledgeBase (KB) Articles of note:
|
||||
|
||||
- Q12230 (regarding the Bus Mouse adapter's IRQ configuration jumpers)
|
||||
- Q46369 (regarding the Bus Mouse adapter's Intel 8255A compatible chip)
|
||||
|
||||
Those articles are no longer available online, thanks to Microsoft's lack of interest in preserving the past, including
|
||||
its own past. However, the PCjs Project is working on fixing that. Stay tuned.
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ class CPU extends Component {
|
|||
|
||||
this.counts = {};
|
||||
this.counts.nBaseCyclesPerSecond = nCycles;
|
||||
this.counts.msPerYield = Math.round(1000 / CPU.YIELDS_PER_SECOND);
|
||||
|
||||
/*
|
||||
* nTargetMultiplier replaces the old "speed" variable (0, 1, 2) and eliminates the need for
|
||||
|
|
@ -92,7 +93,6 @@ class CPU extends Component {
|
|||
* at which point we reset the target back to the default.
|
||||
*/
|
||||
this.counts.nBaseMultiplier = this.counts.nCurrentMultiplier = this.counts.nTargetMultiplier = nMultiplier;
|
||||
|
||||
this.counts.mhzBase = Math.round(this.counts.nBaseCyclesPerSecond / 10000) / 100;
|
||||
this.counts.mhzCurrent = this.counts.mhzTarget = this.counts.mhzBase * this.counts.nTargetMultiplier;
|
||||
|
||||
|
|
@ -129,6 +129,7 @@ class CPU extends Component {
|
|||
*/
|
||||
this.aTimers = [];
|
||||
|
||||
this.idRunTimeout = 0;
|
||||
this.onRunTimeout = this.runCPU.bind(this); // function onRunTimeout() { cpu.runCPU(); };
|
||||
}
|
||||
|
||||
|
|
@ -574,7 +575,6 @@ class CPU extends Component {
|
|||
if (!nMultiplier || nMultiplier > this.counts.nTargetMultiplier) {
|
||||
nMultiplier = 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 * nMultiplier);
|
||||
this.counts.nCurrentMultiplier = nMultiplier;
|
||||
}
|
||||
|
|
@ -771,7 +771,9 @@ class CPU extends Component {
|
|||
|
||||
this.counts.nCyclesThisRun = 0;
|
||||
this.counts.msStartThisRun = Usr.getTime();
|
||||
if (!this.counts.msStartRun) this.counts.msStartRun = this.counts.msStartThisRun;
|
||||
if (!this.counts.msStartRun) {
|
||||
this.counts.msStartRun = this.counts.msStartThisRun;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to detect situations where the browser may have throttled us, such as when the user switches
|
||||
|
|
@ -795,13 +797,12 @@ class CPU extends Component {
|
|||
*
|
||||
* TODO: Consider calling yieldCPU() sooner from message(), so that it can arrange for the msEndThisRun
|
||||
* "snapshot" to occur sooner; it's unclear, however, whether that will really improve the CPU's ability
|
||||
* to hit its target speed, since you would expect any instruction that displays a message to be an
|
||||
* EXTREMELY slow instruction.
|
||||
* to hit its target speed, since any instruction that displays a message is unavoidably slooooow.
|
||||
*/
|
||||
var msDelta = 0;
|
||||
if (this.counts.msEndThisRun) {
|
||||
var msDelta = this.counts.msStartThisRun - this.counts.msEndThisRun;
|
||||
msDelta = this.counts.msStartThisRun - this.counts.msEndThisRun;
|
||||
if (msDelta > this.counts.msPerYield) {
|
||||
if (MAXDEBUG) this.println("large time delay: " + msDelta + "ms");
|
||||
this.counts.msStartRun += msDelta;
|
||||
/*
|
||||
* Bumping msStartRun forward should NEVER cause it to exceed msStartThisRun; however, just
|
||||
|
|
@ -878,10 +879,23 @@ class CPU extends Component {
|
|||
}
|
||||
|
||||
if (DEBUG && this.messageEnabled(Messages.CPU)) {
|
||||
this.printMessage("calcRemainingTime: sleep " + msRemainsThisRun + "ms after " + (this.counts.msEndThisRun - this.counts.msStartThisRun) + "ms burst");
|
||||
/*
|
||||
* Every time the browser gives us another chance to run, we want to display our targets for that run
|
||||
* here, followed by what we accomplished in that run.
|
||||
*/
|
||||
this.printMessage(Str.sprintf("%3dms run %3dms wait %6dcy %6.2fmhz %6dms total %8dcy total %6.2fmhz total",
|
||||
msElapsedThisRun,
|
||||
msRemainsThisRun,
|
||||
this.counts.nCyclesThisRun,
|
||||
Math.round(this.counts.nCyclesThisRun / (msElapsedThisRun * 10)) / 100,
|
||||
msElapsed,
|
||||
nCycles,
|
||||
this.counts.mhzCurrent
|
||||
));
|
||||
}
|
||||
|
||||
this.counts.msEndThisRun += msRemainsThisRun;
|
||||
|
||||
return msRemainsThisRun;
|
||||
}
|
||||
|
||||
|
|
@ -1108,14 +1122,14 @@ class CPU extends Component {
|
|||
if (timer[1] < 0) continue;
|
||||
timer[1] -= nCycles;
|
||||
if (timer[1] <= 0) {
|
||||
if (DEBUG && this.messageEnabled(Messages.CPU)) {
|
||||
if (DEBUG && this.messageEnabled(Messages.CPU | Messages.TIMER)) { // CPU TIMER message (as opposed to CHIPSET TIMER message)
|
||||
this.printMessage("updateTimer(" + nCycles + "): firing " + timer[0] + " with only " + (timer[1] + nCycles) + " cycles left");
|
||||
}
|
||||
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]);
|
||||
if (DEBUG && this.messageEnabled(Messages.CPU)) {
|
||||
if (DEBUG && this.messageEnabled(Messages.CPU | Messages.TIMER)) { // CPU TIMER message (as opposed to CHIPSET TIMER message)
|
||||
this.printMessage("updateTimer(" + nCycles + "): rearming " + timer[0] + " for " + timer[2] + "ms (" + timer[1] + " cycles)");
|
||||
}
|
||||
}
|
||||
|
|
@ -1145,6 +1159,7 @@ class CPU extends Component {
|
|||
*/
|
||||
runCPU()
|
||||
{
|
||||
this.idRunTimeout = 0;
|
||||
if (!this.flags.running) return;
|
||||
|
||||
/*
|
||||
|
|
@ -1208,7 +1223,10 @@ class CPU extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.flags.running) setTimeout(this.onRunTimeout, this.calcRemainingTime());
|
||||
if (this.flags.running) {
|
||||
this.assert(!this.idRunTimeout);
|
||||
this.idRunTimeout = setTimeout(this.onRunTimeout, this.calcRemainingTime());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1229,6 +1247,10 @@ class CPU extends Component {
|
|||
if (!fQuiet) this.println(this.toString() + " busy");
|
||||
return false;
|
||||
}
|
||||
if (this.idRunTimeout) {
|
||||
clearTimeout(this.idRunTimeout);
|
||||
this.idRunTimeout = 0;
|
||||
}
|
||||
/*
|
||||
* 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, and calculates the maximum number
|
||||
|
|
@ -1245,7 +1267,8 @@ class CPU extends Component {
|
|||
if (fUpdateFocus) this.cmp.updateFocus(true);
|
||||
this.cmp.start(this.counts.msStartRun, this.getCycles());
|
||||
}
|
||||
setTimeout(this.onRunTimeout, 0);
|
||||
this.assert(!this.idRunTimeout);
|
||||
this.idRunTimeout = setTimeout(this.onRunTimeout, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,10 +138,11 @@ class Keyboard extends Component {
|
|||
*/
|
||||
this.aKeysActive = [];
|
||||
|
||||
this.msAutoRepeat = 500;
|
||||
this.msNextRepeat = 100;
|
||||
this.msAutoRelease = 50;
|
||||
this.msInjectDelay = 150; // number of milliseconds between injected keystrokes
|
||||
this.msAutoRepeat = 500;
|
||||
this.msNextRepeat = 100;
|
||||
this.msAutoRelease = 50;
|
||||
this.msInjectDefault = 1000; // number of milliseconds between injected keystrokes
|
||||
this.msInjectDelay = 0; // set by the initial injectKeys() call
|
||||
|
||||
/*
|
||||
* autoType records the machine's specified autoType sequence, if any. At the appropriate signal(s),
|
||||
|
|
@ -378,8 +379,15 @@ class Keyboard extends Component {
|
|||
this.bus = bus;
|
||||
this.cpu = cpu;
|
||||
this.dbg = dbg;
|
||||
|
||||
var kbd = this;
|
||||
this.timerInject = this.cpu.addTimer(this.id + ".inject", function() {
|
||||
kbd.injectKeysFromBuffer();
|
||||
});
|
||||
|
||||
this.chipset = cmp.getMachineComponent("ChipSet");
|
||||
this.autoType = cmp.getMachineParm('autoType') || this.autoType;
|
||||
|
||||
cpu.addIntNotify(Interrupts.DOS, this.intDOS.bind(this));
|
||||
}
|
||||
|
||||
|
|
@ -993,7 +1001,7 @@ class Keyboard extends Component {
|
|||
*
|
||||
* @this {Keyboard}
|
||||
* @param {string|undefined} sKeys
|
||||
* @param {number} [msDelay] is an optional injection delay (default is msInjectDelay)
|
||||
* @param {number} [msDelay] is an optional injection delay (default is msInjectDefault)
|
||||
* @return {boolean}
|
||||
*/
|
||||
injectKeys(sKeys, msDelay)
|
||||
|
|
@ -1001,19 +1009,19 @@ class Keyboard extends Component {
|
|||
if (sKeys && !this.sInjectBuffer) {
|
||||
this.sInjectBuffer = this.parseKeys(sKeys);
|
||||
if (!COMPILED) this.log("injectKeys(\"" + this.sInjectBuffer.split("\n").join("\\n") + "\")");
|
||||
this.injectKeysFromBuffer(msDelay || this.msInjectDelay);
|
||||
this.msInjectDelay = msDelay || this.msInjectDefault;
|
||||
this.injectKeysFromBuffer();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* injectKeysFromBuffer(msDelay)
|
||||
* injectKeysFromBuffer()
|
||||
*
|
||||
* @this {Keyboard}
|
||||
* @param {number} msDelay is the delay between injected keys
|
||||
*/
|
||||
injectKeysFromBuffer(msDelay)
|
||||
injectKeysFromBuffer()
|
||||
{
|
||||
var charCode = 0;
|
||||
while (this.sInjectBuffer.length > 0 && !charCode) {
|
||||
|
|
@ -1031,7 +1039,7 @@ class Keyboard extends Component {
|
|||
* by "test;" and return.
|
||||
*/
|
||||
if (charCode >= 0xF0) {
|
||||
msDelay = ((charCode - 0xF0) * 100) || this.msInjectDelay;
|
||||
this.msInjectDelay = ((charCode - 0xF0) * 100) || this.msInjectDefault;
|
||||
charCode = 0;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1049,11 +1057,7 @@ class Keyboard extends Component {
|
|||
this.fnInjectReady = null;
|
||||
}
|
||||
} else {
|
||||
setTimeout(function(kbd) {
|
||||
return function onInjectKeyTimeout() {
|
||||
kbd.injectKeysFromBuffer(msDelay);
|
||||
};
|
||||
}(this), msDelay);
|
||||
this.cpu.setTimer(this.timerInject, this.msInjectDelay);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue