mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-02-27 09:28:53 +00:00
1. Implement new method to open pop-up windows that schedules pop-up opens through a queue, delaying between opens, and retrying with longer delays when the opens fail. This is to satisfy new restrictions on pop-ups introduced in Apple Safari 11.0, even when the browser is configured to enable pop-ups. 2. Remove the Application Cache facility. This API has been deprecated and will be removed from browsers. 3. Implement improved setCallback() asynchronous timing mechanism with a new Promise-based approach developed for the retro-220 emulator. 4. Correct Processor memory access logic to avoid causing an Invalid Address interrupt in Control State. 5. Correct Processor Exit Character Mode (XX00) by implementing it separately from Word Mode exits. 6. Implement separate jump mechanisms for Word and Character Mode to correct edge-case errors. 7. Make slight correction to clock counting in singlePrecisionAdd. 8. Fix bug in double-clicking the "stacker" of the card punch to extract its contents. 9. Disable I/O descriptor word count in IOUnit for card reader/punch operations; remove extraneous blank card between card-load programs from the COLDSTART-XIII.card and COOLSTART-XIII.card decks that was previously required to prevent "eating" the second program's boot card. 10. Remove extraneous whitespace from B5500FramePaper used for most text-only windows and frames. 11. Compute HPT disk latency based on current timestamp rather than a random number. 12. Correct top-of-form handling in B5500LinePrinter. 13. Correct annimation of tape reel in B5500MagTapeDrive. 14. Replace the emulator's custom bindMethod() utility routine with Function.bind(). 15. Replace the emulator's custom DOM className utility routines by DOM classList methods. 16. Add P1 S and F register values to the internal tape dump caption; improve the dump tape's label records. 17. Give the annunciators on the ConsolePanel a dark gray presence when they are not lit. 18. Correct the method to "focus" the ConsolePanel window after the SPO initializes and becomes ready. 19. Clean up line delimiters in source and make consistent for Windows systems.
879 lines
32 KiB
JavaScript
879 lines
32 KiB
JavaScript
/***********************************************************************
|
|
* retro-b5500/emulator B5500ConsolePanel.js
|
|
************************************************************************
|
|
* Copyright (c) 2015, Nigel Williams and Paul Kimpel.
|
|
* Licensed under the MIT License, see
|
|
* http://www.opensource.org/licenses/mit-license.php
|
|
************************************************************************
|
|
* B5500 Operations Console Panel Javascript module.
|
|
*
|
|
* Implements event handlers and control functions for the B5500 emulator
|
|
* operations console.
|
|
*
|
|
************************************************************************
|
|
* 2015-01-24 P.Kimpel
|
|
* Original version, split off from B5500Console.js.
|
|
***********************************************************************/
|
|
"use strict";
|
|
|
|
/**************************************/
|
|
function B5500ConsolePanel(global, autoPowerUp, shutDown) {
|
|
/* Constructor for the Console Panel object. "global" must be the
|
|
global window object; "autoPowerUp" indicates whether the system should
|
|
be powered on automatically; "shutDown" is a function to be called back
|
|
when the window closes */
|
|
var height = 144;
|
|
var width = 1133;
|
|
var left = screen.availWidth - width;
|
|
|
|
this.autoPowerUp = autoPowerUp; // Automatically power on during onload event
|
|
this.aControl; // A-Control button/light
|
|
this.aNormal; // A-Normal button/light
|
|
this.bControl; // B-Control button/light
|
|
this.bNormal; // B-Normal button/light
|
|
this.cc; // B5500CentralControl object
|
|
this.ccLatches = [0, 0, 0]; // I/O- & interrupt-reporting latches
|
|
this.ccLightsMap = new Array(6); // Misc annunciator DOM objects
|
|
this.global = global; // Global window object
|
|
this.intLightsMap = new Array(48); // Interrupt annunciator DOM objects
|
|
this.lastInterruptMask = 0; // Prior mask of interrupt annunciator lights
|
|
this.lastCCMask = 0; // Prior mask of misc annunciator lights
|
|
this.lastUnitBusyMask = 0; // Prior mask of unit-busy annunciator lights
|
|
this.lastPANormalRate = -1; // Prior PA normal-state busy rate
|
|
this.lastPAControlRate = -1; // Prior PA control-state busy rate
|
|
this.lastPBNormalRate = -1; // Prior PB normal-state busy rate
|
|
this.lastPBControlRate = -1; // Prior PB normal-state busy rate
|
|
this.perf = performance; // (it's faster if locally cached)
|
|
this.perLightsMap = new Array(48); // Peripheral I/O annunciator DOM objects
|
|
this.procDelay; // Current average P1 delay [ms]
|
|
this.procSlack; // Current average P1 slack time [%]
|
|
this.showAnnunciators = true; // Display non-purist console mode (annunciators)
|
|
this.shutDown = shutDown; // Function to be called back when the panel closes
|
|
this.statusLabelTimer = 0; // Status label display timer control token
|
|
this.timer = 0; // Console display update timer control token
|
|
this.timerInterval = 50; // Console display update interval [ms]
|
|
|
|
this.doc = null;
|
|
B5500Util.openPopup(window, "../webUI/B5500ConsolePanel.html", "B5500Console",
|
|
"location=no,scrollbars=no,resizable,top=0,left=" + left +
|
|
",width=" + width + ",height=" + height,
|
|
this, B5500ConsolePanel.prototype.consoleOnload);
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.annOnColor = "white"; // annunciator lamp on color
|
|
B5500ConsolePanel.annOffColor = "#333"; // annunciator lamp off color
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.$$ = function $$(id) {
|
|
return this.doc.getElementById(id);
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.setAnnunciators = function setAnnunciators(showEm) {
|
|
/* Sets the visibility of the annunciators based on "showEm" */
|
|
|
|
this.$$("CentralControl").style.display = (showEm && this.cc.poweredUp ? "block" : "none");
|
|
this.$$("RetroVersion").style.visibility = (showEm ? "visible" : "hidden");
|
|
this.$$("RetroLogoImage").style.display = (showEm ? "inline" : "none");
|
|
this.$$("B5500LogoImage").style.display = (showEm ? "none" : "inline");
|
|
this.$$("ConfigLabel").style.display = (showEm ? "inline" : "none");
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.evaluateNotReady = function evaluateNotReady(config) {
|
|
/* Evaluates the system configuration to determine whether the
|
|
NOT READY lamp should be illuminated */
|
|
var lampClass = "whiteButton";
|
|
|
|
switch (false) {
|
|
case config.PA.enabled || config.PA.enabled:
|
|
case (config.PA.enabled && !config.PB1L) || (config.PB.enabled && config.PB1L):
|
|
case config.IO1.enabled || config.IO2.enabled || config.IO3.enabled || config.IO4.enabled:
|
|
case config.memMod[0].enabled:
|
|
case config.units.SPO.enabled:
|
|
case config.units.DKA.enabled:
|
|
lampClass += " whiteLit";
|
|
}
|
|
|
|
this.$$("NotReadyBtn").className = lampClass;
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.focusConsole = function focusConsole() {
|
|
/* Globally-accessible function to focus the console panel window */
|
|
|
|
this.window.focus();
|
|
this.$$("LoadBtn").focus();
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.BurroughsLogo_Click = function BurroughsLogo_Click(ev) {
|
|
/* Toggles the annunciator display state on the panel */
|
|
|
|
this.showAnnunciators = !this.showAnnunciators;
|
|
this.setAnnunciators(this.showAnnunciators);
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.B5500Logo_Click = function B5500Logo_Click(ev) {
|
|
/* Opens the configuration window if the system is powered off */
|
|
var sysConfig = new B5500SystemConfig();
|
|
|
|
if (this.cc.poweredUp) {
|
|
this.$$("StatusLabel").textContent = "System configuration changes are not allowed while power is on.";
|
|
this.clearStatusLabel(15);
|
|
} else {
|
|
this.$$("ConfigLabel").style.display = "none";
|
|
sysConfig.openConfigUI();
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.PowerOnBtn_Click = function PowerOnBtn_Click(ev) {
|
|
/* Powers on the system */
|
|
var sysConfig = new B5500SystemConfig();
|
|
var that = this;
|
|
|
|
function applyPower(config) {
|
|
that.$$("HaltBtn").className = "redButton redLit";
|
|
that.$$("PowerOnBtn").disabled = true;
|
|
that.$$("PowerOffBtn").disabled = false;
|
|
that.$$("LoadSelectBtn").disabled = false;
|
|
that.$$("LoadBtn").disabled = false;
|
|
that.$$("HaltBtn").disabled = true;
|
|
that.$$("MemoryCheckBtn").disabled = false;
|
|
that.cc.powerOn(config);
|
|
that.$$("LoadSelectBtn").className = "yellowButton" + (that.cc.cardLoadSelect ? " yellowLit" : "");
|
|
that.evaluateNotReady(config);
|
|
that.setAnnunciators(that.showAnnunciators);
|
|
that.window.addEventListener("beforeunload", that.beforeUnload);
|
|
}
|
|
|
|
function youMayPowerOnWhenReady_Gridley(config) {
|
|
/* Called-back by sysConfig.getSystemConfig with the requested configuration */
|
|
|
|
if (!config) {
|
|
that.window.alert("No System Configuration found\nCANNOT POWER ON.");
|
|
} else {
|
|
that.$$("PowerOnBtn").className = "greenButton greenLit";
|
|
that.$$("SysConfigName").textContent = config.configName;
|
|
that.$$("StorageName").textContent = config.units.DKA.storageName;
|
|
if (that.showAnnunciators) {
|
|
that.lampTest(applyPower.bind(that), config);
|
|
} else {
|
|
applyPower(config);
|
|
}
|
|
}
|
|
}
|
|
|
|
sysConfig.getSystemConfig(null, youMayPowerOnWhenReady_Gridley); // get current system config
|
|
return true;
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.PowerOffBtn_Click = function PowerOffBtn_Click(ev) {
|
|
/* Powers off the system, halting it first if necessary */
|
|
|
|
this.$$("PowerOnBtn").className = "greenButton";
|
|
this.$$("ANormalBtn").className = "yellowButton";
|
|
this.$$("AControlBtn").className = "yellowButton";
|
|
this.$$("BNormalBtn").className = "yellowButton";
|
|
this.$$("BControlBtn").className = "yellowButton";
|
|
this.$$("LoadSelectBtn").className = "yellowButton";
|
|
this.$$("MemoryCheckBtn").className = "redButton";
|
|
this.$$("NotReadyBtn").className = "whiteButton";
|
|
this.$$("HaltBtn").className = "redButton";
|
|
this.cc.powerOff();
|
|
this.$$("PowerOnBtn").disabled = false;
|
|
this.$$("PowerOffBtn").disabled = true;
|
|
this.$$("LoadSelectBtn").disabled = true;
|
|
this.$$("LoadBtn").disabled = true;
|
|
this.$$("HaltBtn").disabled = true;
|
|
this.$$("MemoryCheckBtn").disabled = true;
|
|
this.$$("CentralControl").style.display = "none";
|
|
this.window.removeEventListener("beforeunload", this.beforeUnload);
|
|
if (this.timer) {
|
|
clearInterval(this.timer);
|
|
this.timer = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.HaltBtn_Click = function HaltBtn_Click(ev) {
|
|
/* Halts the system */
|
|
|
|
this.$$("HaltBtn").className = "redButton redLit";
|
|
this.cc.halt();
|
|
this.$$("HaltBtn").disabled = true;
|
|
this.$$("LoadBtn").disabled = false;
|
|
this.dasBlinkenlichten();
|
|
if (this.timer) {
|
|
clearInterval(this.timer);
|
|
this.timer = 0;
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.LoadBtn_Click = function LoadBtn_Click(ev) {
|
|
/* Initiates a program load for the system */
|
|
var result;
|
|
|
|
window.open("", "SPO").focus(); // re-focus the SPO window
|
|
result = this.cc.load(false);
|
|
switch (result) {
|
|
case 0: // load initiated successfully
|
|
this.$$("HaltBtn").className = "redButton";
|
|
this.$$("HaltBtn").disabled = false;
|
|
this.$$("LoadBtn").disabled = true;
|
|
this.timer = setInterval(
|
|
B5500ConsolePanel.prototype.dasBlinkenlichten.bind(this),
|
|
this.timerInterval);
|
|
break;
|
|
case 1:
|
|
this.window.alert("P1 busy or not available");
|
|
break;
|
|
case 2:
|
|
this.window.alert("SPO is not ready");
|
|
break;
|
|
case 3:
|
|
this.window.alert("SPO is busy");
|
|
break;
|
|
case 4:
|
|
this.window.alert("DKA is not ready");
|
|
break;
|
|
case 5:
|
|
this.window.alert("DKA is busy");
|
|
break;
|
|
default:
|
|
this.window.alert("cc.load() result = " + result);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.LoadSelectBtn_Click = function LoadSelectBtn_Click(ev) {
|
|
/* Toggles the Card Load Select button state */
|
|
|
|
if (this.cc.cardLoadSelect) {
|
|
this.cc.cardLoadSelect = 0;
|
|
this.$$("LoadSelectBtn").className = "yellowButton";
|
|
} else {
|
|
this.cc.cardLoadSelect = 1;
|
|
this.$$("LoadSelectBtn").className = "yellowButton yellowLit";
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.dumpState = function dumpState(caption) {
|
|
/* Generates a dump of the processor states and all of memory */
|
|
var doc = null;
|
|
var lastPhase = -2;
|
|
var win = null;
|
|
var x = 0;
|
|
|
|
var htmlMatch = /[<>&"]/g; // regular expression for escaping HTML text
|
|
|
|
function htmlFilter(c) {
|
|
/* Used to escape HTML-sensitive characters in a string */
|
|
switch (c) {
|
|
case "&":
|
|
return "&";
|
|
case "<":
|
|
return "<";
|
|
case ">":
|
|
return ">";
|
|
case "\"":
|
|
return """;
|
|
default:
|
|
return c;
|
|
}
|
|
}
|
|
|
|
function escapeHTML(text) {
|
|
/* Returns "text" as escaped HTML */
|
|
|
|
return text.replace(htmlMatch, htmlFilter);
|
|
}
|
|
|
|
function writer(phase, text) {
|
|
/* Call-back function for cc.dumpSystemState */
|
|
|
|
switch (phase) {
|
|
case 0: // Initialization and heading line
|
|
lastPhase = phase;
|
|
doc.writeln(escapeHTML(text));
|
|
doc.writeln("User Agent: " + navigator.userAgent);
|
|
break;
|
|
|
|
case 1: // Processor 1 state
|
|
case 2: // Processor 2 state
|
|
if (phase == lastPhase) {
|
|
doc.writeln(escapeHTML(text));
|
|
} else {
|
|
lastPhase = phase;
|
|
doc.writeln();
|
|
doc.writeln(escapeHTML(text));
|
|
doc.writeln();
|
|
}
|
|
break;
|
|
|
|
case 32: // Memory lines
|
|
if (phase != lastPhase) {
|
|
lastPhase = phase;
|
|
doc.writeln();
|
|
}
|
|
doc.writeln();
|
|
doc.writeln(escapeHTML(text));
|
|
break;
|
|
|
|
case -1: // Termination
|
|
break;
|
|
} // switch
|
|
}
|
|
|
|
function dumpStateOnLoad(ev) {
|
|
/* Call-back function for the dumpState onload event */
|
|
|
|
doc = ev.target;
|
|
win = doc.defaultView;
|
|
doc.open();
|
|
doc.writeln("<html><head><title>retro-B5500 Console State Dump</title>");
|
|
doc.writeln("</head><body>");
|
|
doc.write("<pre>");
|
|
|
|
this.cc.dumpSystemState(caption, writer);
|
|
|
|
doc.writeln("</pre></body></html>")
|
|
doc.close();
|
|
win.focus();
|
|
}
|
|
|
|
// Outer block of dumpState
|
|
B5500Util.openPopup(this.window, "./B5500FramePaper.html", "",
|
|
"location=no,resizable,scrollbars,status",
|
|
this, dumpStateOnLoad);
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.dumpTape = function dumpTape(caption) {
|
|
/* Generates a dump of all of memory to a MEMORY/DUMP tape image */
|
|
var doc = null;
|
|
var win = null;
|
|
var x = 0;
|
|
|
|
var htmlMatch = /[<>&"]/g; // regular expression for escaping HTML text
|
|
var tapeLabel = " LABEL 000000000MDUMP 001750010199365000006400000640000000051300513000002900|4";
|
|
|
|
function htmlFilter(c) {
|
|
/* Used to escape HTML-sensitive characters in a string */
|
|
switch (c) {
|
|
case "&":
|
|
return "&";
|
|
case "<":
|
|
return "<";
|
|
case ">":
|
|
return ">";
|
|
case "\"":
|
|
return """;
|
|
default:
|
|
return c;
|
|
}
|
|
}
|
|
|
|
function escapeHTML(text) {
|
|
/* Returns "text" as escaped HTML */
|
|
|
|
return text.replace(htmlMatch, htmlFilter);
|
|
}
|
|
|
|
function writer(phase, text) {
|
|
/* Call-back function for cc.dumpSystemTape */
|
|
|
|
switch (phase) {
|
|
case 0: // Initialization, write tape label
|
|
doc.writeln(tapeLabel);
|
|
doc.writeln("}"); // tape mark
|
|
break;
|
|
|
|
case 32: // Dump data
|
|
doc.writeln(escapeHTML(text));
|
|
break;
|
|
|
|
case -1: // Termination, write tape label
|
|
doc.writeln(text.substring(0, 160));
|
|
doc.writeln("}"); // tape mark
|
|
doc.writeln(tapeLabel);
|
|
break;
|
|
} // switch
|
|
}
|
|
|
|
function dumpTapeOnLoad(ev) {
|
|
/* Call-back function for the dumpTape window onload event */
|
|
|
|
doc = ev.target;
|
|
win = doc.defaultView;
|
|
doc.open();
|
|
doc.writeln("<html><head><title>retro-B5500 Console Tape Dump</title>");
|
|
doc.writeln("</head><body>");
|
|
doc.write("<pre>");
|
|
|
|
this.cc.dumpSystemTape(caption, writer);
|
|
|
|
doc.writeln("</pre></body></html>")
|
|
doc.close();
|
|
win.focus();
|
|
}
|
|
|
|
// Outer block of dumpTape
|
|
B5500Util.openPopup(this.window, "./B5500FramePaper.html", "",
|
|
"location=no,resizable,scrollbars,status",
|
|
this, dumpTapeOnLoad);
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.displayCallbackState = function displayCallbackState() {
|
|
/* Builds a table of outstanding callback state */
|
|
var cb;
|
|
var cbs;
|
|
var e;
|
|
var body = document.createElement("tbody");
|
|
var oldBody = document.getElementById("CallbackBody");
|
|
var row;
|
|
var state = getCallbackState(0x03);
|
|
var token;
|
|
|
|
cbs = state.delayDev;
|
|
for (token in cbs) {
|
|
row = document.createElement("tr");
|
|
|
|
e = document.createElement("td");
|
|
e.appendChild(document.createTextNode(token));
|
|
row.appendChild(e);
|
|
|
|
e = document.createElement("td");
|
|
e.appendChild(document.createTextNode((cbs[token]||0).toFixed(2)));
|
|
row.appendChild(e);
|
|
|
|
e = document.createElement("td");
|
|
e.colSpan = 2;
|
|
row.appendChild(e);
|
|
body.appendChild(row);
|
|
}
|
|
|
|
cbs = state.pendingCallbacks;
|
|
for (token in cbs) {
|
|
cb = cbs[token];
|
|
row = document.createElement("tr");
|
|
|
|
e = document.createElement("td");
|
|
e.appendChild(document.createTextNode(token.toString()));
|
|
row.appendChild(e);
|
|
|
|
e = document.createElement("td");
|
|
e.appendChild(document.createTextNode(cb.delay.toFixed(2)));
|
|
row.appendChild(e);
|
|
|
|
e = document.createElement("td");
|
|
e.appendChild(document.createTextNode((cb.context && cb.context.mnemonic) || "??"));
|
|
row.appendChild(e);
|
|
|
|
e = document.createElement("td");
|
|
e.appendChild(document.createTextNode((cb.args ? cb.args.length : 0).toString()));
|
|
row.appendChild(e);
|
|
body.appendChild(row);
|
|
}
|
|
|
|
body.id = oldBody.id;
|
|
oldBody.parentNode.replaceChild(body, oldBody);
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.displayCentralControl = function displayCentralControl() {
|
|
/* Displays the I/O and interrupt status in CentralControl */
|
|
var cells;
|
|
var s;
|
|
var interruptMask;
|
|
var interruptChange;
|
|
var ccMask;
|
|
var ccChange;
|
|
var unitBusyMask;
|
|
var unitBusyChange;
|
|
var x;
|
|
|
|
this.cc.fetchCCLatches(this.ccLatches);
|
|
ccMask = this.ccLatches[0];
|
|
ccChange = this.lastCCMask ^ ccMask;
|
|
this.lastCCMask = ccMask;
|
|
|
|
interruptMask = this.ccLatches[1] % 0x4000;
|
|
interruptChange = this.lastInterruptMask ^ interruptMask;
|
|
this.lastInterruptMask = interruptMask;
|
|
|
|
unitBusyMask = this.ccLatches[2];
|
|
unitBusyChange = this.lastUnitBusyMask ^ unitBusyMask;
|
|
this.lastUnitBusyMask = unitBusyMask;
|
|
|
|
x = 0;
|
|
while (ccChange) {
|
|
if (ccChange & 0x01) {
|
|
if (this.ccLightsMap[x]) {
|
|
this.ccLightsMap[x].style.color = (ccMask & 0x01 ? B5500ConsolePanel.annOnColor : B5500ConsolePanel.annOffColor);
|
|
}
|
|
}
|
|
ccMask >>>= 1;
|
|
ccChange >>>= 1;
|
|
x++;
|
|
}
|
|
|
|
x = 47;
|
|
while (interruptChange) {
|
|
if (interruptChange & 0x01) {
|
|
if (this.intLightsMap[x]) {
|
|
this.intLightsMap[x].style.color = (interruptMask & 0x01 ? B5500ConsolePanel.annOnColor : B5500ConsolePanel.annOffColor);
|
|
}
|
|
}
|
|
interruptMask >>>= 1;
|
|
interruptChange >>>= 1;
|
|
x--;
|
|
}
|
|
|
|
x = 47;
|
|
while (unitBusyChange) {
|
|
if (unitBusyChange & 0x01) {
|
|
if (this.perLightsMap[x]) {
|
|
this.perLightsMap[x].style.color = (unitBusyMask & 0x01 ? B5500ConsolePanel.annOnColor : B5500ConsolePanel.annOffColor);
|
|
}
|
|
}
|
|
unitBusyMask >>>= 1;
|
|
unitBusyChange >>>= 1;
|
|
x--;
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.dasBlinkenlichten = function dasBlinkenlichten() {
|
|
/* Updates the panel display from current system state */
|
|
var cycles = 0;
|
|
var pa = this.cc.PA;
|
|
var pb = this.cc.PB;
|
|
var p1 = this.cc.P1;
|
|
var stateRate = 0;
|
|
|
|
cycles = p1.normalCycles+p1.controlCycles+1; // avoid div zero
|
|
|
|
if (pa) {
|
|
if (pa.normalCycles+pa.controlCycles <= 0) {
|
|
if (this.lastPAControlRate != -1) {
|
|
this.lastPAControlRate = this.lastPANormalRate = -1;
|
|
this.aNormal.className = "yellowButton";
|
|
this.aControl.className = (pa === p1 ? "yellowButton yellowLit" : "yellowButton");
|
|
}
|
|
} else {
|
|
stateRate = Math.round(pa.normalCycles/cycles*6 + 0.25);
|
|
if (stateRate != this.lastPANormalRate) {
|
|
this.lastPANormalRate = stateRate;
|
|
switch (stateRate) {
|
|
case 0:
|
|
this.aNormal.className = "yellowButton";
|
|
break;
|
|
case 1:
|
|
this.aNormal.className = "yellowButton yellowLit1";
|
|
break;
|
|
case 2:
|
|
this.aNormal.className = "yellowButton yellowLit2";
|
|
break;
|
|
case 3:
|
|
this.aNormal.className = "yellowButton yellowLit3";
|
|
break;
|
|
case 4:
|
|
this.aNormal.className = "yellowButton yellowLit4";
|
|
break;
|
|
case 5:
|
|
this.aNormal.className = "yellowButton yellowLit5";
|
|
break;
|
|
default:
|
|
this.aNormal.className = "yellowButton yellowLit";
|
|
break;
|
|
}
|
|
}
|
|
|
|
stateRate = (pa === p1 ? Math.round(pa.controlCycles/cycles*6 + 0.25) : 0);
|
|
if (stateRate != this.lastPAControlRate) {
|
|
this.lastPAControlRate = stateRate;
|
|
switch (stateRate) {
|
|
case 0:
|
|
this.aControl.className = "yellowButton";
|
|
break;
|
|
case 1:
|
|
this.aControl.className = "yellowButton yellowLit1";
|
|
break;
|
|
case 2:
|
|
this.aControl.className = "yellowButton yellowLit2";
|
|
break;
|
|
case 3:
|
|
this.aControl.className = "yellowButton yellowLit3";
|
|
break;
|
|
case 4:
|
|
this.aControl.className = "yellowButton yellowLit4";
|
|
break;
|
|
case 5:
|
|
this.aControl.className = "yellowButton yellowLit5";
|
|
break;
|
|
default:
|
|
this.aControl.className = "yellowButton yellowLit";
|
|
break;
|
|
}
|
|
}
|
|
|
|
pa.controlCycles = pa.normalCycles = 0;
|
|
}
|
|
}
|
|
|
|
if (pb) {
|
|
if (pb.normalCycles+pb.controlCycles <= 0) {
|
|
if (this.lastPBControlRate != -1) {
|
|
this.lastPBControlRate = this.lastPBNormalRate = -1;
|
|
this.bNormal.className = "yellowButton";
|
|
this.bControl.className = (pb === p1 ? "yellowButton yellowLit" : "yellowButton");
|
|
}
|
|
} else {
|
|
stateRate = Math.round(pb.normalCycles/cycles*6 + 0.25);
|
|
if (stateRate != this.lastPBNormalRate) {
|
|
this.lastPBNormalRate = stateRate;
|
|
switch (stateRate) {
|
|
case 0:
|
|
this.bNormal.className = "yellowButton";
|
|
break;
|
|
case 1:
|
|
this.bNormal.className = "yellowButton yellowLit1";
|
|
break;
|
|
case 2:
|
|
this.bNormal.className = "yellowButton yellowLit2";
|
|
break;
|
|
case 3:
|
|
this.bNormal.className = "yellowButton yellowLit3";
|
|
break;
|
|
case 4:
|
|
this.bNormal.className = "yellowButton yellowLit4";
|
|
break;
|
|
case 5:
|
|
this.bNormal.className = "yellowButton yellowLit5";
|
|
break;
|
|
default:
|
|
this.bNormal.className = "yellowButton yellowLit";
|
|
break;
|
|
}
|
|
}
|
|
|
|
stateRate = (pb === p1 ? Math.round(pb.controlCycles/cycles*6 + 0.25) : 0);
|
|
if (stateRate != this.lastPBControlRate) {
|
|
this.lastPBControlRate = stateRate;
|
|
switch (stateRate) {
|
|
case 0:
|
|
this.bControl.className = "yellowButton";
|
|
break;
|
|
case 1:
|
|
this.bControl.className = "yellowButton yellowLit1";
|
|
break;
|
|
case 2:
|
|
this.bControl.className = "yellowButton yellowLit2";
|
|
break;
|
|
case 3:
|
|
this.bControl.className = "yellowButton yellowLit3";
|
|
break;
|
|
case 4:
|
|
this.bControl.className = "yellowButton yellowLit4";
|
|
break;
|
|
case 5:
|
|
this.bControl.className = "yellowButton yellowLit5";
|
|
break;
|
|
default:
|
|
this.bControl.className = "yellowButton yellowLit";
|
|
break;
|
|
}
|
|
}
|
|
|
|
pb.controlCycles = pb.normalCycles = 0;
|
|
}
|
|
}
|
|
|
|
this.procDelay.textContent = p1.delayDeltaAvg.toFixed(1);
|
|
this.procSlack.textContent = (p1.procSlackAvg/p1.procRunAvg*100).toFixed(1);
|
|
|
|
if (this.showAnnunciators) {
|
|
this.displayCentralControl();
|
|
}
|
|
//this.displayCallbackState();
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.buildLightMaps = function buildLightMaps() {
|
|
/* Builds tables of the DOM entries for the annunciator lights, for efficient access */
|
|
var mnem;
|
|
var spec;
|
|
var x;
|
|
|
|
this.ccLightsMap[0] = this.$$("AD1F");
|
|
this.ccLightsMap[1] = this.$$("AD2F");
|
|
this.ccLightsMap[2] = this.$$("AD3F");
|
|
this.ccLightsMap[3] = this.$$("AD4F");
|
|
this.ccLightsMap[4] = this.$$("P2BF");
|
|
this.ccLightsMap[5] = this.$$("HP2F");
|
|
|
|
for (x=3; x<=16; x++) {
|
|
this.intLightsMap[50-x] = this.$$("CCI" + (x+100).toString().substring(1) + "F");
|
|
}
|
|
|
|
for (mnem in B5500CentralControl.unitSpecs) {
|
|
spec = B5500CentralControl.unitSpecs[mnem];
|
|
this.perLightsMap[spec.unitIndex] = this.$$(mnem);
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.lampTest = function lampTest(callback, callbackParam) {
|
|
/* Lights up the operator console, waits a bit, then turns everything
|
|
off and calls the "callback" function, passing "callbackParam".
|
|
The Power On lamp is not affected */
|
|
var that = this;
|
|
|
|
function switchEm(mode) {
|
|
var visibility = (mode ? B5500ConsolePanel.annOnColor : B5500ConsolePanel.annOffColor);
|
|
var x;
|
|
|
|
that.$$("ANormalBtn").className = "yellowButton" + (mode ? " yellowLit" : "");
|
|
that.$$("AControlBtn").className = "yellowButton" + (mode ? " yellowLit" : "");
|
|
that.$$("BNormalBtn").className = "yellowButton" + (mode ? " yellowLit" : "");
|
|
that.$$("BControlBtn").className = "yellowButton" + (mode ? " yellowLit" : "");
|
|
that.$$("LoadSelectBtn").className = "yellowButton" + (mode ? " yellowLit" : "");
|
|
that.$$("MemoryCheckBtn").className = "redButton" + (mode ? " redLit" : "");
|
|
that.$$("NotReadyBtn").className = "whiteButton" + (mode ? " whiteLit" : "");
|
|
that.$$("HaltBtn").className = "redButton" + (mode ? " redLit" : "");
|
|
|
|
for (x in that.ccLightsMap) {
|
|
if (that.ccLightsMap[x]) {
|
|
that.ccLightsMap[x].style.color = visibility;
|
|
}
|
|
}
|
|
|
|
for (x in that.intLightsMap) {
|
|
if (that.intLightsMap[x]) {
|
|
that.intLightsMap[x].style.color = visibility;
|
|
}
|
|
}
|
|
|
|
for (x in that.perLightsMap) {
|
|
if (that.perLightsMap[x]) {
|
|
that.perLightsMap[x].style.color = visibility;
|
|
}
|
|
}
|
|
|
|
if (!mode) {
|
|
that.setAnnunciators(that.showAnnunciators);
|
|
setCallback(null, that, 250, callback, callbackParam);
|
|
}
|
|
}
|
|
|
|
this.setAnnunciators(true);
|
|
this.$$("CentralControl").style.display = "block"; // overrides if !this.cc.poweredUp
|
|
switchEm(1);
|
|
setCallback(null, this, 2000, switchEm, 0);
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.beforeUnload = function beforeUnload(ev) {
|
|
var msg = "Closing this window will make the emulator unusable.\n" +
|
|
"Suggest you stay on the page and minimize this window instead";
|
|
|
|
ev.preventDefault();
|
|
ev.returnValue = msg;
|
|
return msg;
|
|
};
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.consoleUnload = function consoleUnload(ev) {
|
|
/* Called when the ConsolePanel window unloads or is closed */
|
|
|
|
if (this.cc && this.cc.poweredUp) {
|
|
this.cc.powerOff();
|
|
if (this.timer) {
|
|
clearInterval(this.timer);
|
|
this.timer = 0;
|
|
}
|
|
}
|
|
|
|
this.shutDown();
|
|
};
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.clearStatusLabel = function clearStatusLabel(inSeconds) {
|
|
/* Delays for "inSeconds" seconds, then clears the StatusLabel element */
|
|
|
|
if (this.statusLabelTimer) {
|
|
clearTimeout(this.statusLabelTimer);
|
|
}
|
|
|
|
this.statusLabelTimer = setCallback(null, this, inSeconds*1000, function(ev) {
|
|
this.$$("StatusLabel").textContent = "";
|
|
this.statusLabelTimer = 0;
|
|
});
|
|
}
|
|
|
|
/**************************************/
|
|
B5500ConsolePanel.prototype.consoleOnload = function consoleOnload(ev) {
|
|
/* Initialization function called when window finishes loading */
|
|
|
|
this.doc = ev.target;
|
|
this.window = this.doc.defaultView;
|
|
this.$$("RetroVersion").textContent = B5500CentralControl.version;
|
|
this.window.name = "B5500Console";
|
|
this.window.addEventListener("unload",
|
|
B5500ConsolePanel.prototype.consoleUnload.bind(this));
|
|
this.$$("BurroughsLogo").addEventListener("click",
|
|
B5500ConsolePanel.prototype.BurroughsLogo_Click.bind(this));
|
|
this.$$("B5500Logo").addEventListener("click",
|
|
B5500ConsolePanel.prototype.B5500Logo_Click.bind(this));
|
|
this.$$("PowerOnBtn").addEventListener("click",
|
|
B5500ConsolePanel.prototype.PowerOnBtn_Click.bind(this));
|
|
this.$$("PowerOffBtn").addEventListener("click",
|
|
B5500ConsolePanel.prototype.PowerOffBtn_Click.bind(this));
|
|
this.$$("HaltBtn").addEventListener("click",
|
|
B5500ConsolePanel.prototype.HaltBtn_Click.bind(this));
|
|
this.$$("LoadBtn").addEventListener("click",
|
|
B5500ConsolePanel.prototype.LoadBtn_Click.bind(this));
|
|
this.$$("LoadSelectBtn").addEventListener("click",
|
|
B5500ConsolePanel.prototype.LoadSelectBtn_Click.bind(this));
|
|
this.$$("MemoryCheckBtn").addEventListener("click",
|
|
function(ev) {
|
|
this.dumpState("Memory-Check Button");
|
|
}.bind(this));
|
|
this.$$("NotReadyBtn").addEventListener("click", function(ev) {
|
|
this.dumpTape("Not-Ready Button");
|
|
}.bind(this));
|
|
|
|
this.aControl = this.$$("AControlBtn");
|
|
this.aNormal = this.$$("ANormalBtn");
|
|
this.bControl = this.$$("BControlBtn");
|
|
this.bNormal = this.$$("BNormalBtn");
|
|
this.procDelay = this.$$("procDelay");
|
|
this.procSlack = this.$$("procSlack");
|
|
this.buildLightMaps();
|
|
|
|
this.cc = new B5500CentralControl(this.global);
|
|
this.cc.B5500DumpState = B5500ConsolePanel.prototype.dumpState.bind(this); // for use by Processor
|
|
this.cc.B5500DumpTape = B5500ConsolePanel.prototype.dumpTape.bind(this); // for use by Processor
|
|
this.cc.focusConsole = B5500ConsolePanel.prototype.focusConsole.bind(this);
|
|
|
|
this.window.resizeTo(this.doc.documentElement.scrollWidth + this.window.outerWidth - this.window.innerWidth + 2, // kludge +2, dunno why
|
|
this.doc.documentElement.scrollHeight + this.window.outerHeight - this.window.innerHeight);
|
|
this.window.moveTo(screen.availWidth - this.window.outerWidth, 0);
|
|
this.window.focus();
|
|
this.setAnnunciators(this.showAnnunciators);
|
|
|
|
if (this.autoPowerUp) {
|
|
setCallback(null, this, 1000, this.PowerOnBtn_Click, ev);
|
|
}
|
|
};
|