/*********************************************************************** * retro-220/webUI B220ConsolePrinter.js ************************************************************************ * Copyright (c) 2017, Paul Kimpel. * Licensed under the MIT License, see * http://www.opensource.org/licenses/mit-license.php ************************************************************************ * Burroughs 220 Console Printer and * High-Speed Paper Tape Punch devices. ************************************************************************ * 2017-03-17 P.Kimpel * Original version, from retro-205 D205ConsoleOutput.js. ***********************************************************************/ "use strict"; /**************************************/ function B220ConsolePrinter(mnemonic, unitIndex, config) { /* Constructor for the Console Printer 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.columns = 72; // right-margin auto-return position this.format = 0; // 0=space, 1=tab, 2=carriage-return this.nextCharTime = 0; // next time a character can be printed this.mapMemory = 0; // map-memory switch setting this.unitMask = 0; // unit selection mask this.unitSwitch = new Array(11); // unit selection switch objects this.tabStop = []; // 0-relative tab stop positions this.zeroSuppress = 0; // zero-suppression switch setting this.charPeriod = 0; // printer speed, ms/char this.newLinePeriod = 0; // delay for carriage-returns this.boundButton_Click = B220ConsolePrinter.prototype.button_Click.bind(this); this.boundText_OnChange = B220ConsolePrinter.prototype.text_OnChange.bind(this); this.boundFlipSwitch = B220ConsolePrinter.prototype.flipSwitch.bind(this); this.boundReceiveSign = B220ConsolePrinter.prototype.receiveSign.bind(this); this.boundReceiveChar = B220ConsolePrinter.prototype.receiveChar.bind(this); this.clear(); // Create the printer window and onload event this.doc = null; this.window = null; this.paper = null; this.printerEOP = null; this.printerLine = 0; this.printerCol = 0; B220Util.openPopup(window, "../webUI/B220ConsolePrinter.html", mnemonic, "location=no,scrollbars=no,resizable,width=640,height=240," + "left=" + left + ",top=" + top, this, B220ConsolePrinter.prototype.printerOnLoad); } /**************************************/ B220ConsolePrinter.offSwitchImage = "./resources/ToggleDown.png"; B220ConsolePrinter.onSwitchImage = "./resources/ToggleUp.png"; B220ConsolePrinter.ttySpeed = 10; // TTY printer speed, char/sec B220ConsolePrinter.ttyNewLine = 200; // TTY carriage-return delay, ms B220ConsolePrinter.whippetSpeed = 1000; // Whippet printer speed, char/sec B220ConsolePrinter.whippetNewLine = 75; // Whippet carriage-return delay, ms B220ConsolePrinter.formFeedPeriod = 500;// full-page form-feed delay, ms B220ConsolePrinter.cursorChar = "_"; // end-of-line cursor B220ConsolePrinter.pageSize = 66; // lines/page for form-feed B220ConsolePrinter.maxScrollLines = 15000; // Maximum amount of paper scrollback B220ConsolePrinter.codeXlate = [ // translate internal B220 code to ANSI " ", "?", " ", ".", "\u00A4", "?", "?", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 00-0F "&", "?", "?", "$", "*", "\f", "\n", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 10-1F "-", "/", "?", ",", "%", "?", "\t", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 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 "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // A0-AF "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // B0-BF "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // C0-CF "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // D0-DF "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", // E0-EF "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"]; // F0-FF /**************************************/ B220ConsolePrinter.prototype.clear = function clear() { /* Initializes (and if necessary, creates) the SPO unit state */ this.ready = false; // ready status this.busy = false; // busy status this.eowAction = 0; // 1 => End-of-Word action needed this.suppressLZ = 0; // 1 => currently suppressing leading zeroes }; /**************************************/ B220ConsolePrinter.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; }; /**************************************/ B220ConsolePrinter.prototype.$$ = function $$(e) { return this.doc.getElementById(e); }; /**************************************/ B220ConsolePrinter.prototype.emptyPaper = function emptyPaper() { /* Empties the printer output "paper" and initializes it for new output */ while (this.paper.firstChild) { this.paper.removeChild(this.paper.firstChild); } this.paper.appendChild(this.doc.createTextNode("")); this.printerLine = 0; this.printerCol = 0; this.printerEOP.scrollIntoView(); }; /**************************************/ B220ConsolePrinter.prototype.printNewLine = function printNewLine(text) { /* Removes excess lines already output, then appends a newline to the current text node, and then a new text node to the end of the
element
within the paper element. Note that "text" is an ANSI string */
var paper = this.paper;
var lastLine = paper.lastChild.nodeValue;
var line = text || "";
while (paper.childNodes.length > B220ConsolePrinter.maxScrollLines) {
paper.removeChild(paper.firstChild);
}
paper.lastChild.nodeValue = lastLine.substring(0, lastLine.length-1) + "\n";
paper.appendChild(this.doc.createTextNode(line + B220ConsolePrinter.cursorChar));
++this.printerLine;
this.printerCol = line.length;
this.printerEOP.scrollIntoView();
};
/**************************************/
B220ConsolePrinter.prototype.printChar = function printChar(code) {
/* Outputs the character "code" to the device */
var c = B220ConsolePrinter.codeXlate[code];
var line;
var len;
if (c != "?") { // some 220 codes just don't print
line = this.paper.lastChild.nodeValue;
len = line.length;
if (len < 1) { // first char on line
this.paper.lastChild.nodeValue = c + B220ConsolePrinter.cursorChar;
this.printerCol = 1;
} else if (len < this.columns) {// normal char
this.paper.lastChild.nodeValue =
line.substring(0, len-1) + c + B220ConsolePrinter.cursorChar;
++this.printerCol;
} else { // right margin overflow
this.printNewLine(c);
}
}
};
/**************************************/
B220ConsolePrinter.prototype.printTab = function printTab() {
/* Simulates tabulation by outputting an appropriate number of spaces */
var tabCol = this.columns+1; // tabulation column (defaults to line overflow)
var x = 0; // scratch index
for (x=0; x this.printerCol) {
tabCol = this.tabStop[x];
break; // out of for loop
}
} // for x
if (this.columns < tabCol) {
this.printNewLine(); // tab would overflow right margin
} else {
while (this.printerCol < tabCol) {
this.printChar(0x00); // output a space
}
}
};
/**************************************/
B220ConsolePrinter.prototype.printFormFeed = function printFormFeed() {
/* Simulates a form feed by appending a newline and form feed to the
current text node, and then a new text node to the end of the element
within the paper element, with sufficient spaces to position the print head
to the same position on the line */
var paper = this.paper;
var line = "";
while (line.length < this.printerCol-8) {
line += " ";
}
while (line.length < this.printerCol) {
line += " ";
}
paper.lastChild.nodeValue += "\n\f"; // newline + formfeed
paper.appendChild(this.doc.createTextNode(line));
this.printerLine = 0;
this.printerEOP.scrollIntoView();
};
/**************************************/
B220ConsolePrinter.prototype.resizeWindow = function resizeWindow(ev) {
/* Handles the window onresize event by scrolling the "paper" so it remains at the end */
this.printerEOP.scrollIntoView();
};
/**************************************/
B220ConsolePrinter.prototype.copyPaper = function copyPaper(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.paper.textContent;
var title = "B220 " + this.mnemonic + " Text Snapshot";
B220Util.openPopup(this.window, "./B220FramePaper.html", "",
"scrollbars,resizable,width=500,height=500",
this, function(ev) {
var doc = ev.target;
var win = doc.defaultView;
doc.title = title;
win.moveTo((screen.availWidth-win.outerWidth)/2, (screen.availHeight-win.outerHeight)/2);
doc.getElementById("Paper").textContent = text;
});
this.emptyPaper();
ev.preventDefault();
ev.stopPropagation();
};
/**************************************/
B220ConsolePrinter.prototype.button_Click = function button_Click(ev) {
/* Handler for button clicks */
switch (ev.target.id) {
case "LineFeedBtn":
this.printNewLine();
break;
case "CarriageReturnBtn":
if (this.printerCol > 0) {
this.printNewLine();
}
break;
case "OpenPanelBtn":
ev.target.disabled = true;
this.$$("FormatControlsDiv").style.display = "block";
break;
case "ClosePanelBtn":
this.$$("OpenPanelBtn").disabled = false;
this.$$("FormatControlsDiv").style.display = "none";
break;
} // switch ev.target.id
ev.preventDefault();
ev.stopPropagation();
};
/**************************************/
B220ConsolePrinter.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 "ZeroSuppressSwitch":
this.zeroSuppressSwitch.flip();
prefs.zeroSuppress = this.zeroSuppress = this.zeroSuppressSwitch.state;
break;
case "MapMemorySwitch":
this.mapMemorySwitch.flip();
prefs.mapMemory = this.mapMemory = this.mapMemorySwitch.state;
break;
case "RemoteKnob":
this.remoteKnob.step();
prefs.remote = this.remoteKnob.position;
this.ready = (this.remoteKnob.position != 0);
break;
case "FormatKnob":
this.formatKnob.step();
prefs.format = this.formatKnob.position;
this.format = this.formatKnob.position;
break;
case "SpeedSwitch":
this.speedSwitch.flip();
prefs.printerSpeed = this.speedSwitch.state;
if (this.speedSwitch.state) {
this.charPeriod = 1000/B220ConsolePrinter.whippetSpeed;
this.newLinePeriod = B220ConsolePrinter.whippetNewLine;
} else {
this.charPeriod = 1000/B220ConsolePrinter.ttySpeed;
this.newLinePeriod = B220ConsolePrinter.ttyNewLine;
}
break;
default:
x = id.indexOf("UnitSwitch");
if (x == 0) {
x = parseInt(id.substring(10), 10);
if (!isNaN(x)) {
this.unitSwitch[x].flip();
this.unitMask ^= B220Processor.pow2[x];
prefs.unitMask = this.unitMask;
}
}
break;
}
this.config.putNode("ConsoleOutput.units", prefs, this.unitIndex);
ev.preventDefault();
ev.stopPropagation();
};
/**************************************/
B220ConsolePrinter.prototype.text_OnChange = function text_OnChange(ev) {
/* Handler for text onchange events */
var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
var text = ev.target.value;
var v = null;
switch (ev.target.id) {
case "Columns":
v = parseInt(text, 10);
if (!isNaN(v)) {
this.columns = v;
ev.target.value = text = v.toFixed();
prefs.columns = v;
}
break;
case "TabStops":
v = this.parseTabStops(prefs.tabs || "", this.window);
if (v !== null) {
this.tabStop = v;
ev.target.value = text = this.formatTabStops(v);
prefs.tabs = text;
}
break;
} // switch ev.target.id
this.config.putNode("ConsoleOutput.units", prefs, this.unitIndex);
ev.preventDefault();
ev.stopPropagation();
};
/**************************************/
B220ConsolePrinter.prototype.formatTabStops = function formatTabStops(tabStops) {
/* Formats the array "tabStops" of 0-relative tab stop positions as a comma-
delimited string of 1-relative numbers */
var s = (tabStops[0]+1).toString();
var x = 0;
for (x=1; x= 0) {
cols = text.split(",");
for (x=0; x 0) { // ignore empty fields
col = parseInt(raw, 10);
if (isNaN(col)) {
copacetic = false;
alertWin.alert("Tab stop #" + (x+1) + " (\"" + cols[x] + "\") is not numeric");
break; // out of for loop
} else if (col <= lastCol) {
copacetic = false;
alertWin.alert("Tab stop #" + (x+1) + " (\"" + col + "\") is out of sequence");
break; // out of for loop
} else {
lastCol = col;
tabStop.push(col-1);
}
}
} // for x
}
return (copacetic ? tabStop : null);
};
/**************************************/
B220ConsolePrinter.prototype.printerOnLoad = function printerOnLoad(ev) {
/* Initializes the Teletype printer window and user interface */
var body;
var id;
var mask;
var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
var tabStop = null;
var x;
this.doc = ev.target;
this.window = this.doc.defaultView;
this.doc.title = "retro-220 Printer - " + this.mnemonic;
this.paper = this.$$("Paper");
this.printerEOP = this.$$("EndOfPaper");
this.emptyPaper();
body = this.$$("FormatControlsDiv");
this.remoteKnob = new BlackControlKnob(body, null, null, "RemoteKnob",
prefs.remote, [20, -20]);
this.ready = (prefs.remote != 0);
this.zeroSuppressSwitch = new ToggleSwitch(body, null, null, "ZeroSuppressSwitch",
B220ConsolePrinter.offSwitchImage, B220ConsolePrinter.onSwitchImage);
this.zeroSuppressSwitch.set(prefs.zeroSuppress);
this.zeroSuppress = this.zeroSuppressSwitch.state;
this.mapMemorySwitch = new ToggleSwitch(body, null, null, "MapMemorySwitch",
B220ConsolePrinter.offSwitchImage, B220ConsolePrinter.onSwitchImage);
this.mapMemorySwitch.set(prefs.mapMemory);
this.mapMemory = this.mapMemorySwitch.state;
this.speedSwitch = new ToggleSwitch(body, null, null, "SpeedSwitch",
B220ConsolePrinter.offSwitchImage, B220ConsolePrinter.onSwitchImage);
this.speedSwitch.set(prefs.printerSpeed);
if (this.speedSwitch.state) {
this.charPeriod = 1000/B220ConsolePrinter.whippetSpeed;
this.newLinePeriod = B220ConsolePrinter.whippetNewLine;
} else {
this.charPeriod = 1000/B220ConsolePrinter.ttySpeed;
this.newLinePeriod = B220ConsolePrinter.ttyNewLine;
}
mask = 0x001;
this.unitMask = prefs.unitMask;
for (x=0; x