1
0
mirror of https://github.com/pkimpel/retro-220.git synced 2026-01-24 19:31:29 +00:00
pkimpel.retro-220/webUI/B220PaperTapePunch.js
Paul Kimpel df29455c91 Commit version 0.00d:
1. Implement console teletype printer and paper tape punch devices.
2. Correct timing mechanism and operation complete processing for asynchronous (I/O) processor mode.
3. Delete extraneous alarm toggles from B220Processor.
4. Correct Reset And Transfer switch so it will work when the Processor is running.
5. Implement Hello World default program for SPO at address 0020.
6. Change keyboard keystroke assignments for B220ConsoleKeyboard.
7. Implement Reset-to-Defaults feature for B220SystemConfig.
8. Implement DOM Element.classList for B220Util class attribute manipulation.
9. Minor typo correction to BALGOL-Generator.bacg.
2017-04-29 13:41:02 -07:00

325 lines
13 KiB
JavaScript

/***********************************************************************
* retro-220/webUI B220PaperTapePunch.js
************************************************************************
* Copyright (c) 2017, Paul Kimpel.
* Licensed under the MIT License, see
* http://www.opensource.org/licenses/mit-license.php
************************************************************************
* Burroughs 220 High-Speed Paper Tape Punch device.
************************************************************************
* 2017-04-28 P.Kimpel
* Original version, from retro-205 D205ConsoleOutput.js.
***********************************************************************/
"use strict";
/**************************************/
function B220PaperTapePunch(mnemonic, unitIndex, config) {
/* Constructor for the Console Paper Tape Punch object */
var top = unitIndex*32;
var left = unitIndex*32;
this.config = config; // System configuration object
this.mnemonic = mnemonic; // Unit mnemonic
this.unitIndex = unitIndex; // Unit index into console output units
this.outTimer = 0; // output setCallback() token
this.nextCharTime = 0; // next time a character can be punched
this.unitMask = 0; // unit selection mask
this.unitSwitch = new Array(11); // unit selection switch objects
this.tabStop = []; // 0-relative tab stop positions
this.boundFlipSwitch = B220Util.bindMethod(this, B220PaperTapePunch.prototype.flipSwitch);
this.boundReceiveSign = B220Util.bindMethod(this, B220PaperTapePunch.prototype.receiveSign);
this.boundReceiveChar = B220Util.bindMethod(this, B220PaperTapePunch.prototype.receiveChar);
this.clear();
// Create the punch window and onload event
this.doc = null;
this.punchTape = null;
this.punchEOP = null;
this.window = window.open("../webUI/B220PaperTapePunch.html", mnemonic,
"location=no,scrollbars=no,resizable,width=240,height=160," +
"left=" + left + ",top=" + top);
this.window.addEventListener("load", B220Util.bindMethod(this,
B220PaperTapePunch.prototype.punchOnLoad));
}
/**************************************/
B220PaperTapePunch.offSwitchImage = "./resources/ToggleDown.png";
B220PaperTapePunch.onSwitchImage = "./resources/ToggleUp.png";
B220PaperTapePunch.charsPerSecond = 60; // Punch speed, characters/second
B220PaperTapePunch.charPeriod = 1000/B220PaperTapePunch.charsPerSecond;
// Inter-character period, ms
B220PaperTapePunch.maxScrollLines = 45000;
// Maximum amount of punch word scrollback
B220PaperTapePunch.codeXlate = [ // translate internal B220 code to ANSI
// Note that ANSI new-line sequences are used for end-of-word characters,
// so B220 carriage-return translates to "|". To avoide space-expansion of
// tab characters, they are translated to "~".
" ", "?", " ", ".", "\u00A4", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 00-0F
"&", "?", "?", "$", "*", "\f", "|", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 10-1F
"-", "/", "?", ",", "%", "?", "~", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 20-2F
"?", "?", "?", "#", "@", "!", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 30-3F
"?", "A", "B", "C", "D", "E", "F", "G", "H", "I", "?", "?", "?", "?", "?", "?", // 40-4F
"?", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "?", "?", "?", "?", "?", "?", // 50-5F
"?", "?", "S", "T", "U", "V", "W", "X", "Y", "Z", "?", "?", "?", "?", "?", "?", // 60-6F
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 70-7F
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "?", "?", "?", "?", "?", "?", // 80-8F
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]; // 90-9F
/**************************************/
B220PaperTapePunch.prototype.clear = function clear() {
/* Initializes (and if necessary, creates) the SPO unit state */
this.ready = false; // ready status
this.busy = false; // busy status
};
/**************************************/
B220PaperTapePunch.prototype.beforeUnload = function beforeUnload(ev) {
var msg = "Closing this window will make the device unusable.\n" +
"Suggest you stay on the page and minimize this window instead";
ev.preventDefault();
ev.returnValue = msg;
return msg;
};
/**************************************/
B220PaperTapePunch.prototype.$$ = function $$(e) {
return this.doc.getElementById(e);
};
/**************************************/
B220PaperTapePunch.prototype.punchEmptyPaper = function punchEmptyPaper() {
/* Empties the punch output "paper" and initializes it for new output */
while (this.punchTape.firstChild) {
this.punchTape.removeChild(this.punchTape.firstChild);
}
this.punchTape.appendChild(this.doc.createTextNode(""));
};
/**************************************/
B220PaperTapePunch.prototype.punchEmptyLine = function punchEmptyLine(text) {
/* Removes excess lines already output, then appends a new text node to the
<pre> element within the paper element. Note that "text" is an ANSI string */
var paper = this.punchTape;
var line = text || "";
while (paper.childNodes.length > B220PaperTapePunch.maxScrollLines) {
paper.removeChild(paper.firstChild);
}
paper.lastChild.nodeValue += "\n"; // newline
paper.appendChild(this.doc.createTextNode(line));
};
/**************************************/
B220PaperTapePunch.prototype.punchChar = function punchChar(code) {
/* Outputs the character "code" to the device */
var c = B220PaperTapePunch.codeXlate[code];
var line;
var len;
if (c != "?") { // some 220 codes just don't print
line = this.punchTape.lastChild.nodeValue;
len = line.length;
if (len < 1) {
this.punchTape.lastChild.nodeValue = c;
} else {
this.punchTape.lastChild.nodeValue = line + c;
}
}
};
/**************************************/
B220PaperTapePunch.prototype.resizeWindow = function resizeWindow(ev) {
/* Handles the window onresize event by scrolling the "tape" so it remains at the end */
this.punchEOP.scrollIntoView();
};
/**************************************/
B220PaperTapePunch.prototype.punchCopyTape = function punchCopyTape(ev) {
/* Copies the text contents of the "paper" area of the device, opens a new
temporary window, and pastes that text into the window so it can be copied
or saved by the user */
var text = this.punchTape.textContent;
var title = "B220 " + this.mnemonic + " Text Snapshot";
var win = window.open("./B220FramePaper.html", "PaperTape-Snapshot",
"scrollbars,resizable,width=500,height=500");
win.moveTo((screen.availWidth-win.outerWidth)/2, (screen.availHeight-win.outerHeight)/2);
win.addEventListener("load", function() {
var doc;
doc = win.document;
doc.title = title;
doc.getElementById("Paper").textContent = text;
});
this.punchEmptyPaper();
ev.preventDefault();
ev.stopPropagation();
};
/**************************************/
B220PaperTapePunch.prototype.flipSwitch = function flipSwitch(ev) {
/* Handler for switch clicks */
var id = ev.target.id;
var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
var x;
switch (id) {
case "RemoteSwitch":
this.remoteSwitch.flip();
prefs.remote = this.remoteSwitch.state;
this.ready = (this.remoteSwitch.state != 0);
this.readyLamp.set(this.remoteSwitch.state);
break;
case "UnitDesignateKnob":
x = this.unitDesignateKnob.selectedIndex;
if (x < 0) {
x = this.unitDesignateKnob.length-1;
this.unitMask = 0;
} else {
this.unitMask = B220Processor.pow2[x];
prefs.unitMask = this.unitMask
}
break;
}
this.config.putNode("ConsoleOutput.units", prefs, this.unitIndex);
ev.preventDefault();
ev.stopPropagation();
};
/**************************************/
B220PaperTapePunch.prototype.punchOnLoad = function punchOnLoad() {
/* Initializes the Paper Tape Punch window and user interface */
var body;
var id;
var mask;
var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
var x;
this.doc = this.window.document;
this.doc.title = "retro-220 Punch - " + this.mnemonic;
this.punchTape = this.$$("Paper");
this.punchEOP = this.$$("EndOfPaper");
this.punchEmptyPaper();
body = this.$$("PaperTapePunch")
this.remoteSwitch = new ToggleSwitch(body, null, null, "RemoteSwitch",
B220PaperTapePunch.offSwitchImage, B220PaperTapePunch.onSwitchImage);
this.remoteSwitch.set(prefs.remote);
this.ready = (this.remoteSwitch.state != 0);
this.readyLamp = new ColoredLamp(body, null, null, "ReadyLamp", "blueLamp lampCollar", "blueLit");
this.readyLamp.set(this.remoteSwitch.state);
this.unitDesignateKnob = this.$$("UnitDesignateKnob");
mask = 0x001;
this.unitMask = prefs.unitMask;
if (this.unitMask == 0) {
this.unitDesignateKnob.selectedIndex = this.unitDesignateKnob.length-1;
} else {
for (x=0; x<this.unitDesignateKnob.length; ++x) {
if (this.unitMask & mask) {
this.unitDesignateKnob.selectedIndex = x;
break; // out of for loop
} else {
mask <<= 1;
}
}
}
// Events
this.window.addEventListener("beforeunload",
B220PaperTapePunch.prototype.beforeUnload);
this.window.addEventListener("resize",
B220Util.bindMethod(this, B220PaperTapePunch.prototype.resizeWindow));
this.punchTape.addEventListener("dblclick",
B220Util.bindMethod(this, B220PaperTapePunch.prototype.punchCopyTape));
this.remoteSwitch.addEventListener("click", this.boundFlipSwitch);
this.unitDesignateKnob.addEventListener("change", this.boundFlipSwitch);
//this.punchWin.moveTo(screen.availWidth-this.punchWin.outerWidth,
// screen.availHeight-this.punchWin.outerHeight);
//this.punchWin.moveTo(0, 430);
this.window.focus();
};
/***********************************************************************
* Output Entry Points *
***********************************************************************/
/**************************************/
B220PaperTapePunch.prototype.initiateOutput = function initiateOutput(successor) {
/* Initiates output to the punch. This simply calls the successor function,
passing our receiver function, so the processor can get the ball rolling */
successor(this.boundReceiveSign);
};
/**************************************/
B220PaperTapePunch.prototype.receiveSign = function receiveSign(char, successor) {
/* Receives the sign character from the processor and handles it according
to the value of the sign and the setting of the Map Memory and LZ Suppress
switches */
var delay = B220PaperTapePunch.charPeriod; // default character delay
var stamp = performance.now(); // current time
this.punchChar(char); // punch the sign
if (this.nextCharTime <= stamp) {
this.nextCharTime = stamp;
}
setCallback(this.mnemonic, this, this.nextCharTime-stamp+delay, successor, this.boundReceiveChar);
this.nextCharTime += delay;
};
/**************************************/
B220PaperTapePunch.prototype.receiveChar = function receiveChar(char, successor) {
/* Receives a non-sign character from the processor and outputs it. Special handling
is provided for tabs, carriage returns, form feeds, and end-of-word characters */
var delay = B220PaperTapePunch.charPeriod; // default character delay
var nextReceiver = this.boundReceiveChar; // default routine to receive next char
var stamp = performance.now(); // current time
switch (char) {
case 0x35: // end-of-word
delay = 0;
this.punchEmptyLine();
nextReceiver = this.boundReceiveSign; // next will be start of a new word
break;
default: // all others
this.punchChar(char);
break;
} // switch char
setCallback(this.mnemonic, this, this.nextCharTime-stamp+delay, successor, nextReceiver);
this.nextCharTime += delay;
};
/**************************************/
B220PaperTapePunch.prototype.shutDown = function shutDown() {
/* Shuts down the device */
if (this.outTimer) {
clearCallback(this.outTimer);
}
if (this.window) {
this.window.removeEventListener("beforeunload", B220PaperTapePunch.prototype.beforeUnload);
this.window.close();
this.window = null;
}
};