mirror of
https://github.com/pkimpel/retro-220.git
synced 2026-01-13 07:10:08 +00:00
. Adjust (hopefully for the last time) the RGB code for the 220 Sundland Beige panel color: it's now #C5B1A0. This has been a really difficult color to pin down. . Implement Clear Memory button on Console; implement "hover captions" for the Clear Memory and Load Card buttons. . Implement current block-number display on magnetic tape panel. . Implement "word index" counter on the B220MagTapeDrive panel to indicate the tape position in terms of 11-digit 220 words. . Implement Rewind and Unload buttons on the paper-tape reader. . Implement "word index" counter on the B220PaperTapeReader panel. . When a Cardatron or paper-tape reader encounters a sign=6 control word, do not reschedule the Processor if it has been halted. . Correct handling of reload-lockout in B220CardatronInput; change method of reporting Cardatron end-of-I/O signal to the Processor. . Correct this.pendingFinish timing race in B220CardatronOutput. . Correct validation when loading tape images in B220MagTapeDrive so that the drive will continue to be usable after an invalid image is detected. . Correct logic for spacing and searching magnetic tape blocks backwards. . Correct output of non-printing characters in B220PaperTapePunch. . Correct handling of invalid tape image characters in B220PaperTapeReader. . Implement additional tracing for paper-tape and magnetic tape I/Os (currently disabled).
332 lines
13 KiB
JavaScript
332 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 + 264;
|
|
var left = (unitIndex-1)*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.boundFlipSwitch = B220PaperTapePunch.prototype.flipSwitch.bind(this);
|
|
this.boundReceiveSign = B220PaperTapePunch.prototype.receiveSign.bind(this);
|
|
this.boundReceiveChar = B220PaperTapePunch.prototype.receiveChar.bind(this);
|
|
|
|
this.clear();
|
|
|
|
// Create the punch window and onload event
|
|
this.doc = null;
|
|
this.window = null;
|
|
this.punchTape = null;
|
|
this.punchEOP = null;
|
|
B220Util.openPopup(window, "../webUI/B220PaperTapePunch.html", mnemonic,
|
|
"location=no,scrollbars=no,resizable,width=240,height=160," +
|
|
"left=" + left + ",top=" + top,
|
|
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 (16) translates to "|". To avoid space-expansion
|
|
// of tabs (26), they are translated to "~". The 02 "blank" code is "_".
|
|
// Form-feed (15) translates to "^".
|
|
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
|
|
" ", "?", "_", ".", "<", ">", "\\","?", "?", "?", "!", "!", "!", "!", "!", "!", // 00-0F
|
|
"&", "?", "]", "$", "*", "^", "|", "?", "?", "?", "!", "!", "!", "!", "!", "!", // 10-1F
|
|
"-", "/", "{", ",", "%", "}", "~", ":", "?", "?", "!", "!", "!", "!", "!", "!", // 20-2F
|
|
"?", "?", "[", "#", "@", "\n","\"","`", "?", "?", "!", "!", "!", "!", "!", "!", // 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
|
|
|
|
|
|
/**************************************/
|
|
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.$$ = 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));
|
|
this.punchEOP.scrollIntoView();
|
|
};
|
|
|
|
/**************************************/
|
|
B220PaperTapePunch.prototype.punchChar = function punchChar(code) {
|
|
/* Outputs the character "code" to the device */
|
|
var c = B220PaperTapePunch.codeXlate[code];
|
|
var line;
|
|
var len;
|
|
|
|
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.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.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";
|
|
|
|
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.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(ev) {
|
|
/* Initializes the Paper Tape Punch window and user interface */
|
|
var body;
|
|
var mask;
|
|
var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
|
|
var x;
|
|
|
|
this.doc = ev.target;
|
|
this.window = this.doc.defaultView;
|
|
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, false);
|
|
this.window.addEventListener("resize",
|
|
B220PaperTapePunch.prototype.resizeWindow.bind(this), false);
|
|
this.punchTape.addEventListener("dblclick",
|
|
B220PaperTapePunch.prototype.punchCopyTape.bind(this), false);
|
|
this.remoteSwitch.addEventListener("click", this.boundFlipSwitch);
|
|
this.unitDesignateKnob.addEventListener("change", this.boundFlipSwitch);
|
|
|
|
//this.punchWin.resizeBy(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;
|
|
}
|
|
|
|
this.nextCharTime += delay;
|
|
setCallback(this.mnemonic, this, this.nextCharTime-stamp, successor, this.boundReceiveChar);
|
|
};
|
|
|
|
/**************************************/
|
|
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
|
|
|
|
this.nextCharTime += delay;
|
|
setCallback(this.mnemonic, this, this.nextCharTime-stamp, successor, nextReceiver);
|
|
};
|
|
|
|
/**************************************/
|
|
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);
|
|
this.window.close();
|
|
this.window = null;
|
|
}
|
|
};
|