Added 'diagnostics' (0, 1, or 2) and 'randomize' (0 or 1) PCx86 machine parameters to control diagnostic message display/prompting and video screen randomization, respectively

This commit is contained in:
Jeff Parsons 2017-08-09 11:42:38 -07:00 • committed by Jeff Parsons
commit aac9a13328
7 changed files with 1072 additions and 1023 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -117,6 +117,8 @@ class Computer extends Component {
* *
* url: the location of the machine XML file * url: the location of the machine XML file
* *
* diagnostics: 0 for none, 1 for normal diagnostics, and 2 for diagnostics with prompting
*
* If a predefined state is supplied AND it's successfully loaded, then resume behavior * If a predefined state is supplied AND it's successfully loaded, then resume behavior
* defaults to '1' (ie, resume enabled without prompting). * defaults to '1' (ie, resume enabled without prompting).
* *
@ -147,6 +149,8 @@ class Computer extends Component {
this.setMachineParms(parmsMachine); this.setMachineParms(parmsMachine);
this.fAutoPower = this.getMachineParm('autoPower', parmsComputer); this.fAutoPower = this.getMachineParm('autoPower', parmsComputer);
this.nDiagnostics = +this.getMachineParm('diagnostics', parmsComputer);
if (!(this.nDiagnostics >= 0 && this.nDiagnostics <= 2)) this.nDiagnostics = 1;
/* /*
* nPowerChange is 0 while the power state is stable, 1 while power is transitioning * nPowerChange is 0 while the power state is stable, 1 while power is transitioning
@ -218,6 +222,7 @@ class Computer extends Component {
this.printComputer = this.panel.print; this.printComputer = this.panel.print;
this.printlnComputer = this.panel.println; this.printlnComputer = this.panel.println;
} }
for (iComponent = 0; iComponent < aComponents.length; iComponent++) { for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent]; component = aComponents[iComponent];
component.notice = function noticeComputer(s, fPrintOnly, id) { component.notice = function noticeComputer(s, fPrintOnly, id) {
@ -232,8 +237,11 @@ class Computer extends Component {
return cmp.printlnComputer.call(this, s, type, id); return cmp.printlnComputer.call(this, s, type, id);
}.bind(component); }.bind(component);
} }
this.cDiagnosticScreens = 0; this.cDiagnosticScreens = 0;
if (!this.controlPanel) this.enableDiagnostics(); if (!this.controlPanel && this.nDiagnostics) {
this.enableDiagnostics();
}
this.println(PCX86.APPNAME + " v" + (XMLVERSION || PCX86.APPVERSION) + "\n" + COPYRIGHT + "\n" + LICENSE); this.println(PCX86.APPNAME + " v" + (XMLVERSION || PCX86.APPVERSION) + "\n" + COPYRIGHT + "\n" + LICENSE);
@ -351,19 +359,21 @@ class Computer extends Component {
*/ */
enableDiagnostics() enableDiagnostics()
{ {
for (var i = 0; i < this.aVideo.length; i++) { if (!this.cDiagnosticScreens) {
var video = this.aVideo[i]; for (var i = 0; i < this.aVideo.length; i++) {
if (video) { var video = this.aVideo[i];
var control = video.getTextArea(); if (video) {
if (control) { var control = video.getTextArea();
/* if (control) {
* By default, the Video textarea overlay has opacity and lineHeight styles set to "0" /*
* to make the overall textarea and its blinking caret invisible (respectively), so in order * By default, the Video textarea overlay has opacity and lineHeight styles set to "0"
* to use it as a diagnostic display, we must temporarily set both those styles to "1". * to make the overall textarea and its blinking caret invisible (respectively), so in order
*/ * to use it as a diagnostic display, we must temporarily set both those styles to "1".
control.style.opacity = "1"; */
control.style.lineHeight = "1"; control.style.opacity = "1";
this.cDiagnosticScreens++; control.style.lineHeight = "1";
this.cDiagnosticScreens++;
}
} }
} }
} }
@ -373,33 +383,43 @@ class Computer extends Component {
* disableDiagnostics() * disableDiagnostics()
* *
* @this {Computer} * @this {Computer}
* @return {boolean} (true if diagnostics were, or already are, disabled; false if they remain disabled)
*/ */
disableDiagnostics() disableDiagnostics()
{ {
for (var i = 0; i < this.aVideo.length; i++) { if (this.cDiagnosticScreens) {
var video = this.aVideo[i]; if (this.nDiagnostics == 2) {
if (video) { this.nDiagnostics++;
var control = video.getTextArea(); this.println("Press any key to continue...");
if (control) { return false;
var agent = Web.getUserAgent(); }
/* for (var i = 0; i < this.aVideo.length; i++) {
* Return the Video textarea overlay's opacity and lineHeight styles to their original values. var video = this.aVideo[i];
*/ if (video) {
control.style.opacity = "0"; var control = video.getTextArea();
control.style.lineHeight = "0"; if (control) {
/* var agent = Web.getUserAgent();
* Setting lineHeight in IE isn't sufficient to hide the caret; we must also set fontSize to "0", /*
* and we make the change IE-specific because it can have weird side-effects in other browsers (eg, * Return the Video textarea overlay's opacity and lineHeight styles to their original values.
* it makes Safari on iOS over-zoom whenever the textarea receives focus). And making it IE-specific */
* is, as usual, harder than it should be, because IE11 stopped identifying itself as "MSIE", hence control.style.opacity = "0";
* the additional "Trident" check. control.style.lineHeight = "0";
*/ /*
if (agent.indexOf("MSIE") >= 0 || agent.indexOf("Trident") >= 0) control.style.fontSize = "0"; * Setting lineHeight in IE isn't sufficient to hide the caret; we must also set fontSize to "0",
control.value = ""; * and we make the change IE-specific because it can have weird side-effects in other browsers (eg,
* it makes Safari on iOS over-zoom whenever the textarea receives focus). And making it IE-specific
* is, as usual, harder than it should be, because IE11 stopped identifying itself as "MSIE", hence
* the additional "Trident" check.
*/
if (agent.indexOf("MSIE") >= 0 || agent.indexOf("Trident") >= 0) control.style.fontSize = "0";
control.value = "";
}
} }
} }
this.cDiagnosticScreens = 0;
} }
this.cDiagnosticScreens = 0; this.nDiagnostics = 0;
return true;
} }
/** /**
@ -411,22 +431,39 @@ class Computer extends Component {
*/ */
outputDiagnostics(sMessage, sType) outputDiagnostics(sMessage, sType)
{ {
if (!this.cDiagnosticScreens) return; if (this.cDiagnosticScreens) {
for (var i = 0; i < this.aVideo.length; i++) { for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i]; var video = this.aVideo[i];
if (video) { if (video) {
var control = video.getTextArea(); var control = video.getTextArea();
if (control) { if (control) {
if (sType != Component.TYPE.PROGRESS || sMessage.slice(-3) != "...") { if (sType != Component.TYPE.PROGRESS || sMessage.slice(-3) != "...") {
Component.appendControl(control, sMessage + '\n'); Component.appendControl(control, sMessage + '\n');
} else { } else {
Component.replaceControl(control, sMessage, sMessage + '.'); Component.replaceControl(control, sMessage, sMessage + '.');
}
} }
} }
} }
} }
} }
/**
* notifyKbdEvent(event)
*
* This is called by the Keyboard component for all key presses, and it is effectively a no-op except
* in the one special case where disableDiagnostics() has delayed powerOn until a key is pressed.
*
* @this {Computer}
*/
notifyKbdEvent(event)
{
if (this.nDiagnostics == 3) {
this.nDiagnostics++;
this.setReady();
}
}
/** /**
* getMachineID() * getMachineID()
* *
@ -859,23 +896,28 @@ class Computer extends Component {
*/ */
donePowerOn(aParms) donePowerOn(aParms)
{ {
var stateComputer = aParms[0]; if (!this.flags.initDone) {
var fRepower = (aParms[1] < 0); if (!this.disableDiagnostics()) {
var fRestore = aParms[2]; this.setReady(false);
this.wait(this.donePowerOn, aParms);
return;
}
this.flags.initDone = true;
}
if (DEBUG && this.flags.powered && this.messageEnabled()) { if (DEBUG && this.flags.powered && this.messageEnabled()) {
this.printMessage("Computer.donePowerOn(): redundant"); this.printMessage("Computer.donePowerOn(): redundant");
} }
if (!this.flags.initDone) { var stateComputer = aParms[0];
this.disableDiagnostics(); var fRepower = (aParms[1] < 0);
this.flags.initDone = true; var fRestore = aParms[2];
}
this.flags.powered = true;
var controlPower = this.bindings["power"]; var controlPower = this.bindings["power"];
if (controlPower) controlPower.textContent = "Shutdown"; if (controlPower) controlPower.textContent = "Shutdown";
this.flags.powered = true;
/* /*
* Once we get to this point, we're guaranteed that all components are ready, so it's safe to power the CPU; * Once we get to this point, we're guaranteed that all components are ready, so it's safe to power the CPU;
* the CPU should begin executing immediately, unless a debugger is attached. * the CPU should begin executing immediately, unless a debugger is attached.

View file

@ -571,7 +571,9 @@ class CPU extends Component {
calcCycles() calcCycles()
{ {
var nMultiplier = this.counts.mhzCurrent / this.counts.mhzBase; var nMultiplier = this.counts.mhzCurrent / this.counts.mhzBase;
if (!nMultiplier || nMultiplier > this.counts.nTargetMultiplier) nMultiplier = this.counts.nTargetMultiplier; if (!nMultiplier || nMultiplier > this.counts.nTargetMultiplier) {
nMultiplier = this.counts.nTargetMultiplier;
}
this.counts.msPerYield = Math.round(1000 / CPU.YIELDS_PER_SECOND); 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.nCyclesPerYield = Math.floor(this.counts.nBaseCyclesPerSecond / CPU.YIELDS_PER_SECOND * nMultiplier);
this.counts.nCurrentMultiplier = nMultiplier; this.counts.nCurrentMultiplier = nMultiplier;

View file

@ -1647,6 +1647,8 @@ class Keyboard extends Component {
this.sInjectBuffer = ""; // actual key events should stop any injection currently in progress this.sInjectBuffer = ""; // actual key events should stop any injection currently in progress
this.cmp.notifyKbdEvent(event);
if (this.fAllDown) { if (this.fAllDown) {
var simCode = this.checkActiveKey(); var simCode = this.checkActiveKey();
if (simCode && this.isAlphaKey(simCode) && this.isAlphaKey(keyCode) && simCode != keyCode) { if (simCode && this.isAlphaKey(simCode) && this.isAlphaKey(keyCode) && simCode != keyCode) {

View file

@ -83,11 +83,11 @@ class Mouse extends Component {
this.setActive(false); this.setActive(false);
this.fCaptured = this.fLocked = false; this.fCaptured = this.fLocked = false;
/* /*
* Initially, no video devices, and therefore no input devices, are attached. initBus() will update aVideo, * Initially, no video devices, and therefore no screens, are attached. initBus() will update aVideo,
* and powerUp() will update aInput. * and powerUp() will update aScreens.
*/ */
this.aVideo = []; this.aVideo = [];
this.aInput = []; this.aScreens = [];
this.setReady(); this.setReady();
} }
@ -183,10 +183,10 @@ class Mouse extends Component {
} }
} }
if (this.componentAdapter) { if (this.componentAdapter) {
this.aInput = []; // ensure the input device array is empty before (re)filling it this.aScreens = []; // ensure the screen array is empty before (re)filling it
for (var i = 0; i < this.aVideo.length; i++) { for (var i = 0; i < this.aVideo.length; i++) {
var input = this.aVideo[i].getInput(this); var screen = this.aVideo[i].getScreen(this);
if (input) this.aInput.push(input); if (screen) this.aScreens.push(screen);
} }
} else { } else {
Component.warning(this.id + ": " + this.sAdapterType + " " + this.idAdapter + " unavailable"); Component.warning(this.id + ": " + this.sAdapterType + " " + this.idAdapter + " unavailable");
@ -322,8 +322,8 @@ class Mouse extends Component {
captureAll() captureAll()
{ {
if (!this.fCaptured) { if (!this.fCaptured) {
for (var i = 0; i < this.aInput.length; i++) { for (var i = 0; i < this.aScreens.length; i++) {
if (this.captureMouse(this.aInput[i])) this.fCaptured = true; if (this.captureMouse(this.aScreens[i])) this.fCaptured = true;
} }
} }
} }
@ -336,8 +336,8 @@ class Mouse extends Component {
releaseAll() releaseAll()
{ {
if (this.fCaptured) { if (this.fCaptured) {
for (var i = 0; i < this.aInput.length; i++) { for (var i = 0; i < this.aScreens.length; i++) {
if (this.releaseMouse(this.aInput[i])) this.fCaptured = false; if (this.releaseMouse(this.aScreens[i])) this.fCaptured = false;
} }
} }
} }
@ -425,7 +425,7 @@ class Mouse extends Component {
if (fDown !== undefined) { if (fDown !== undefined) {
if (this.fLocked === false) { if (this.fLocked === false) {
/* /*
* If there's no support for automatic pointer locking in the Video component, then notifyPointerActive() * If there's no support for automatic pointer locking in the Video component, notifyPointerActive()
* will return false, and we will set fLocked to null, ensuring that we never attempt this again. * will return false, and we will set fLocked to null, ensuring that we never attempt this again.
*/ */
if (!this.aVideo.length || !this.aVideo[0].notifyPointerActive(true)) { if (!this.aVideo.length || !this.aVideo[0].notifyPointerActive(true)) {

View file

@ -44,6 +44,7 @@ if (NODE) {
/* /*
* MDA/CGA Support * MDA/CGA Support
* ---------------
* *
* Since there's a lot of similarity between the MDA and CGA (eg, their text-mode video buffer * Since there's a lot of similarity between the MDA and CGA (eg, their text-mode video buffer
* format, and their use of the 6845 CRT controller), since the MDA ROM contains the fonts used * format, and their use of the 6845 CRT controller), since the MDA ROM contains the fonts used
@ -81,10 +82,9 @@ if (NODE) {
* TODO: Whenever there are borders, they should be filled with the CGA's overscan colors. However, * TODO: Whenever there are borders, they should be filled with the CGA's overscan colors. However,
* in the case of graphics modes (and text modes whenever font scaling is enabled), we don't reserve * in the case of graphics modes (and text modes whenever font scaling is enabled), we don't reserve
* any space for borders, so if borders are important, explicit border support will be required. * any space for borders, so if borders are important, explicit border support will be required.
*/ *
/*
* EGA Support * EGA Support
* -----------
* *
* EGA support piggy-backs on the existing MDA/CGA support. All the existing MDA/CGA port handlers * EGA support piggy-backs on the existing MDA/CGA support. All the existing MDA/CGA port handlers
* now refer to either cardMono or cardColor (instead of directly to cardMDA or cardCGA), enabling * now refer to either cardMono or cardColor (instead of directly to cardMDA or cardCGA), enabling
@ -103,10 +103,9 @@ if (NODE) {
* control certain assumptions about the virtual display's capabilities (ie, Color Display vs. Enhanced * control certain assumptions about the virtual display's capabilities (ie, Color Display vs. Enhanced
* Color Display). P3 can switch all the I/O ports from 0x3nn to 0x2nn; the default is 0x3nn, and * Color Display). P3 can switch all the I/O ports from 0x3nn to 0x2nn; the default is 0x3nn, and
* that's the only port range the EGA ROM supports as well. * that's the only port range the EGA ROM supports as well.
*/ *
/*
* VGA Support * VGA Support
* -----------
* *
* More will be said here about PCjs VGA support later. But first, a word from IBM: "Video Graphics Array [VGA] * More will be said here about PCjs VGA support later. But first, a word from IBM: "Video Graphics Array [VGA]
* Programming Considerations": * Programming Considerations":
@ -2166,6 +2165,7 @@ class Video extends Component {
* fontROM: path to .rom file (or a JSON representation) containing the character set * fontROM: path to .rom file (or a JSON representation) containing the character set
* touchScreen: string specifying desired touch-screen support (default is none) * touchScreen: string specifying desired touch-screen support (default is none)
* autoLock: true to (attempt to) auto-lock the mouse to the canvas (default is false) * autoLock: true to (attempt to) auto-lock the mouse to the canvas (default is false)
* randomize: 1 enables screen randomization, 0 disables (default is 1)
* *
* An EGA/VGA may specify the following additional properties: * An EGA/VGA may specify the following additional properties:
* *
@ -2220,6 +2220,8 @@ class Video extends Component {
this.nCard = aModelDefaults[0]; this.nCard = aModelDefaults[0];
this.cbMemory = parmsVideo['memory'] || 0; // zero means fallback to the cardSpec's default size this.cbMemory = parmsVideo['memory'] || 0; // zero means fallback to the cardSpec's default size
this.sSwitches = parmsVideo['switches']; this.sSwitches = parmsVideo['switches'];
this.nRandomize = parmsVideo['randomize'];
if (this.nRandomize == null) this.nRandomize = 1;
/* /*
* powerUp() uses the default mode ONLY if ChipSet doesn't give us a default. * powerUp() uses the default mode ONLY if ChipSet doesn't give us a default.
@ -2418,6 +2420,9 @@ class Video extends Component {
this.cpu = cpu; this.cpu = cpu;
this.dbg = dbg; this.dbg = dbg;
var nRandomize = +cmp.getMachineParm('randomize');
if (nRandomize >= 0 && nRandomize <= 1) this.nRandomize = nRandomize;
/* /*
* nCard will be undefined if no model was explicitly set (whereas this.nCard is ALWAYS defined). * nCard will be undefined if no model was explicitly set (whereas this.nCard is ALWAYS defined).
*/ */
@ -2588,15 +2593,15 @@ class Video extends Component {
} }
/** /**
* getInput() * getScreen()
* *
* This is an interface used by the Mouse component, so that it can invoke capture/release mouse events from the screen element. * This is an interface used by the Mouse component, so that it can capture mouse events from the screen.
* *
* @this {Video} * @this {Video}
* @param {Mouse} [mouse] * @param {Mouse} [mouse]
* @return {Object|undefined} * @return {Object|undefined}
*/ */
getInput(mouse) getScreen(mouse)
{ {
this.mouse = mouse; this.mouse = mouse;
return this.inputScreen; return this.inputScreen;
@ -3127,7 +3132,6 @@ class Video extends Component {
*/ */
reset() reset()
{ {
var fRandomize = true;
var nMonitorType = ChipSet.MONITOR.NONE; var nMonitorType = ChipSet.MONITOR.NONE;
/* /*
@ -3176,7 +3180,6 @@ class Video extends Component {
if (this.nMonitorType !== nMonitorType) { if (this.nMonitorType !== nMonitorType) {
this.nMonitorType = nMonitorType; this.nMonitorType = nMonitorType;
fRandomize = true;
} }
this.cardActive = null; this.cardActive = null;
@ -3199,7 +3202,7 @@ class Video extends Component {
this.nMode = null; this.nMode = null;
this.setMode(this.nModeDefault); this.setMode(this.nModeDefault);
if (this.cardActive.addrBuffer && fRandomize) { if (this.cardActive.addrBuffer && this.nRandomize) {
/* /*
* On the initial power-on, we initialize the video buffer to random characters, as a way of testing * On the initial power-on, we initialize the video buffer to random characters, as a way of testing
* whether our font(s) were successfully loaded. It's assumed that our default display mode is a text mode, * whether our font(s) were successfully loaded. It's assumed that our default display mode is a text mode,