1
0
mirror of https://github.com/pkimpel/retro-b5500.git synced 2026-01-12 00:42:59 +00:00
pkimpel.retro-b5500/webUI/B5500CardPunch.js
Paul Kimpel 1f82a16aaf Commit retro-b5500 release 1.06:
1. Redesign and simplify delay and time deviation adjustment algorithm
in global SetCallback function; reinstate alpha parameter to control
adjustment decay rate.
2. Correct memory stores during tape drive backward read.
3. Compare tape image file extensions in case-insensitive manner.
4. Detect dropouts (no flux change) in magnetic tape images.
5. Suppress printed SPO greeting after power-on.
6. Disable (for now) the ConsolePanel lamp test.
7. Update stacker progress meter and stacker-full annunciator when
emptying a stacker.
8. Supply (benign) terminating semicolons in ConsolePanel method
declarations.
9. Optimize extraction of MSCW address during Character Mode exit.
10. Miscellaneous minor additions and enhancements to scripts in tools/
directory.
2020-01-03 20:50:28 -08:00

369 lines
14 KiB
JavaScript

/***********************************************************************
* retro-b5500/emulator B5500CardPunch.js
************************************************************************
* Copyright (c) 2013, Nigel Williams and Paul Kimpel.
* Licensed under the MIT License, see
* http://www.opensource.org/licenses/mit-license.php
************************************************************************
* B5500 Card Punch Peripheral Unit module.
*
* Defines a card punch peripheral unit type.
*
************************************************************************
* 2013-06-16 P.Kimpel
* Original version, from B5500CardReader.js & B5500DummyPrinter.js.
***********************************************************************/
"use strict";
/**************************************/
function B5500CardPunch(mnemonic, unitIndex, designate, statusChange, signal, options) {
/* Constructor for the CardPunch object */
this.mnemonic = mnemonic; // Unit mnemonic
this.unitIndex = unitIndex; // Ready-mask bit number
this.designate = designate; // IOD unit designate number
this.statusChange = statusChange; // external function to call for ready-status change
this.signal = signal; // external function to call for special signals (not used here)
this.timer = 0; // setCallback() token
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
this.useAlgolGlyphs = options.algolGlyphs; // format Unicode for special Algol chars
this.clear();
this.doc = null;
this.window = null;
this.stacker1 = null;
this.endOfStacker1 = null;
this.stacker2 = null;
this.endOfStacker2 = null;
B5500Util.openPopup(window, "../webUI/B5500CardPunch.html", mnemonic,
"location=no,scrollbars=no,resizable,width=560,height=204,left=0,top=220",
this, B5500CardPunch.prototype.punchOnload);
}
B5500CardPunch.prototype.cardsPerMinute = 300; // Punch speed
B5500CardPunch.prototype.msPerCard = 60000/B5500CardPunch.prototype.cardsPerMinute;
B5500CardPunch.prototype.maxScrollLines = 850; // Maximum punch stacker scrollback (stacker capacity)
B5500CardPunch.prototype.rtrimRex = /\s+$/g; // regular expression for right-trimming card text
/**************************************/
B5500CardPunch.prototype.$$ = function $$(e) {
return this.doc.getElementById(e);
};
/**************************************/
B5500CardPunch.prototype.clear = function clear() {
/* Initializes (and if necessary, creates) the punch unit state */
this.ready = false; // ready status
this.busy = false; // busy status
this.errorMask = 0; // error mask for finish()
this.finish = null; // external function to call for I/O completion
this.runoutArmed = false; // EOF button: armed state
this.stacker1Count = 0; // cards in stacker #1
this.stacker2Count = 0; // cards in stacker #2
};
/**************************************/
B5500CardPunch.prototype.emptyStacker = function emptyStacker(stacker) {
/* Empties the stacker of all text lines */
while (stacker.firstChild) {
stacker.removeChild(stacker.firstChild);
}
};
/**************************************/
B5500CardPunch.prototype.copyStacker = function copyStacker(ev) {
/* Copies the text contents of a "stacker" 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 stacker = ev.target;
var text = stacker.textContent;
var title = "B5500 " + this.mnemonic + " Stacker Snapshot";
B5500Util.openPopup(window, "./B5500FramePaper.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.emptyStacker(stacker);
if (stacker == this.stacker1) {
this.stacker1Count = 0;
this.$$("CPStacker1Bar").value = 0;
this.$$("CPStacker1Full").classList.remove("annunciatorLit");
} else if (stacker == this.stacker2) {
this.stacker2Count = 0;
this.$$("CPStacker2Bar").value = 0;
this.$$("CPStacker2Full").classList.remove("annunciatorLit");
}
ev.preventDefault();
ev.stopPropagation();
};
/**************************************/
B5500CardPunch.prototype.setPunchReady = function setPunchReady(ready) {
/* Controls the ready-state of the card punch */
if (ready && !this.ready) {
this.statusChange(1);
this.$$("CPStartBtn").classList.add("greenLit")
this.$$("CPNotReadyLight").classList.remove("whiteLit");
this.ready = true;
if (this.runoutArmed) {
if (this.stacker1Count || this.stacker2Count) {
if (this.window.confirm("Empty both " + this.mnemonic + " stackers?")) {
this.stacker1Count = this.stacker2Count = 0;
this.$$("CPStacker1Bar").value = 0;
this.$$("CPStacker1Full").classList.remove("annunciatorLit");
this.emptyStacker(this.stacker1);
this.$$("CPStacker2Bar").value = 0;
this.$$("CPStacker2Full").classList.remove("annunciatorLit");
this.emptyStacker(this.stacker2);
}
}
this.armRunout(false);
}
} else if (!ready && this.ready) {
this.statusChange(0);
this.$$("CPStartBtn").classList.remove("greenLit")
this.$$("CPNotReadyLight").classList.add("whiteLit");
this.ready = false;
}
};
/**************************************/
B5500CardPunch.prototype.setAlgolGlyphs = function setAlgolGlyphs(makeItPretty) {
/* Controls the display of Unicode glyphs for the special Algol characters */
if (makeItPretty) {
if (!this.useAlgolGlyphs) {
B5500Util.xlateDOMTreeText(this.stacker1, B5500Util.xlateASCIIToAlgol);
B5500Util.xlateDOMTreeText(this.stacker2, B5500Util.xlateASCIIToAlgol);
}
} else {
if (this.useAlgolGlyphs) {
B5500Util.xlateDOMTreeText(this.stacker1, B5500Util.xlateAlgolToASCII);
B5500Util.xlateDOMTreeText(this.stacker2, B5500Util.xlateAlgolToASCII);
}
}
this.$$("CPAlgolGlyphsCheck").checked = makeItPretty;
this.useAlgolGlyphs = makeItPretty;
};
/**************************************/
B5500CardPunch.prototype.appendLine = function appendLine(stacker, text) {
/* Appends a new <pre> element to the <iframe>, creating an empty text
node inside the new element */
stacker.appendChild(this.doc.createTextNode(text || "\xA0"));
};
/**************************************/
B5500CardPunch.prototype.armRunout = function armRunout(armed) {
/* Controls the arming/disarming of the EOF signal when starting with
an empty input stacker */
if (armed && !this.ready) {
this.$$("CPRunoutBtn").classList.add("redLit");
this.runoutArmed = true;
} else {
this.$$("CPRunoutBtn").classList.remove("redLit");
this.runoutArmed = false;
}
};
/**************************************/
B5500CardPunch.prototype.CPStartBtn_onclick = function CPStartBtn_onclick(ev) {
/* Handle the click event for the START button */
if (!this.ready) {
this.setPunchReady(true);
}
};
/**************************************/
B5500CardPunch.prototype.CPStopBtn_onclick = function CPStopBtn_onclick(ev) {
/* Handle the click event for the STOP button */
if (this.ready) {
this.setPunchReady(false);
} else if (this.runoutArmed) {
this.armRunout(false);
}
};
/**************************************/
B5500CardPunch.prototype.CPRunoutBtn_onclick = function CPRunoutBtn_onclick(ev) {
/* Handle the click event for the EOF button */
this.armRunout(!this.runoutArmed);
};
/**************************************/
B5500CardPunch.prototype.CPAlgolGlyphsCheck_onclick = function CPAlgolGlyphsCheck_onclick(ev) {
/* Handle the click event for the Algol Glyphs checkbox */
this.setAlgolGlyphs(ev.target.checked);
};
/**************************************/
B5500CardPunch.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;
};
/**************************************/
B5500CardPunch.prototype.punchOnload = function punchOnload(ev) {
/* Initializes the punch window and user interface */
var de;
this.doc = ev.target;
this.window = this.doc.defaultView;
de = this.doc.documentElement;
this.doc.title = "retro-B5500 Card Punch " + this.mnemonic;
this.stacker1Frame = this.$$("CPStacker1Frame");
this.stacker1 = this.stacker1Frame.contentDocument.getElementById("Paper");
this.endOfStacker1 = this.stacker1Frame.contentDocument.getElementById("EndOfPaper");
this.stacker2Frame = this.$$("CPStacker2Frame");
this.stacker2 = this.stacker2Frame.contentDocument.getElementById("Paper");
this.endOfStacker2 = this.stacker2Frame.contentDocument.getElementById("EndOfPaper");
this.setAlgolGlyphs(this.useAlgolGlyphs);
this.armRunout(false);
this.setPunchReady(true);
this.window.addEventListener("beforeunload",
B5500CardPunch.prototype.beforeUnload, false);
this.stacker1.addEventListener("dblclick",
B5500CardPunch.prototype.copyStacker.bind(this));
this.stacker2.addEventListener("dblclick",
B5500CardPunch.prototype.copyStacker.bind(this));
this.$$("CPStartBtn").addEventListener("click",
B5500CardPunch.prototype.CPStartBtn_onclick.bind(this), false);
this.$$("CPStopBtn").addEventListener("click",
B5500CardPunch.prototype.CPStopBtn_onclick.bind(this), false);
this.$$("CPRunoutBtn").addEventListener("click",
B5500CardPunch.prototype.CPRunoutBtn_onclick.bind(this), false);
this.$$("CPAlgolGlyphsCheck").addEventListener("click",
B5500CardPunch.prototype.CPAlgolGlyphsCheck_onclick.bind(this), false);
this.$$("CPStacker1Bar").max = this.maxScrollLines;
this.$$("CPStacker2Bar").max = this.maxScrollLines;
this.window.resizeBy(de.scrollWidth - this.window.innerWidth + 4, // kludge for right-padding/margin
de.scrollHeight - this.window.innerHeight);
};
/**************************************/
B5500CardPunch.prototype.read = function read(finish, buffer, length, mode, control) {
/* Initiates a read operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500CardPunch.prototype.space = function space(finish, length, control) {
/* Initiates a space operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500CardPunch.prototype.write = function write(finish, buffer, length, mode, control) {
/* Initiates a write operation on the unit */
var text;
this.errorMask = 0;
this.busy = true;
text = String.fromCharCode.apply(null, buffer.subarray(0, length)).replace(this.rtrimRex, '');
if (this.useAlgolGlyphs) {
text = B5500Util.xlateASCIIToAlgol(text);
}
//console.log("WRITE: L=" + length + ", M=" + mode + ", C=" + control + " : " + text);
if (control) {
this.appendLine(this.stacker2, text + "\n");
this.endOfStacker2.scrollIntoView();
this.$$("CPStacker2Bar").value = (++this.stacker2Count);
if (this.stacker2Count >= this.maxScrollLines) {
this.$$("CPStacker2Full").classList.add("annunciatorLit");
this.setPunchReady(false);
}
} else {
this.appendLine(this.stacker1, text + "\n");
this.endOfStacker1.scrollIntoView();
this.$$("CPStacker1Bar").value = (++this.stacker1Count);
if (this.stacker1Count >= this.maxScrollLines) {
this.$$("CPStacker1Full").classList.add("annunciatorLit");
this.setPunchReady(false);
}
}
this.timer = setCallback(this.mnemonic, this,
this.msPerCard + this.initiateStamp - performance.now(),
function writeDelay() {
this.busy = false;
finish(this.errorMask, length);
});
};
/**************************************/
B5500CardPunch.prototype.erase = function erase(finish, length) {
/* Initiates an erase operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500CardPunch.prototype.rewind = function rewind(finish) {
/* Initiates a rewind operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500CardPunch.prototype.readCheck = function readCheck(finish, length, control) {
/* Initiates a read check operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500CardPunch.prototype.readInterrogate = function readInterrogate(finish, control) {
/* Initiates a read interrogate operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500CardPunch.prototype.writeInterrogate = function writeInterrogate(finish, control) {
/* Initiates a write interrogate operation on the unit */
finish(0x04, 0); // report unit not ready
};
/**************************************/
B5500CardPunch.prototype.shutDown = function shutDown() {
/* Shuts down the device */
if (this.timer) {
clearCallback(this.timer);
}
this.window.removeEventListener("beforeunload", B5500CardPunch.prototype.beforeUnload, false);
this.window.close();
};