1
0
mirror of https://github.com/pkimpel/retro-b5500.git synced 2026-02-12 03:07:30 +00:00

1. Release emulator version 0.13 (finally).

2. Implement new setCallback() mechanism to wrap setTimeout() and setImmediate().
2. Minor change to P2 management; remove context-bound callbacks.
3. Reduce window size and font size for peripheral UIs.
4. Implement <label> for file names on ColdLoader Load table.
5. Correct placement and color of buttons on Console and SPO.
6. Minor wiki updates for UI changes.
7. Rework SPO implementation to eliminate ".that" properties, implement setCallback(), and fix long-standing bugs that caused flaky operation.
This commit is contained in:
paul.kimpel@digm.com
2013-09-30 12:18:01 +00:00
parent 7c9cab76ba
commit d1e59a2f7c
16 changed files with 457 additions and 296 deletions

View File

@@ -25,8 +25,8 @@ DIV#CPDiv {
position: relative;
color: white;
background-color: #666;
width: 700px;
height: 300px;
width: 550px;
height: 194px;
border: 1px solid black;
border-radius: 8px;
padding: 0;
@@ -36,7 +36,7 @@ BUTTON.greenButton {
background-color: #060;
color: white;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 10px;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -47,7 +47,7 @@ BUTTON.blackButton {
background-color: black;
color: white;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 10px;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -58,7 +58,7 @@ BUTTON.redButton {
background-color: #900;
color: white;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 10px;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -96,44 +96,44 @@ BUTTON.redLit {
top: 56px;
left: 8px;
right: 8px;
height: 160px;
height: 80px;
font-weight: bold}
#CPStacker1Bar {
border: 1px solid white;
width: 610px}
width: 450px}
#CPStacker1Frame {
width: 100%;
height: 140px;
height: 60px;
margin-top: 1px;
border: 1px solid white;
color: black;
background-color: white;
font-family: Lucida Sans Typewriter, Courier New, Courier, monospace;
font-size: 10pt;
font-size: 8pt;
font-weight: normal}
#CPStacker2Div {
position: absolute;
top: 220px;
top: 140px;
left: 8px;
right: 8px;
height: 70px;
height: 44px;
font-weight: bold}
#CPStacker2Bar {
border: 1px solid white;
width: 610px}
width: 450px}
#CPStacker2Frame {
width: 100%;
height: 50px;
height: 30px;
margin-top: 1px;
border: 1px solid white;
color: black;
background-color: white;
font-family: Lucida Sans Typewriter, Courier New, Courier, monospace;
font-size: 10pt;
font-size: 8pt;
font-weight: normal}

View File

@@ -42,7 +42,7 @@ function B5500CardPunch(mnemonic, unitIndex, designate, statusChange, signal) {
this.stacker2 = null;
this.endOfStacker2 = null;
this.window = window.open("/B5500/webUI/B5500CardPunch.html", mnemonic,
"scrollbars=no,resizable,width=700,height=500");
"scrollbars=no,resizable,width=560,height=204,left=0,top=220");
this.window.addEventListener("load", function windowLoad() {
that.punchOnload();
}, false);
@@ -199,8 +199,8 @@ B5500CardPunch.prototype.punchOnload = function punchOnload() {
this.stacker1Frame = this.$$("CPStacker1Frame");
this.stacker1Frame.contentDocument.head.innerHTML += "<style>" +
"BODY {background-color: #FFE; margin: 2px} " +
"PRE {margin: 0; font-size: 9pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
"BODY {background-color: white; margin: 2px} " +
"PRE {margin: 0; font-size: 8pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
"</style>";
this.stacker1 = this.doc.createElement("pre");
this.stacker1Frame.contentDocument.body.appendChild(this.stacker1);
@@ -209,18 +209,14 @@ B5500CardPunch.prototype.punchOnload = function punchOnload() {
this.stacker2Frame = this.$$("CPStacker2Frame");
this.stacker2Frame.contentDocument.head.innerHTML += "<style>" +
"BODY {background-color: #FFE; margin: 2px} " +
"PRE {margin: 0; font-size: 9pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
"BODY {background-color: white; margin: 2px} " +
"PRE {margin: 0; font-size: 8pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
"</style>";
this.stacker2 = this.doc.createElement("pre");
this.stacker2Frame.contentDocument.body.appendChild(this.stacker2);
this.endOfStacker2 = this.doc.createElement("div");
this.stacker2Frame.contentDocument.body.appendChild(this.endOfStacker2);
this.window.moveTo(0, 180);
this.window.resizeTo(this.window.outerWidth+this.$$("CPDiv").scrollWidth-this.window.innerWidth+12,
this.window.outerHeight+this.$$("CPDiv").scrollHeight-this.window.innerHeight+12);
this.window.addEventListener("beforeunload", this.beforeUnload, false);
this.armRunout(false);

View File

@@ -18,7 +18,7 @@ BODY {
DIV#CRDiv {
position: relative;
background-color: #666;
width: 700px;
width: 550px;
height: 150px;
border: 1px solid black;
border-radius: 8px;
@@ -29,7 +29,7 @@ BUTTON.greenButton {
background-color: #060;
color: white;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 10px;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -40,7 +40,7 @@ BUTTON.blackButton {
background-color: black;
color: white;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 10px;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -51,7 +51,7 @@ BUTTON.redButton {
background-color: #900;
color: white;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 10px;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -59,7 +59,7 @@ BUTTON.redButton {
border-radius: 4px}
BUTTON.greenLit {
background-color: green}
background-color: #0F0}
BUTTON.redLit {
background-color: #F00}
@@ -88,14 +88,14 @@ BUTTON.redLit {
position: absolute;
border: 1px solid white;
color: white;
width: 680px;
width: 530px;
top: 54px;
left: 8px}
#CRProgressBar {
position: absolute;
border: 1px solid white;
width: 680px;
width: 530px;
top: 84px;
left: 8px}
@@ -103,7 +103,7 @@ BUTTON.redLit {
position: absolute;
top: 106px;
left: 8px;
width: 680px;
width: 530px;
height: 35px;
border: 1px solid white;
color: black;

View File

@@ -19,6 +19,7 @@
function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) {
/* Constructor for the CardReader object */
var that = this;
var x = (mnemonic == "CRA" ? 0 : 30);
this.mnemonic = mnemonic; // Unit mnemonic
this.unitIndex = unitIndex; // Ready-mask bit number
@@ -38,7 +39,7 @@ function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) {
}
this.doc = null;
this.window = window.open("/B5500/webUI/B5500CardReader.html", mnemonic,
"scrollbars=no,resizable,width=700,height=150");
"scrollbars=no,resizable,width=560,height=160,left="+x+",top="+x);
this.window.addEventListener("load", function windowLoad() {
that.readerOnload();
}, false);
@@ -320,19 +321,14 @@ B5500CardReader.prototype.beforeUnload = function beforeUnload(ev) {
B5500CardReader.prototype.readerOnload = function readerOnload() {
/* Initializes the reader window and user interface */
var that = this;
var x = (this.mnemonic == "CRA" ? 0 : this.window.outerWidth + 16);
this.doc = this.window.document;
this.doc.title = "retro-B5500 " + this.mnemonic;
this.window.moveTo(x, 0);
this.window.resizeTo(this.window.outerWidth+this.$$("CRDiv").scrollWidth-this.window.innerWidth+12,
this.window.outerHeight+this.$$("CRDiv").scrollHeight-this.window.innerHeight+12);
this.outHopperFrame = this.$$("CROutHopperFrame");
this.outHopperFrame.contentDocument.head.innerHTML += "<style>" +
"BODY {background-color: #FFE; margin: 2px} " +
"PRE {margin: 0; font-size: 9pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
"BODY {background-color: white; margin: 2px} " +
"PRE {margin: 0; font-size: 8pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
"</style>";
this.outHopper = this.doc.createElement("pre");
this.outHopperFrame.contentDocument.body.appendChild(this.outHopper);

View File

@@ -1261,7 +1261,10 @@ window.addEventListener("load", function() {
row.appendChild(cell);
// File ID
cell = document.createElement("td");
cell.appendChild(document.createTextNode(tapeDir[x]));
e = document.createElement("label");
e.appendChild(document.createTextNode(tapeDir[x]));
e.htmlFor = "File_" + x;
cell.appendChild(e);
row.appendChild(cell);
// Load as MCP selection radio button
cell = document.createElement("td");
@@ -2037,7 +2040,8 @@ window.addEventListener("load", function() {
if (!checkBrowser()) {
$$("FileSelector").addEventListener("change", fileSelector_onChange, false);
$$("ColdstartBtn").addEventListener("click", function(ev) {
if (confirm("Are you sure you want to do a COLD START?")) {
if (confirm("Are you sure you want to do a COLD START?\n" +
"This will PERMANENTLY DELETE all files in the B5500 disk subsystem.")) {
initializeDisk();
}
}, false);

View File

@@ -48,7 +48,7 @@ DIV#RetroVersion {
right: 170px;
color: white;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: x-small;
font-size: 7pt;
font-weight: bold}
IMG#BurroughsLogoImage {
@@ -65,7 +65,31 @@ BUTTON.whiteButton {
background-color: #CCC;
color: black;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: x-small;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
border: 1px solid #DDD;
border-radius: 4px}
BUTTON.greenButton {
position: absolute;
background-color: #060;
color: black;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
border: 1px solid #DDD;
border-radius: 4px}
BUTTON.redButton {
position: absolute;
background-color: #900;
color: white;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -77,7 +101,7 @@ BUTTON.blackButton {
background-color: black;
color: #999;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: x-small;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -89,7 +113,7 @@ BUTTON.yellowButton {
background-color: #990;
color: black;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: x-small;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -102,6 +126,12 @@ BUTTON.whiteLit {
BUTTON.blackLit {
color: #FFF}
BUTTON.greenLit {
background-color: #0F0}
BUTTON.redLit {
background-color: #F00}
BUTTON.yellowLit {
background-color: #FF0}
BUTTON.yellowLit5 {
@@ -132,7 +162,7 @@ BUTTON#NotReadyBtn {
top: 31px;
left: 132px}
BUTTON#LoadSelectBtn {
BUTTON#MemoryCheckBtn {
top: 31px;
left: 202px}
@@ -140,7 +170,8 @@ BUTTON#LoadBtn {
top: 31px;
left: 272px}
BUTTON#MemoryCheckBtn {
BUTTON#LoadSelectBtn {
line-height: 100%;
top: 31px;
left: 372px}
@@ -171,9 +202,10 @@ BUTTON#PowerOffBtn {
TABLE#CentralControl {
position: absolute;
bottom: 0;
width: 100%;
color: #666;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: x-small;
font-size: 7pt;
font-weight: bold}
COL.AnnunciatorCol {
@@ -182,6 +214,9 @@ COL.AnnunciatorCol {
TD#procDelay, TD#procSlack {
color: white;
text-align: right}
TD.statLabel {
color: white;
text-align: left}
.busy {

View File

@@ -7,7 +7,7 @@
<meta http-equiv="Content-Style-Type" content="text/css">
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500Console.css">
<script src="./setImmediate.js"></script>
<script src="./B5500SetCallback.js"></script>
<script src="./B5500DummyUnit.js"></script>
<script src="./B5500SPOUnit.js"></script>
@@ -68,11 +68,12 @@ window.addEventListener("load", function() {
}
function PowerOnBtn_Click(ev) {
$$("PowerOnBtn").className = "whiteButton whiteLit";
$$("AControlBtn").className = "yellowButton yellowLit";
$$("PowerOnBtn").className = "greenButton greenLit";
$$("HaltBtn").className = "redButton redLit";
cc.powerOn();
$$("PowerOnBtn").disabled = true;
$$("PowerOffBtn").disabled = false;
$$("LoadSelectBtn").disabled = false;
$$("LoadBtn").disabled = false;
$$("HaltBtn").disabled = true;
boundBlinkenlicht();
@@ -82,15 +83,21 @@ window.addEventListener("load", function() {
}
function PowerOffBtn_Click(ev) {
$$("PowerOnBtn").className = "whiteButton";
$$("PowerOnBtn").className = "greenButton";
$$("ANormalBtn").className = "yellowButton";
$$("AControlBtn").className = "yellowButton";
$$("BNormalBtn").className = "yellowButton";
$$("BControlBtn").className = "yellowButton";
$$("LoadSelectBtn").className = "yellowButton";
$$("MemoryCheckBtn").className = "redButton";
$$("NotReadyBtn").className = "whiteButton";
$$("HaltBtn").className = "redButton";
cc.powerOff();
$$("PowerOnBtn").disabled = false;
$$("PowerOffBtn").disabled = true;
$$("HaltBtn").disabled = true;
$$("LoadSelectBtn").disabled = true;
$$("LoadBtn").disabled = true;
$$("HaltBtn").disabled = true;
if (timer) {
clearTimeout(timer);
timer = null;
@@ -99,6 +106,7 @@ window.addEventListener("load", function() {
}
function HaltBtn_Click(ev) {
$$("HaltBtn").className = "redButton redLit";
cc.halt();
$$("HaltBtn").disabled = true;
$$("LoadBtn").disabled = false;
@@ -110,6 +118,7 @@ window.addEventListener("load", function() {
result = cc.load(false);
switch (result) {
case 0: // load initiated successfully
$$("HaltBtn").className = "redButton";
$$("HaltBtn").disabled = false;
$$("LoadBtn").disabled = true;
break;
@@ -131,10 +140,10 @@ window.addEventListener("load", function() {
function LoadSelectBtn_Click(ev) {
if (cc.cardLoadSelect) {
cc.cardLoadSelect = 0;
$$("LoadSelectBtn").className = "blackButton blackLit silverBorder";
$$("LoadSelectBtn").className = "yellowButton";
} else {
cc.cardLoadSelect = 1;
$$("LoadSelectBtn").className = "blackButton blackLit yellowBorder";
$$("LoadSelectBtn").className = "yellowButton yellowLit";
}
}
@@ -340,7 +349,7 @@ window.addEventListener("load", function() {
while (et < 0) {
et += stamp;
}
procDelay.innerHTML = p1.delayDeltaAvg.toFixed(3);
procDelay.innerHTML = p1.delayDeltaAvg.toFixed(1);
procSlack.innerHTML = (p1.procSlack/et*100).toFixed(1) + "%";
if (showAnnunciators) {
@@ -418,19 +427,19 @@ window.addEventListener("load", function() {
<body>
<div id=consoleDiv>
<button id=HaltBtn class="blackButton blackLit" DISABLED>HALT</button>
<button id=HaltBtn class="redButton" DISABLED>HALT</button>
<button id=NotReadyBtn class=yellowButton>NOT READY</button>
<button id=LoadSelectBtn class="blackButton blackLit silverBorder">LOAD SELECT</button>
<button id=NotReadyBtn class=whiteButton>NOT READY</button>
<button id=MemoryCheckBtn class=redButton>MEMORY CHECK</button>
<button id=LoadBtn class="blackButton blackLit" DISABLED>LOAD</button>
<button id=MemoryCheckBtn class=yellowButton>MEMORY CHECK</button>
<button id=LoadSelectBtn class="yellowButton" DISABLED>CARD LOAD SELECT</button>
<button id=ANormalBtn class=yellowButton>A NORMAL</button>
<button id=AControlBtn class=yellowButton>A CONTROL</button>
<button id=BNormalBtn class=yellowButton>B NORMAL</button>
<button id=BControlBtn class=yellowButton>B CONTROL</button>
<button id=PowerOnBtn class=whiteButton>POWER<br>ON</button>
<button id=PowerOnBtn class=greenButton>POWER<br>ON</button>
<button id=PowerOffBtn class="blackButton blackLit" DISABLED>POWER OFF</button>
<div id=BurroughsLogo>
@@ -447,8 +456,8 @@ window.addEventListener("load", function() {
<table id=CentralControl style="visibility:hidden">
<colgroup>
<col span=32 class=AnnunciatorCol>
<col>
<col span=31 class=AnnunciatorCol>
<col span=2>
</colgroup>
<tbody>
<tr id=CCInterruptRow>
@@ -472,7 +481,7 @@ window.addEventListener("load", function() {
<td id=CCI16F>DK2F <!-- Disk file #2 read check finished -->
<td colspan=13>
<td id=procSlack>
<td class=busy title="Percentage of time Processor A is throttling its performance">P1 Slack
<td class=statLabel title="Percentage of time Processor A is throttling its performance">P1 Slack
<tr id=CCPeripheralRow>
<td id=DRA>DRA <!-- 31 -->
<td id=DRB>DRB <!-- 30 -->
@@ -506,7 +515,7 @@ window.addEventListener("load", function() {
<td id=MTS>MTS <!-- 33 -->
<td id=MTT>MTT <!-- 32 -->
<td id=procDelay>
<td class=busy title="Average excess throttling delay for Processor A (ms)">P1 Delay
<td class=statLabel title="Average excess throttling delay for Processor A (ms)">P1 Delay
</table>
</div>

View File

@@ -7,6 +7,15 @@
<meta http-equiv="Content-Script-Type" content="text/javascript">
<meta http-equiv="Content-Style-Type" content="text/css">
<!-- link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500SPOUnit.css" -->
<style>
BODY {
padding: 4px}
PRE {
font-family: Lucida Sans Typewriter, Courier New, Courier, monospace;
font-size: 8pt}
</style>
</head>
<body>

View File

@@ -20,6 +20,9 @@
function B5500DummyPrinter(mnemonic, unitIndex, designate, statusChange, signal) {
/* Constructor for the DummyPrinter object */
var that = this;
var h = screen.availHeight*0.60;
var w = 900;
var s;
this.mnemonic = mnemonic; // Unit mnemonic
this.unitIndex = unitIndex; // Ready-mask bit number
@@ -41,7 +44,8 @@ function B5500DummyPrinter(mnemonic, unitIndex, designate, statusChange, signal)
this.paper = null;
this.endOfPaper = null;
this.window = window.open("/B5500/webUI/B5500DummyPrinter.html", mnemonic,
"scrollbars,resizable,width=600,height=500");
s = "scrollbars,resizable,width=" + w + ",height=" + h +
",left=0,top=" + (screen.availHeight - h));
this.window.addEventListener("load", function windowOnLoad() {
that.printerOnload();
}, false);
@@ -113,9 +117,6 @@ B5500DummyPrinter.prototype.printerOnload = function printerOnload() {
this.endOfPaper = this.doc.createElement("div");
this.doc.body.appendChild(this.endOfPaper);
this.window.moveTo(40, 40);
this.window.resizeTo(1000, screen.availHeight*0.80);
this.window.addEventListener("click", function windowOnClick(ev) {
if (ev.detail == 2) { // check for left-button double-click
that.ripPaper(ev);

View File

@@ -14,36 +14,42 @@ BODY {
position: relative;
background-color: black;
margin: 4px}
PRE {
font-family: Lucida Sans Typewriter, Courier New, Courier, monospace;
font-size: 10pt;
font-size: 8pt;
margin: 0}
DIV#SPODiv {
position: relative;
width: 800px;
background-color: #F7E7CE;
border-radius: 32px;
padding: 2em;
width: 750px;
height: 500px;
background-color: #FFC;
border-radius: 16px;
text-align: left}
IFRAME#SPOUT {
width: 620px;
height: 475px;
border: 2px solid black;
background-color: #FFE;
position: absolute;
left: 16px;
bottom: 16px;
height: 468px;
width: 560px;
border: 1px solid black}
BODY.SPOUT {
background-color: white;
font-family: Lucida Sans Typewriter, Courier New, Courier, monospace;
font-size: 10pt}
font-size: 8pt}
DIV#SPOControlsDiv {
position: absolute;
text-align: center;
height: 468px;
width: 136px;
left: 690px;
top: 32px}
right: 16px;
bottom: 16px}
IMG#TeletypeLogo {
left: auto;
right: auto}
@@ -53,19 +59,19 @@ BUTTON.whiteButton {
background-color: #CCC;
color: black;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 10px;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
border: 1px solid #DDD;
border-radius: 4px}
BUTTON.blackButton {
BUTTON.greenButton {
position: absolute;
background-color: black;
color: white;
background-color: #060;
color: black;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 10px;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -77,7 +83,7 @@ BUTTON.yellowButton {
background-color: #990;
color: black;
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 10px;
font-size: 8pt;
font-weight: bold;
width: 60px;
height: 40px;
@@ -87,50 +93,53 @@ BUTTON.yellowButton {
BUTTON.whiteLit {
background-color: white}
BUTTON.greenLit {
background-color: #0F0}
BUTTON.yellowLit {
background-color: #FF0}
BUTTON.blackBorder {
border: 1px solid black}
BUTTON#SPOReadyBtn {
top: 187px;
bottom: 224px;
left: 0px}
BUTTON#SPOPowerBtn {
top: 187px;
bottom: 224px;
right: 0px}
BUTTON#SPORemoteBtn {
top: 243px;
bottom: 168px;
left: 0px}
BUTTON#SPOLocalBtn {
top: 243px;
bottom: 168px;
right: 0px}
BUTTON#SPOInputRequestBtn {
top: 299px;
bottom: 112px;
left: 0px}
BUTTON#SPOEndOfMessageBtn {
top: 299px;
bottom: 112px;
right: 0px}
BUTTON#SPOBlank1Btn {
top: 355px;
bottom: 56px;
left: 0px}
BUTTON#SPOErrorBtn {
top: 355px;
bottom: 56px;
right: 0px}
BUTTON#SPOBlank2Btn {
top: 411px;
bottom: 0px;
left: 0px}
BUTTON#SPOBlank3Btn {
top: 411px;
bottom: 0px;
right: 0px}
.center {
@@ -139,7 +148,7 @@ BUTTON#SPOBlank3Btn {
.data {
font-family: Courier New, Courier, monospace;
text-align: left}
.number {
font-family: Courier New, Courier, monospace;
text-align: right}

View File

@@ -14,9 +14,9 @@
<div id=SPODiv>
<iframe id=SPOUT scrolling=auto></iframe>
<div id=SPOControlsDiv>
<img id=TeletypeLogo src="TeletypeLogo.gif">
<img id=TeletypeLogo src="TeletypeLogo.gif" alt="TeleType Model 33 KSR Logo">
<button id=SPOReadyBtn class="yellowButton blackBorder">READY</button>
<button id=SPOPowerBtn class="blackButton blackBorder">POWER</button>
<button id=SPOPowerBtn class="greenButton blackBorder greenLit">POWER</button>
<button id=SPORemoteBtn class="yellowButton blackBorder">REMOTE</button>
<button id=SPOLocalBtn class="yellowButton blackBorder yellowLit">LOCAL</button>
<button id=SPOInputRequestBtn class="yellowButton blackBorder">INPUT REQUEST</button>

View File

@@ -19,7 +19,6 @@
/**************************************/
function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) {
/* Constructor for the SPOUnit object */
var that = this;
this.maxScrollLines = 500; // Maximum amount of printer scrollback
this.charPeriod = 100; // Printer speed, milliseconds per character
@@ -31,15 +30,11 @@ function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) {
this.signal = signal; // external function to call for special signals (e.g,. SPO input request)
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
this.inTimer = null; // input setTimeout() token
this.outTimer = null; // output setTimeout() token
this.inTimer = null; // input setCallback() token
this.outTimer = null; // output setCallback() token
this.clear();
this.backspaceChar.that = this; // Store object context for these functions
this.printChar.that = this;
this.outputChar.that = this;
this.window = window.open("", mnemonic);
if (this.window) {
this.shutDown(); // destroy the previously-existing window
@@ -49,10 +44,9 @@ function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) {
this.paper = null;
this.endOfPaper = null;
this.window = window.open("/B5500/webUI/B5500SPOUnit.html", mnemonic,
"scrollbars,resizable,width=600,height=500");
this.window.addEventListener("load", function windowOnLoad() {
that.spoOnload();
}, false);
"scrollbars,resizable,width=758,height=508");
this.window.moveTo(screen.availWidth-this.window.outerWidth, screen.availHeight-this.window.outerHeight);
this.window.addEventListener("load", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.spoOnload, this), false);
}
// this.spoState enumerations
@@ -130,21 +124,20 @@ B5500SPOUnit.prototype.removeClass = function removeClass(e, name) {
B5500SPOUnit.prototype.setLocal = function setLocal() {
/* Sets the status of the SPO to Local */
if (this.spoState == this.spoRemote) {
this.spoState = this.spoLocal;
this.addClass(this.$$("SPOLocalBtn"), "yellowLit");
this.removeClass(this.$$("SPORemoteBtn"), "yellowLit");
this.removeClass(this.$$("SPOInputRequestBtn"), "yellowLit");
this.statusChange(0);
this.spoLocalRequested = false;
this.spoState = this.spoLocal;
this.addClass(this.$$("SPOLocalBtn"), "yellowLit");
this.removeClass(this.$$("SPORemoteBtn"), "yellowLit");
this.removeClass(this.$$("SPOInputRequestBtn"), "yellowLit");
this.statusChange(0);
// Set up to echo characters from the keyboard
this.buffer = null;
this.bufLength = 0;
this.bufIndex = 0;
this.printCol = 0;
this.nextCharTime = new Date().getTime();
this.finish = null;
}
// Set up to echo characters from the keyboard
this.buffer = null;
this.bufLength = 0;
this.bufIndex = 0;
this.printCol = 0;
this.nextCharTime = new Date().getTime();
this.finish = null;
};
/**************************************/
@@ -177,14 +170,13 @@ B5500SPOUnit.prototype.appendEmptyLine = function appendEmptyLine() {
/**************************************/
B5500SPOUnit.prototype.backspaceChar = function backspaceChar() {
/* Handles backspace for SPO input */
var that = backspaceChar.that;
var line = that.paper.lastChild;
var line = this.paper.lastChild;
if (that.bufLength > 0) {
that.bufIndex--;
if (this.bufLength > 0) {
this.bufIndex--;
}
if (that.printCol > 0) {
that.printCol--;
if (this.printCol > 0) {
this.printCol--;
}
if (line.nodeValue.length > 0) {
line.nodeValue = line.nodeValue.substring(0, line.nodeValue.length-1);
@@ -194,8 +186,7 @@ B5500SPOUnit.prototype.backspaceChar = function backspaceChar() {
/**************************************/
B5500SPOUnit.prototype.printChar = function printChar(c) {
/* Echoes the character code "c" to the SPO printer */
var that = printChar.that;
var line = that.paper.lastChild;
var line = this.paper.lastChild;
var len = line.nodeValue.length;
if (len < 1) {
@@ -212,38 +203,35 @@ B5500SPOUnit.prototype.outputChar = function outputChar() {
/* Outputs one character from the buffer to the SPO. If more characters remain
to be printed, schedules itself 100 ms later to print the next one, otherwise
calls finished(). If the column counter exceeds 72, a CR/LF pair is output.
A CR/LF pair is also output at the end of the message. Note the use of the local
function property "that" (initialized in the constructor), which supplies the
necessary SPOUnit object context across setTimeout() calls */
var that = outputChar.that; // retrieve our object context
var nextTime = that.nextCharTime + that.charPeriod;
A CR/LF pair is also output at the end of the message */
var nextTime = this.nextCharTime + this.charPeriod;
var delay = nextTime - new Date().getTime();
that.nextCharTime = nextTime;
if (that.printCol < 72) { // print the character
if (that.bufIndex < that.bufLength) {
that.printChar(that.buffer[that.bufIndex]);
that.bufIndex++;
that.printCol++;
this.outTimer = setTimeout(that.outputChar, delay);
this.nextCharTime = nextTime;
if (this.printCol < 72) { // print the character
if (this.bufIndex < this.bufLength) {
this.printChar(this.buffer[this.bufIndex]);
this.bufIndex++;
this.printCol++;
this.outTimer = setCallback(this.outputChar, this, delay);
} else { // set up for the final CR/LF
that.printCol = 72;
this.outTimer = setTimeout(that.outputChar, delay);
this.printCol = 72;
this.outTimer = setCallback(this.outputChar, this, delay);
}
} else if (that.printCol == 72) { // delay to fake the output of a carriage-return
that.printCol++;
this.outTimer = setTimeout(that.outputChar, delay+that.charPeriod);
} else if (this.printCol == 72) { // delay to fake the output of a carriage-return
this.printCol++;
this.outTimer = setCallback(this.outputChar, this, delay+this.charPeriod);
} else { // actually output the CR/LF
that.appendEmptyLine();
if (that.bufIndex < that.bufLength) {
that.printCol = 0; // more characters to print after the CR/LF
this.outTimer = setTimeout(that.outputChar, delay);
this.appendEmptyLine();
if (this.bufIndex < this.bufLength) {
this.printCol = 0; // more characters to print after the CR/LF
this.outTimer = setCallback(this.outputChar, this, delay);
} else { // message text is exhausted
that.finish(that.errorMask, that.bufLength); // report finish with any errors
if (that.spoLocalRequested) {
that.setLocal();
this.finish(this.errorMask, this.bufLength); // report finish with any errors
if (this.spoLocalRequested) {
this.setLocal();
} else {
that.spoState = that.spoRemote;
this.spoState = this.spoRemote;
}
}
}
@@ -251,7 +239,7 @@ B5500SPOUnit.prototype.outputChar = function outputChar() {
/**************************************/
B5500SPOUnit.prototype.terminateInput = function terminateInput() {
/* Handles the End of Message event. Turns off then Input Request lamp, then
/* Handles the End of Message event. Turns off the Ready lamp, then
calls outputChar(), which will find bufIndex==bufLength, output a new-line,
set the state to Remote, and call finish() for us. Slick, eh? */
@@ -268,12 +256,9 @@ B5500SPOUnit.prototype.cancelInput = function cancelInput() {
/* Handles the Error message event. This is identical to terminateInput(),
but it also sets a parity error so the input message will be rejected */
if (this.spoState = this.spoInput) {
this.removeClass(this.$$("SPOReadyBtn"), "yellowLit");
if (this.spoState == this.spoInput) {
this.errorMask |= 0x10; // set parity/error-button bit
this.bufLength = this.bufIndex;
this.nextCharTime = new Date().getTime();
this.outputChar();
this.terminateInput();
}
};
@@ -282,7 +267,6 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
/* Handles keyboard character events. Depending on the state of the unit,
either buffers the character for transmission to the I/O Unit, simply echos
it to the printer, or ignores it altogether */
var that = this;
var c = ev.charCode;
var index = this.bufLength;
var nextTime;
@@ -300,27 +284,21 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
if (this.printCol < 72) {
this.printCol++;
}
this.inTimer = setTimeout(function keyPressChar() {
that.printChar(c);
}, nextTime-stamp);
this.inTimer = setCallback(this.printChar, this, nextTime-stamp, c);
}
if (c == 126) { // "~" (B5500 group-mark)
c = this.keyFilter[c];
if (this.printCol < 72) {
this.printCol++;
}
this.inTimer = setTimeout(function keyPressGM() {
that.printChar(c);
}, nextTime-stamp);
this.inTimer = setCallback(this.printChar, this, nextTime-stamp, c);
this.nextCharTime = nextTime + this.charPeriod;
this.terminateInput();
}
} else if (this.spoState == this.spoLocal) {
if (c >= 32 && c <= 126) {
c = this.keyFilter[c];
this.inTimer = setTimeout(function keyPressLocalChar() {
that.printChar(c);
}, nextTime-stamp);
this.inTimer = setCallback(this.printChar, this, nextTime-stamp, c);
}
}
@@ -330,7 +308,6 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
/**************************************/
B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
/* Handles key-down events to capture ESC, BS, and Enter keystrokes */
var that = this;
var c = ev.keyCode;
var nextTime;
var result = true;
@@ -361,7 +338,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
switch (this.spoState) {
case this.spoInput:
case this.spoLocal:
this.inTimer = setTimeout(this.backspaceChar, nextTime-stamp);
this.inTimer = setCallback(this.backspaceChar, this, nextTime-stamp);
this.nextCharTime = nextTime;
result = false;
break;
@@ -375,9 +352,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
result = false;
break
case this.spoLocal:
this.inTimer = setTimeout(function keyDownLocal() {
that.appendEmptyLine();
}, nextTime-stamp+this.charPeriod);
this.inTimer = setCallback(this.appendEmptyLine, this, nextTime-stamp+this.charPeriod);
this.nextCharTime = nextTime;
result = false;
break;
@@ -424,7 +399,6 @@ B5500SPOUnit.prototype.beforeUnload = function beforeUnload(ev) {
/**************************************/
B5500SPOUnit.prototype.spoOnload = function spoOnload() {
/* Initializes the SPO window and user interface */
var that = this;
var x;
this.doc = this.window.document;
@@ -436,56 +410,47 @@ B5500SPOUnit.prototype.spoOnload = function spoOnload() {
this.endOfPaper.appendChild(this.doc.createTextNode("\xA0"));
this.$$("SPOUT").contentDocument.body.appendChild(this.endOfPaper);
this.$$("SPOUT").contentDocument.head.innerHTML += "<style>" +
"BODY {background-color: #FFE} " +
"PRE {margin: 0; font-size: 10pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
"BODY {background-color: white} " +
"PRE {margin: 0; font-size: 8pt; font-family: Lucida Sans Typewriter, Courier New, Courier, monospace}" +
"</style>";
this.window.resizeTo(this.window.outerWidth+this.$$("SPODiv").scrollWidth-this.window.innerWidth+8,
this.window.outerHeight+this.$$("SPODiv").scrollHeight-this.window.innerHeight+8);
this.window.moveTo(0, screen.availHeight-this.window.outerHeight);
this.window.focus();
this.window.addEventListener("beforeunload", this.beforeUnload, false);
this.window.addEventListener("keypress", function windowKeyPress(ev) {
that.keyPress(ev);
}, false);
this.window.addEventListener("keypress", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.keyPress, this), false);
this.window.addEventListener("keydown", function windowKeyDown(ev) {
that.keyDown(ev);
}, false);
this.window.addEventListener("keydown", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.keyDown, this), false);
this.$$("SPORemoteBtn").addEventListener("click", function remoteClick() {
that.setRemote();
}, false);
this.$$("SPORemoteBtn").addEventListener("click", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.setRemote, this), false);
this.$$("SPOLocalBtn").addEventListener("click", function localClick() {
that.setLocal();
}, false);
this.$$("SPOInputRequestBtn").addEventListener("click", function inputRequestClick() {
if (that.spoState == that.spoRemote || that.spoState == that.spoOutput) {
that.addClass(that.$$("SPOInputRequestBtn"), "yellowLit");
that.signal();
this.$$("SPOLocalBtn").addEventListener("click", B5500CentralControl.bindMethod(function localClick() {
if (this.spoState == this.spoRemote) {
this.setLocal();
} else {
this.spoLocalRequested = true;
}
}, false);
}, this), false);
this.$$("SPOErrorBtn").addEventListener("click", function errorClick() {
that.cancelInput();
}, false);
this.$$("SPOInputRequestBtn").addEventListener("click", B5500CentralControl.bindMethod(function inputRequestClick() {
if (this.spoState == this.spoRemote || this.spoState == this.spoOutput) {
this.addClass(this.$$("SPOInputRequestBtn"), "yellowLit");
this.signal();
}
}, this), false);
this.$$("SPOEndOfMessageBtn").addEventListener("click", function endOfMessageClick() {
that.terminateInput();
}, false);
this.$$("SPOErrorBtn").addEventListener("click", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.cancelInput, this), false);
this.$$("SPOEndOfMessageBtn").addEventListener("click", B5500CentralControl.bindMethod(B5500SPOUnit.prototype.terminateInput, this), false);
for (x=0; x<32; x++) {
this.appendEmptyLine();
}
this.printText("retro-B5500 Emulator Version " + B5500CentralControl.version, function initComplete() {
this.printText("retro-B5500 Emulator Version " + B5500CentralControl.version, B5500CentralControl.bindMethod(function initComplete() {
this.window.focus();
that.setRemote();
that.appendEmptyLine();
});
this.setRemote();
this.appendEmptyLine();
}, this));
};
/**************************************/

166
webUI/B5500SetCallback.js Normal file
View File

@@ -0,0 +1,166 @@
/***********************************************************************
* retro-b5500/webUI B5500SetCallback.js
************************************************************************
* Copyright (c) 2013, Nigel Williams and Paul Kimpel.
* Licensed under the MIT License, see
* http://www.opensource.org/licenses/mit-license.php
************************************************************************
* B5500 universal function call-back module.
*
* Implements a combination setTimeout() and setImmediate() facility for the
* B5500 emulator web-based user interface. setCallback() is used the same way
* that setTimeout() is used, except that for low values of the timeout parameter,
* it merely yields control to any other pending events and timers before calling
* the call-back function.
*
* This facility is needed because modern browsers implement a minimum delay
* when calling setTimeout(). HTML5 specs require 4ms, but on Microsoft Windows
* systems (at least through Win7), the minimum precision of setTimeout() is
* about 15ms, unless you are running Google Chrome. This module will use
* setTimeout() if the requested delay time is above a certain threshold, and
* a setImmediate()-like mechanism (based on window.postMessage) if the requested
* delay is above that threshold.
*
* Even though this mechanism may execute the call-back function sooner than the
* requested delay specifies, the timing and throttling mechanisms in the
* emulator will correct for that in subsequent delay cycles. We are going for
* good average behavior, and quick call-backs are better than consistently
* too-long callbacks in this environment, so that I/Os can be initiated and
* their finish detected in finer-grained time increments.
*
* The SetCallback mechanism defines two functions, which become members of the
* global (window) object:
*
* cookie = setCallback(fcn, context, delay, args...)
*
* Requests that the function "fcn" be called after "delay" milliseconds.
* The function will be called as a method of "context", passing the
* list of arguments "args...". The call-back "fcn" may be called
* earlier or later than the specified delay. setCallBack returns a
* numeric token identifying the call-back event, which can be used
* with clearCallback(). Note that passing a string in lieu of a function
* object is not permitted.
*
* clearCallBack(cookie)
*
* Cancels a pending call-back event, if in fact it is still pending.
* The "cookie" parameter is a value returned from setCallback().
*
* This implementation has been inspired by Domenic Denicola's shim for the
* setImmediate() API at https://github.com/NobleJS/setImmediate, and
* David Baron's setZeroTimeout() implemenmentation described in his blog
* at http://dbaron.org/log/20100309-faster-timeouts.
*
* Stole a little of their code, too.
*
************************************************************************
* 2013-08-04 P.Kimpel
* Original version, cloned from B5500DiskUnit.js.
***********************************************************************/
"use strict";
(function (global) {
/* Define a closure for the setCallback() mechanism */
var minTimeout = 4; // minimum setTimeout() threshold, milliseconds
var nextCookieNr = 1; // next setCallback cookie return value
var pendingCallbacks = {}; // hash of pending callbacks, indexed by cookie as a string
var secretPrefix = "com.google.code.p.retro-b5500.webUI." + new Date().getTime().toString(16);
/**************************************/
function activateCallback(cookieName) {
/* Activates a callback after its delay period has expired */
var thisCallback;
if (cookieName in pendingCallbacks) {
thisCallback = pendingCallbacks[cookieName];
delete pendingCallbacks[cookieName];
try {
thisCallback.fcn.apply(thisCallback.context, thisCallback.args);
} catch (err) {
console.log("B5500SetCallback.activateCallback: " + err);
}
}
}
/**************************************/
function clearCallback(cookie) {
/* Disables a pending callback, if it still exists and is still pending */
var cookieName = cookie.toString();
var thisCallback;
if (cookieName in pendingCallbacks) {
thisCallback = pendingCallbacks[cookieName];
delete pendingCallbacks[cookieName];
if (thisCallback.cancelToken) {
if (thisCallback.type == 2) {
global.clearTimeout(thisCallback.cancelToken);
}
}
}
}
/**************************************/
function setCallback(fcn, context, callbackDelay, args) {
/* Sets up and schedules a callback for function "fcn", called with context
"context", after a delay of "delay" ms. Any "args" will be passed to "fcn".
If the delay is less than "minTimeout", a setImmediate-like mechanism based on
window.postsMessage() will be used; otherwise the environment's standard
setTimeout mechanism will be used */
var delay = callbackDelay || 0;
var cookie = nextCookieNr++;
var cookieName = cookie.toString();
var thisCallback = {
args: null,
fcn: fcn,
context: context || this,
};
pendingCallbacks[cookieName] = thisCallback;
if (arguments.length > 3) {
thisCallback.args = Array.slice(arguments, 3);
}
if (delay < minTimeout) {
thisCallback.type = 1;
global.postMessage(secretPrefix + cookieName, "*");
} else {
thisCallback.type = 2;
thisCallback.cancelToken = global.setTimeout(activateCallback, delay, cookieName);
}
return cookie;
}
/**************************************/
function onMessage(ev) {
/* Handler for the global.onmessage event. Activates the callback */
var cookieName;
var payload;
if (ev.source === global) {
payload = ev.data.toString();
if (payload.substring(0, secretPrefix.length) === secretPrefix) {
cookieName = payload.substring(secretPrefix.length);
activateCallback(cookieName);
}
}
}
/********** Outer block of anonymous closure **********/
if (!global.setCallback && global.postMessage && !global.importScripts) {
// Attach to the prototype of global, if possible, otherwise to global itself
var attachee = global;
/*****
if (typeof Object.getPrototypeOf === "function") {
if ("setTimeout" in Object.getPrototypeOf(global)) {
attachee = Object.getPrototypeOf(global);
}
}
*****/
global.addEventListener("message", onMessage, false);
attachee.setCallback = setCallback;
attachee.clearCallback = clearCallback;
}
}(typeof global === "object" && global ? global : this));