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
*
* 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
* defaults to '1' (ie, resume enabled without prompting).
*
@ -147,6 +149,8 @@ class Computer extends Component {
this.setMachineParms(parmsMachine);
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
@ -218,6 +222,7 @@ class Computer extends Component {
this.printComputer = this.panel.print;
this.printlnComputer = this.panel.println;
}
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
component.notice = function noticeComputer(s, fPrintOnly, id) {
@ -232,8 +237,11 @@ class Computer extends Component {
return cmp.printlnComputer.call(this, s, type, id);
}.bind(component);
}
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);
@ -351,19 +359,21 @@ class Computer extends Component {
*/
enableDiagnostics()
{
for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i];
if (video) {
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
* to use it as a diagnostic display, we must temporarily set both those styles to "1".
*/
control.style.opacity = "1";
control.style.lineHeight = "1";
this.cDiagnosticScreens++;
if (!this.cDiagnosticScreens) {
for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i];
if (video) {
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
* to use it as a diagnostic display, we must temporarily set both those styles to "1".
*/
control.style.opacity = "1";
control.style.lineHeight = "1";
this.cDiagnosticScreens++;
}
}
}
}
@ -373,33 +383,43 @@ class Computer extends Component {
* disableDiagnostics()
*
* @this {Computer}
* @return {boolean} (true if diagnostics were, or already are, disabled; false if they remain disabled)
*/
disableDiagnostics()
{
for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i];
if (video) {
var control = video.getTextArea();
if (control) {
var agent = Web.getUserAgent();
/*
* Return the Video textarea overlay's opacity and lineHeight styles to their original values.
*/
control.style.opacity = "0";
control.style.lineHeight = "0";
/*
* 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,
* 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 = "";
if (this.cDiagnosticScreens) {
if (this.nDiagnostics == 2) {
this.nDiagnostics++;
this.println("Press any key to continue...");
return false;
}
for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i];
if (video) {
var control = video.getTextArea();
if (control) {
var agent = Web.getUserAgent();
/*
* Return the Video textarea overlay's opacity and lineHeight styles to their original values.
*/
control.style.opacity = "0";
control.style.lineHeight = "0";
/*
* 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,
* 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)
{
if (!this.cDiagnosticScreens) return;
for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i];
if (video) {
var control = video.getTextArea();
if (control) {
if (sType != Component.TYPE.PROGRESS || sMessage.slice(-3) != "...") {
Component.appendControl(control, sMessage + '\n');
} else {
Component.replaceControl(control, sMessage, sMessage + '.');
if (this.cDiagnosticScreens) {
for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i];
if (video) {
var control = video.getTextArea();
if (control) {
if (sType != Component.TYPE.PROGRESS || sMessage.slice(-3) != "...") {
Component.appendControl(control, sMessage + '\n');
} else {
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()
*
@ -859,23 +896,28 @@ class Computer extends Component {
*/
donePowerOn(aParms)
{
var stateComputer = aParms[0];
var fRepower = (aParms[1] < 0);
var fRestore = aParms[2];
if (!this.flags.initDone) {
if (!this.disableDiagnostics()) {
this.setReady(false);
this.wait(this.donePowerOn, aParms);
return;
}
this.flags.initDone = true;
}
if (DEBUG && this.flags.powered && this.messageEnabled()) {
this.printMessage("Computer.donePowerOn(): redundant");
}
if (!this.flags.initDone) {
this.disableDiagnostics();
this.flags.initDone = true;
}
var stateComputer = aParms[0];
var fRepower = (aParms[1] < 0);
var fRestore = aParms[2];
this.flags.powered = true;
var controlPower = this.bindings["power"];
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;
* the CPU should begin executing immediately, unless a debugger is attached.

View file

@ -571,7 +571,9 @@ class CPU extends Component {
calcCycles()
{
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.nCyclesPerYield = Math.floor(this.counts.nBaseCyclesPerSecond / CPU.YIELDS_PER_SECOND * 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.cmp.notifyKbdEvent(event);
if (this.fAllDown) {
var simCode = this.checkActiveKey();
if (simCode && this.isAlphaKey(simCode) && this.isAlphaKey(keyCode) && simCode != keyCode) {

View file

@ -83,11 +83,11 @@ class Mouse extends Component {
this.setActive(false);
this.fCaptured = this.fLocked = false;
/*
* Initially, no video devices, and therefore no input devices, are attached. initBus() will update aVideo,
* and powerUp() will update aInput.
* Initially, no video devices, and therefore no screens, are attached. initBus() will update aVideo,
* and powerUp() will update aScreens.
*/
this.aVideo = [];
this.aInput = [];
this.aScreens = [];
this.setReady();
}
@ -183,10 +183,10 @@ class Mouse extends Component {
}
}
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++) {
var input = this.aVideo[i].getInput(this);
if (input) this.aInput.push(input);
var screen = this.aVideo[i].getScreen(this);
if (screen) this.aScreens.push(screen);
}
} else {
Component.warning(this.id + ": " + this.sAdapterType + " " + this.idAdapter + " unavailable");
@ -322,8 +322,8 @@ class Mouse extends Component {
captureAll()
{
if (!this.fCaptured) {
for (var i = 0; i < this.aInput.length; i++) {
if (this.captureMouse(this.aInput[i])) this.fCaptured = true;
for (var i = 0; i < this.aScreens.length; i++) {
if (this.captureMouse(this.aScreens[i])) this.fCaptured = true;
}
}
}
@ -336,8 +336,8 @@ class Mouse extends Component {
releaseAll()
{
if (this.fCaptured) {
for (var i = 0; i < this.aInput.length; i++) {
if (this.releaseMouse(this.aInput[i])) this.fCaptured = false;
for (var i = 0; i < this.aScreens.length; i++) {
if (this.releaseMouse(this.aScreens[i])) this.fCaptured = false;
}
}
}
@ -425,7 +425,7 @@ class Mouse extends Component {
if (fDown !== undefined) {
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.
*/
if (!this.aVideo.length || !this.aVideo[0].notifyPointerActive(true)) {

View file

@ -44,6 +44,7 @@ if (NODE) {
/*
* MDA/CGA Support
* ---------------
*
* 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
@ -81,10 +82,9 @@ if (NODE) {
* 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
* any space for borders, so if borders are important, explicit border support will be required.
*/
/*
*
* EGA Support
* -----------
*
* 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
@ -103,10 +103,9 @@ if (NODE) {
* 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
* that's the only port range the EGA ROM supports as well.
*/
/*
*
* VGA Support
* -----------
*
* More will be said here about PCjs VGA support later. But first, a word from IBM: "Video Graphics Array [VGA]
* Programming Considerations":
@ -2166,6 +2165,7 @@ class Video extends Component {
* fontROM: path to .rom file (or a JSON representation) containing the character set
* 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)
* randomize: 1 enables screen randomization, 0 disables (default is 1)
*
* An EGA/VGA may specify the following additional properties:
*
@ -2220,6 +2220,8 @@ class Video extends Component {
this.nCard = aModelDefaults[0];
this.cbMemory = parmsVideo['memory'] || 0; // zero means fallback to the cardSpec's default size
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.
@ -2418,6 +2420,9 @@ class Video extends Component {
this.cpu = cpu;
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).
*/
@ -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}
* @param {Mouse} [mouse]
* @return {Object|undefined}
*/
getInput(mouse)
getScreen(mouse)
{
this.mouse = mouse;
return this.inputScreen;
@ -3127,7 +3132,6 @@ class Video extends Component {
*/
reset()
{
var fRandomize = true;
var nMonitorType = ChipSet.MONITOR.NONE;
/*
@ -3176,7 +3180,6 @@ class Video extends Component {
if (this.nMonitorType !== nMonitorType) {
this.nMonitorType = nMonitorType;
fRandomize = true;
}
this.cardActive = null;
@ -3199,7 +3202,7 @@ class Video extends Component {
this.nMode = null;
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
* whether our font(s) were successfully loaded. It's assumed that our default display mode is a text mode,