1
0
mirror of https://github.com/pkimpel/retro-220.git synced 2026-01-13 15:18:24 +00:00
pkimpel.retro-220/webUI/B220CardatronInput.js
Paul Kimpel 936aadf6ee Commit retro-220 emulator version 0.05:
1. Remove Application Cache mechanism (has been deprecated as a web standard).
2. Replace internal bindMethod() utility function with standard Javascript object.bind().
3. Automatically reset Digit Check Alarm when bits are corrected in registers.
4. Correct setting of Overflow Toggle in IFL.
5. Terminate magnetic tape data transfer if AST toggle gets reset.
6. Correct way Processor was released by magnetic tape TCU.
7. Correct construction of preface word in memory for mag tape MRR.
8. Correct determination of mag tape "remote" status to allow tape to be unloaded immediately after a rewind.
9. Fix bug in BCS detecting the switch setting.
10. Modify behavior of Reset/Transfer switch to allow recovery after a tape malfunction.
11. Correct formatting of HIGH lamp on Control Console.
12. Remove extraneous whitespace from B220FramePaper sub-window markup.
2018-01-12 08:40:49 -08:00

902 lines
37 KiB
JavaScript

/***********************************************************************
* retro-220/webUI B220CardatronInput.js
************************************************************************
* Copyright (c) 2017, Paul Kimpel.
* Licensed under the MIT License, see
* http://www.opensource.org/licenses/mit-license.php
************************************************************************
* Burroughs 220 Cardatron Input Unit module.
*
* Defines a card reader peripheral unit type.
*
************************************************************************
* 2017-05-19 P.Kimpel
* Original version, from retro-205 D205CardatronInput.js.
***********************************************************************/
"use strict";
/**************************************/
function B220CardatronInput(mnemonic, unitIndex, config) {
/* Constructor for the Cardatron Input object */
var h = 160; // window height
var left = 0; // (temporary window x-offset)
var tks = B220CardatronInput.trackSize;
var w = 560; // window width
var x;
this.config = config; // System configuration object
this.mnemonic = mnemonic; // Unit mnemonic
this.unitIndex = unitIndex; // Input unit number
this.timer = 0; // setCallback() token
this.clear();
// Buffer drum: information band is [0], format bands are [1]-[6], [7] is a dummy for indexing only
this.bufferDrum = new ArrayBuffer(tks*8);
this.info = new Uint8Array(this.bufferDrum, 0, tks); // information band
this.formatBand = [
null, // no format band 0
new Uint8Array(this.bufferDrum, tks*1, tks), // format band 1
new Uint8Array(this.bufferDrum, tks*2, tks), // format band 2
new Uint8Array(this.bufferDrum, tks*3, tks), // format band 3
new Uint8Array(this.bufferDrum, tks*4, tks), // format band 4
new Uint8Array(this.bufferDrum, tks*5, tks), // format band 5
new Uint8Array(this.bufferDrum, tks*6, tks), // format band 6 (fixed)
new Uint8Array(this.bufferDrum, tks*7, tks)]; // format band 7 (dummy)
// Initialize format band 6 for all-numeric transfer
// (note that ArrayBuffer storage is initialized to zero, so band[160..233] == 0)
for (x=0; x<160; x+=2) {
this.formatBand[6][x] = 1;
this.formatBand[6][x+1] = 3;
}
for (x=234; x<this.info.length; ++x) {
this.formatBand[6][x] = 3;
}
this.doc = null;
this.hopperBar = null;
this.outHopperFrame = null;
this.outHopper = null;
this.formatCol = 0; // current format-determination column
this.formatColumnList = null;
this.formatSelect = 0; // current format selection
this.formatSelectList = null;
this.window = window.open("../webUI/B220CardatronInput.html", mnemonic,
"location=no,scrollbars,resizable,width=" + w + ",height=" + h +
",left=" + ((unitIndex-1)*32) +
",top=" + (screen.availHeight - h - (unitIndex-1)*32));
this.window.addEventListener("load",
B220CardatronInput.prototype.readerOnLoad.bind(this), false);
}
/**************************************/
B220CardatronInput.prototype.eolRex = /([^\n\r\f]*)((:?\r[\n\f]?)|\n|\f)?/g;
B220CardatronInput.prototype.cardsPerMinute = 240; // IBM Type 087/089 collator
B220CardatronInput.prototype.eodBias = -0x900000000000; // signals end-of-data to Processor
B220CardatronInput.trackSize = 319; // digits
B220CardatronInput.drumTransferTime = 60000/21600;
// one drum rotation, about 2.78 ms
// Filter ASCII character values to buffer drum info band zone/numeric digits.
// See U.S. Patent 3,000,556, September 16, 1961, L.L. Bewley et al, Figure 2.
B220CardatronInput.prototype.cardFilter = [
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 00-0F
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 10-1F
0x00,0x60,0x07,0x0B,0x2B,0x4C,0x10,0x0C,0x4C,0x1C,0x2C,0x10,0x4B,0x20,0x1B,0x41, // 20-2F
0x40,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x02,0x2B,0x1C,0x0B,0x00,0x00, // 30-3F
0x0C,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x21,0x22,0x23,0x24,0x25,0x26, // 40-4F
0x27,0x28,0x29,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x00,0x00,0x00,0x00,0x00, // 50-5F
0x01,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x21,0x22,0x23,0x24,0x25,0x26, // 60-6F
0x27,0x28,0x29,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x50,0x05,0x06,0x07,0x00]; // 70-7F
// Translate buffer zone digits to internal zone decades.
// Each row is indexed by the zone digit from the buffer drum info band;
// each column is indexed by the PREVIOUS numeric digit from the info band.
// See U.S. Patent 3,000,556, September 16, 1961, L.L. Bewley et al, Figure 9.
B220CardatronInput.prototype.zoneXlate = [
//0 1 2 3 4 5 6 7 8 9 10 11 12
[0, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 3, 3], // zone digit 0
[1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0], // zone digit 1
[2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 1, 1], // zone digit 2
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // there no zone=3 digits
[8, 2, 6, 6, 6, 6, 6, 6, 6, 6, 0, 2, 2], // zone digit 4
[4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // zone digit 5
[5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // zone digit 6
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // there no zone=7 digits
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // there no zone=8 digits
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]; // there no zone=9 digits
/**************************************/
B220CardatronInput.prototype.clear = function clear() {
/* Initializes (and if necessary, creates) the reader unit state */
this.ready = false; // ready status
this.bufferReady = false; // buffer drum info band is ready to send data to Processor
this.noFormatAlarm = false; // No Formal Alarm toggle
this.reloadLockout = false; // Reload Lockout toggle
this.formatLockout = false; // Format Lockout toggle
this.readRequested = false; // Processor has initiated a read, waiting for buffer
this.togNumeric = false; // current digit came from zone (false) or numeric (true) punches
this.buffer = ""; // card reader "input hopper"
this.bufLength = 0; // current input buffer length (characters)
this.bufIndex = 0; // 0-relative offset to next "card" to be read
this.digitCount = 0; // digit within word being returned (sign=10)
this.infoIndex = 0; // 0-relative offset into info band on drum
this.lastNumericDigit = 0; // last numeric digit encountered
this.pendingInputWord = 0; // partially-accumulated input word
this.rDigit = 0; // stashed reload-lockout/format band number
this.selectedFormat = 0; // currently-selected format band
this.pendingCall = null; // stashed pending function reference
this.pendingParams = null; // stashed pending function parameters
this.pendingFinish = null; // stashed pending signalFinish() reference
};
/**************************************/
B220CardatronInput.prototype.$$ = function $$(e) {
return this.doc.getElementById(e);
};
/**************************************/
B220CardatronInput.prototype.setFormatColumn = function setFormatColumn(index) {
/* Determines the zero-relative format-selection column from the selectedIndex
of the FormatColumn select list and sets this.formatCol */
if (index < 0) {
this.formatCol = 0; // treat unselected as column 1
} else if (index == 0) {
this.formatCol = 79; // column 80
} else if (index < 80) {
this.formatCol = index-1; // columns 1-79
} else {
this.formatCol = 0; // treat everything else as col 1
}
};
/**************************************/
B220CardatronInput.prototype.clearInfoBand = function clearInfoBand() {
/* Clears the entire info band to zeroes */
var x;
this.infoIndex = 0; // restart at the beginning of the format band
for (x=this.info.length-1; x>=0; --x) {
this.info[x] = 0;
}
};
/**************************************/
B220CardatronInput.prototype.setReaderReady = function setReaderReady(ready) {
/* Controls the ready-state of the card reader */
this.$$("CIFileSelector").disabled = ready;
if (ready && !this.ready) {
B220Util.addClass(this.$$("CIStartBtn"), "greenLit")
B220Util.removeClass(this.$$("CIStopBtn"), "redLit");
this.ready = true;
} else if (this.ready && !ready) {
B220Util.removeClass(this.$$("CIStartBtn"), "greenLit")
B220Util.addClass(this.$$("CIStopBtn"), "redLit");
this.ready = false;
}
};
/**************************************/
B220CardatronInput.prototype.setNoFormatAlarm = function setNoFormatAlarm(noFormat) {
/* Controls the state of the No Format alarm and lamp */
this.noFormatAlarm = noFormat;
if (noFormat) {
this.setReaderReady(false);
this.noFormatLamp.set(1);
} else {
this.noFormatLamp.set(0);
}
};
/**************************************/
B220CardatronInput.prototype.setReloadLockout = function setReloadLockout(lockout) {
/* Controls the state of the Reload Lockout (RLO) toggle and lamp */
this.reloadLockout = lockout;
if (lockout) {
this.reloadLockoutLamp.set(1);
} else {
this.bufferReady = false;
this.setFormatLockout(false);
this.reloadLockoutLamp.set(0);
}
};
/**************************************/
B220CardatronInput.prototype.setFormatLockout = function setFormatLockout(lockout) {
/* Controls the state of the Format Lockout (FLO) toggle and lamp.
Setting Format Lockout implicitly sets Reload Lockout */
this.formatLockout = lockout;
if (lockout) {
this.setReloadLockout(true);
this.formatLockoutLamp.set(1);
} else {
this.formatLockoutLamp.set(0);
}
};
/**************************************/
B220CardatronInput.prototype.setFormatSelectLamps = function setFormatSelectLamps(format) {
/* Sets the FS lamps on the panel from the low-order three bits of "format" */
this.formatSelect1Lamp.set(format & 0x01);
this.formatSelect2Lamp.set((format >>> 1) & 0x01);
this.formatSelect4Lamp.set((format >>> 2) & 0x01);
};
/**************************************/
B220CardatronInput.prototype.CIStartBtn_onClick = function CIStartBtn_onClick(ev) {
/* Handle the click event for the START button */
if (!this.ready) {
this.setNoFormatAlarm(false);
if (this.bufIndex < this.bufLength) {
this.setReaderReady(true);
if (!this.bufferReady) {
this.initiateCardRead();
}
}
}
};
/**************************************/
B220CardatronInput.prototype.CIStopBtn_onClick = function CIStopBtn_onClick(ev) {
/* Handle the click event for the STOP button */
this.$$("CIFileSelector").value = null; // reset the control so the same file can be reloaded
if (this.ready) {
this.setReaderReady(false);
this.startMachineLamp.set(0);
}
};
/**************************************/
B220CardatronInput.prototype.ClearBtn_onClick = function ClearBtn_onClick(ev) {
/* Handle the click event for the CLEAR button */
this.clearUnit();
};
/**************************************/
B220CardatronInput.prototype.CIHopperBar_onClick = function CIHopperBar_onClick(ev) {
/* Handle the click event for the "input hopper" meter bar */
if (this.bufIndex < this.bufLength && !this.ready) {
if (this.window.confirm((this.bufLength-this.bufIndex).toString() + " of " + this.bufLength.toString() +
" characters remaining to read.\nDo you want to clear the input hopper?")) {
this.buffer = "";
this.bufLength = 0;
this.bufIndex = 0;
this.hopperBar.value = 0;
this.$$("CIFileSelector").value = null; // reset the control
while (this.outHopper.childNodes.length > 0) {
this.outHopper.removeChild(this.outHopper.firstChild);
}
}
}
};
/**************************************/
B220CardatronInput.prototype.fileSelector_onChange = function fileSelector_onChange(ev) {
/* Handle the <input type=file> onchange event when files are selected. For each
file, load it and add it to the "input hopper" of the reader */
var deck;
var f = ev.target.files;
var that = this;
var x;
function fileLoader_onLoad(ev) {
/* Handle the onLoad event for a Text FileReader */
if (that.bufIndex >= that.bufLength) {
that.buffer = ev.target.result;
} else {
switch (that.buffer.charAt(that.buffer.length-1)) {
case "\r":
case "\n":
case "\f":
break; // do nothing -- the last card has a delimiter
default:
that.buffer += "\n"; // so the next deck starts on a new line
break;
}
that.buffer = that.buffer.substring(that.bufIndex) + ev.target.result;
}
that.bufIndex = 0;
that.bufLength = that.buffer.length;
that.$$("CIHopperBar").value = that.bufLength;
that.$$("CIHopperBar").max = that.bufLength;
}
for (x=f.length-1; x>=0; x--) {
deck = new FileReader();
deck.onload = fileLoader_onLoad;
deck.readAsText(f[x]);
}
};
/**************************************/
B220CardatronInput.prototype.format_onChange = function format_onChange(ev) {
/* Event handler for changes to the FormatColumn and FormatSelect lists */
var prefs = this.config.getNode("Cardatron.units", this.unitIndex);
var x;
switch (ev.target.id) {
case "FormatColumn":
x = this.formatColumnList.selectedIndex;
this.setFormatColumn(x);
prefs.formatCol = x;
this.config.putNode("Cardatron.units", prefs, this.unitIndex);
break;
case "FormatSelect":
x = this.formatSelectList.selectedIndex;
prefs.formatSelect = this.formatSelect = x;
this.config.putNode("Cardatron.units", prefs, this.unitIndex);
break;
}
};
/**************************************/
B220CardatronInput.prototype.readCardImage = function readCardImage() {
/* Reads one card image from the buffer; pads or trims the image as necessary
to 80 characters. Detects empty buffer (hopper) and sets the reader not ready
after reading the last card. Updates the progress bar. Returns the raw ASCII
card image as a string */
var card; // card image
var match; // result of eolRex.exec()
this.eolRex.lastIndex = this.bufIndex;
match = this.eolRex.exec(this.buffer);
if (!match) {
card = "";
} else {
this.bufIndex += match[0].length;
card = match[1].toUpperCase();
}
if (card.length > 80) {
card = card.substring(0, 80);
} else {
while (card.length <= 70) {
card += " "; // pad with spaces
}
while (card.length < 80) {
card += " ";
}
}
if (this.bufIndex < this.bufLength) {
this.hopperBar.value = this.bufLength-this.bufIndex;
} else {
this.hopperBar.value = 0;
this.buffer = ""; // discard the input buffer
this.bufLength = 0;
this.bufIndex = 0;
this.setReaderReady(false);
this.$$("CIFileSelector").value = null; // reset the control so the same file can be reloaded
}
while (this.outHopper.childNodes.length > 1) {
this.outHopper.removeChild(this.outHopper.firstChild);
}
this.outHopper.appendChild(this.doc.createTextNode("\n"));
this.outHopper.appendChild(this.doc.createTextNode(card));
return card;
};
/**************************************/
B220CardatronInput.prototype.determineFormatBand = function determineFormatBand(card) {
/* Determines the format band number to be applied to the current card.
Returns the format band number or zero if No Format Alarm is set */
var c; // selected column character code
var format; // selected format band
// Check for format override on the reader panel
format = this.formatSelect;
if (format > 6) {
format = 0;
this.setNoFormatAlarm(true);
} else if (format <= 0) {
// No format override, so determine format from a card column
c = card.charAt(this.formatCol);
switch (c) {
case "1":
format = 1;
break;
case "2":
format = 2;
break;
case "3":
format = 3;
break;
case "4":
format = 4;
break;
case "5":
format = 5;
break;
case "6":
format = 6;
break;
case "7":
format = 7;
break;
case "`": // 1-8 punch
format = 1;
this.setFormatLockout(true);
break;
case ":": // 2-8 punch
format = 2;
this.setFormatLockout(true);
break;
case "#": // 3-8 punch
format = 3;
this.setFormatLockout(true);
break;
case "@": // 4-8 punch
format = 4;
this.setFormatLockout(true);
break;
case "'": // 5-8 punch
case "|": // translates to a 5-numeric digit
format = 5;
this.setFormatLockout(true);
break;
case "=": // 6-8 punch
case "}": // translates to a 6-numeric digit
format = 6;
this.setFormatLockout(true);
break;
case "\"": // 7-8 punch -- reject plus lockout
case "~": // translates to a 7-numeric digit
format = 7+8;
this.setFormatLockout(true);
break;
default:
format = 0;
this.setNoFormatAlarm(true);
break;
} // switch c
}
this.setFormatSelectLamps(format);
return format;
};
/**************************************/
B220CardatronInput.prototype.finishCardRead = function finishCardRead() {
/* Processes a card image after the delay to read the card. Establishes the
format band to be used and encodes the data from the card image onto the
info band of the buffer drum per Figure 2 in US Patent 3,000,556 */
var band; // local copy of selected format band
var c; // current character code
var card = this.readCardImage(); // the card image in ASCII
var col; // current card column
var format; // selected format band
var fmax; // length of format band
var info = this.info; // local copy of info band on buffer drum
var nu; // numeric (true), zone (false) toggle
var x; // info/format band digit index
this.startMachineLamp.set(0);
format = this.determineFormatBand(card);
if ((format & 0x07) == 7) {
// Reject format -- clear the information band and read next card
this.selectedFormat = 7;
// If reject+lockout was imposed by a 7-8 punch, read next card and lock out its successor
if ((format & 0x08) || !this.reloadLockout) {
this.initiateCardRead();
}
} else if (format > 0) {
this.selectedFormat = format;
band = this.formatBand[format];
fmax = band.length;
col = card.length-1; // start with last column on card
nu = true; // start with the numeric digit
for (x=0; x<fmax; ++x) {
switch (band[x]) {
case 0:
info[x] = 0;
break;
case 2:
info[x] = 2;
break;
default:
if (nu) {
c = card.charCodeAt(col); // translate char to buffer code
if (c < 0x80) {
c = this.cardFilter[c];
} else if (c == 0xA4) { // the "lozenge" ("¤")
c = this.cardFilter[0x3C]; // use the code for "<"
} else {
c = 0;
}
info[x] = c & 0x0F; // take the numeric half
nu = false; // next will be the zone digit
} else {
info[x] = (c >>> 4) & 0x0F; // take the zone half
nu = true; // next will be the numeric digit
if (col > 0) { // advance to next card column
--col;
}
}
} // switch
} // for x
this.bufferReady = true;
if (this.readRequested) { // fire up any pending read from Processor
this.readRequested = false;
this.pendingCall.apply(this, this.pendingParams);
this.pendingCall = null;
}
}
};
/**************************************/
B220CardatronInput.prototype.initiateCardRead = function initiateCardRead() {
/* Initiates the read of the next card into the buffer drum */
if (this.ready) {
this.startMachineLamp.set(1);
this.setNoFormatAlarm(false);
this.clearInfoBand();
this.timer = setCallback(this.mnemonic, this,
60000/this.cardsPerMinute, this.finishCardRead);
}
};
/**************************************/
B220CardatronInput.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;
};
/**************************************/
B220CardatronInput.prototype.readerOnLoad = function readerOnLoad() {
/* Initializes the reader window and user interface */
var body;
var de;
var prefs = this.config.getNode("Cardatron.units", this.unitIndex);
this.doc = this.window.document;
de = this.doc.documentElement;
this.doc.title = "retro-220 Cardatron Reader " + this.mnemonic;
body = this.$$("CIDiv");
this.hopperBar = this.$$("CIHopperBar");
this.outHopperFrame = this.$$("CIOutHopperFrame");
this.outHopper = this.outHopperFrame.contentDocument.getElementById("Paper");
this.formatColumnList = this.$$("FormatColumn");
this.formatColumnList.selectedIndex = prefs.formatCol;
this.setFormatColumn(prefs.formatCol);
this.formatSelectList = this.$$("FormatSelect");
this.formatSelectList.selectedIndex = this.formatSelect = prefs.formatSelect;
this.noFormatLamp = new ColoredLamp(body, null, null, "NoFormatLamp", "redLamp", "redLit");
this.noFormatLamp.setCaption("NO FORMAT", true);
this.startMachineLamp = new NeonLamp(body, null, null, "StartMachineLamp");
this.startMachineLamp.setCaption("SM", true);
this.reloadLockoutLamp = new NeonLamp(body, null, null, "ReloadLockoutLamp");
this.reloadLockoutLamp.setCaption("RLO", true);
this.formatLockoutLamp = new NeonLamp(body, null, null, "FormatLockoutLamp");
this.formatLockoutLamp.setCaption("FLO", true);
this.formatSelect1Lamp = new NeonLamp(body, null, null, "FormatSelect1Lamp");
this.formatSelect1Lamp.setCaption("FS1", true);
this.formatSelect2Lamp = new NeonLamp(body, null, null, "FormatSelect2Lamp");
this.formatSelect2Lamp.setCaption("FS2", true);
this.formatSelect4Lamp = new NeonLamp(body, null, null, "FormatSelect4Lamp");
this.formatSelect4Lamp.setCaption("FS4", true);
this.ready = true; // so that setReaderReady called from clearUnit
this.clearUnit(); // will actually set the state and lamps correctly
this.window.addEventListener("beforeunload",
B220CardatronInput.prototype.beforeUnload, false);
this.$$("CIFileSelector").addEventListener("change",
B220CardatronInput.prototype.fileSelector_onChange.bind(this), false);
this.$$("FormatColumn").addEventListener("change",
B220CardatronInput.prototype.format_onChange.bind(this), false);
this.$$("FormatSelect").addEventListener("change",
B220CardatronInput.prototype.format_onChange.bind(this), false);
this.$$("CIStartBtn").addEventListener("click",
B220CardatronInput.prototype.CIStartBtn_onClick.bind(this), false);
this.$$("CIStopBtn").addEventListener("click",
B220CardatronInput.prototype.CIStopBtn_onClick.bind(this), false);
this.$$("ClearBtn").addEventListener("click",
B220CardatronInput.prototype.ClearBtn_onClick.bind(this), false);
this.hopperBar.addEventListener("click",
B220CardatronInput.prototype.CIHopperBar_onClick.bind(this), false);
this.window.resizeBy(de.scrollWidth - this.window.innerWidth + 4, // kludge for right-padding/margin
de.scrollHeight - this.window.innerHeight);
this.window.moveTo(0, screen.availHeight - this.window.outerHeight - (this.unitIndex-1)*32);
};
/**************************************/
B220CardatronInput.prototype.inputWord = function inputWord(wordReceiver) {
/* Reads the next word of digits from the info band of the buffer drum,
translating the digits to Datatron Processor code and sending the word to the
Processor via the wordReceiver callback function. If at end of band, returns any
partially-accumulated word minus 0x900000000000 to signal end of I/O. Note that
sign digits must be translated from zone digits specially. Also note that a
completed word is not returned until the next digit is obtained from the info band */
var band; // local copy of format band
var d; // result digit
var eod; // finished with current digit
var eow = false; // finished with word
var info = this.info; // local reference to info band
var ix = this.infoIndex; // current info/format band index
var lastNumeric = this.lastNumericDigit;
var nu = this.togNumeric; // numeric/zone digit toggle
var word = this.pendingInputWord; // word being assembled
var x = this.digitCount; // word-digit index
band = this.formatBand[this.selectedFormat];
do {
eod = false;
do {
if (ix >= info.length) {
// At end of info band -- finish the I/O
d = 0;
word = word + this.eodBias; // flag this as the final word of the I/O
eow = eod = true;
} else {
// Translate or delete the current digit
switch (band[ix]) {
case 0: // insert 0 digit
d = 0;
++ix;
eod = true;
break;
case 1: // translate zone/numeric digit
d = info[ix];
if (nu) {
// Numeric digit: straight translation except for 3-8 and 4-8 punches
nu = false; // next is a zone digit
lastNumeric = d;
if (d > 9) {
d -= 8;
}
} else {
// Zone digit: requires special handling in the sign-digit position
nu = true; // next is a numeric digit
d = this.zoneXlate[d][lastNumeric];
if (x == 10) {
d &= 0x0B; // zero the 4-bit in the sign digit
}
}
++ix;
eod = true;
break;
case 2: // insert 2 digit
d = 2;
++ix;
eod = true;
break;
default: // (3) delete the digit
if (nu) {
nu = false; // next is a zone digit
lastNumeric = info[ix];
} else {
nu = true; // next is a numeric digit
}
++ix;
// We are not yet done producing the next digit...
break;
} // switch band[ix]
}
} while (!eod);
if (x < 11) {
// Increment the digit index and shift this digit into the word
++x;
word = word/0x10 + d*0x10000000000;
} else {
// Word has overflowed -- send the word to the Processor and save
// this digit for the next word
eow = true;
this.pendingInputWord = d*0x10000000000;
this.digitCount = 1;
}
} while (!eow);
this.lastNumericDigit = lastNumeric;
this.togNumeric = nu;
// Send the word to the Processor
if (wordReceiver(word) < 0) {
this.infoIndex = info.length; // stop the I/O
} else {
this.infoIndex = ix;
}
};
/**************************************/
B220CardatronInput.prototype.inputStop = function inputStop() {
/* Terminates data transfer from the input unit and releases the card */
this.setFormatSelectLamps(0);
if (this.rDigit % 2) { // set reload-lockout
if (!this.reloadLockout) {
this.setReloadLockout(true);
}
} else if (this.reloadLockout) { // reset reload-lockout
this.setReloadLockout(false);
}
if (!this.reloadLockout) {
this.bufferReady = false;
this.initiateCardRead();
}
};
/**************************************/
B220CardatronInput.prototype.inputTransfer = function inputTransfer(wordReceiver) {
/* Driver for sending words assembled from the info band to the Processor */
while (this.infoIndex < this.info.length) {
this.inputWord(wordReceiver);
}
this.inputStop();
};
/**************************************/
B220CardatronInput.prototype.inputInitiate = function inputInitiate(rDigit, wordReceiver) {
/* Initiates a read against the buffer drum on this unit. rDigit is the
(41) numeric digit from the instruction word, with odd values indicating
reload-lockout should be imposed at the end of the read. wordReceiver is the
callback function that will receive one word at a time for return to the
Processor. If the buffer is not ready, simply sets the readRequested flag
and exits after stashing rDigit and the wordReceiver callback */
if (!this.bufferReady) {
this.readRequested = true; // wait for the buffer to be filled
this.pendingCall = inputInitiate;
this.pendingParams = [rDigit, wordReceiver];
if (!this.ready) {
this.window.focus();
}
} else {
this.rDigit = rDigit;
this.infoIndex = 0; // start at the beginning of the info band
this.digitCount = 0;
this.pendingInputWord = 0;
this.togNumeric = true;
this.lastNumericDigit = 0;
setCallback(this.mnemonic, this, B220CardatronInput.drumTransferTime*(Math.random()+1),
this.inputTransfer, wordReceiver);
}
};
/**************************************/
B220CardatronInput.prototype.inputReadyInterrogate = function inputReadyInterrogate() {
/* Returns the current ready status of the input unit */
return this.bufferReady;
};
/**************************************/
B220CardatronInput.prototype.inputFormatTransfer = function inputFormatTransfer(requestNextWord) {
/* Receives input format band words from the Processor and stores the
digits from each word into the next 11 format band digits */
var band = this.formatBand[this.selectedFormat];
var d; // current format digit
var ix = 0; // current format band digit index
var word; // band word from Processor
var x; // word-digit index
while (ix < B220CardatronInput.trackSize) {
word = requestNextWord();
if (word < 0) { // transfer terminated
ix = B220CardatronInput.tracksize;
} else {
for (x=0; x<11; ++x) {
d = word % 0x10;
word = (word-d)/0x10;
if (ix < B220CardatronInput.trackSize) {
band[ix++] = d % 4;
} else {
break; // out of for loop
}
} // for x
}
}
this.pendingFinish(); // call signalFinished();
this.pendingFinish = null;
this.inputStop();
};
/**************************************/
B220CardatronInput.prototype.inputFormatInitiate = function inputFormatInitiate(
rDigit, requestNextWord, signalFinished) {
/* Initiates the loading of a format band on this unit. rDigit is the
(41) numeric digit from the instruction word, with odd values indicating
reload-lockout should be imposed at the end of the read and the remaining
three bits indicating the format band to be loaded. requestNextWord is the
callback function that will trigger the Processor to send the next word.
signalFinished is the callback function that will signal the Processor to
terminate the I/O */
if (rDigit > 9) {
signalFinished();
} else {
this.rDigit = rDigit;
this.selectedFormat = ((rDigit >>> 1) & 0x07) + 1;
this.pendingFinish = signalFinished; // stash the call-back function
this.setFormatSelectLamps(this.selectedFormat);
setCallback(this.mnemonic, this, B220CardatronInput.drumTransferTime*(Math.random()+2),
this.inputFormatTransfer, requestNextWord);
}
};
/**************************************/
B220CardatronInput.prototype.clearUnit = function clearUnit() {
/* Clears the input unit and resets all internal state */
this.$$("CIFileSelector").value = null; // reset the control so the same file can be reloaded
this.bufferReady = false;
this.setReaderReady(false);
this.setNoFormatAlarm(false);
this.setReloadLockout(false);
this.setFormatLockout(false);
this.startMachineLamp.set(0);
this.setFormatSelectLamps(7);
// If there is a pending read, confirm that this.pendingParams[1] is a
// function and call it with the end-of-data signal. We assume it's the
// Processor's wordReceiver function. This will prevent the Processor
// from hanging on an I/O to a cleared input unit.
if (this.readRequested) {
if (Object.prototype.toString.call(this.pendingParams) === "[object Array]") {
if (Object.prototype.toString.call(this.pendingParams[1]) === "[object Function]") {
this.pendingParams[1](this.eodBias);
}
}
}
this.clear();
if (this.timer) {
clearCallback(this.timer);
this.timer = 0;
}
};
/**************************************/
B220CardatronInput.prototype.shutDown = function shutDown() {
/* Shuts down the device */
if (this.timer) {
clearCallback(this.timer);
}
this.window.removeEventListener("beforeunload", B220CardatronInput.prototype.beforeUnload, false);
this.window.close();
};