mirror of
https://github.com/pkimpel/retro-220.git
synced 2026-01-13 15:18:24 +00:00
1. Finish implementation of Cardatron input and output units. 2. Minor corrections to BAC-Assembler script and BALGOL-Generator transcription. 3. Apply band-select "punches" in column 1 of WINTER.PI.card assembler deck. 4. Implement striping on Console register displays to highlight the opcode (62) fields. 5. Implement List-Cards utility to read cards from CR1 and list them on LP2.
899 lines
37 KiB
JavaScript
899 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*24) + ",top=" + (unitIndex*24));
|
||
this.window.addEventListener("load",
|
||
B220Util.bindMethod(this, B220CardatronInput.prototype.readerOnLoad), 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" ("<22>")
|
||
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);
|
||
this.$$("CIFileSelector").addEventListener("change",
|
||
B220Util.bindMethod(this, B220CardatronInput.prototype.fileSelector_onChange));
|
||
this.$$("FormatColumn").addEventListener("change",
|
||
B220Util.bindMethod(this, B220CardatronInput.prototype.format_onChange));
|
||
this.$$("FormatSelect").addEventListener("change",
|
||
B220Util.bindMethod(this, B220CardatronInput.prototype.format_onChange));
|
||
this.$$("CIStartBtn").addEventListener("click",
|
||
B220Util.bindMethod(this, B220CardatronInput.prototype.CIStartBtn_onClick));
|
||
this.$$("CIStopBtn").addEventListener("click",
|
||
B220Util.bindMethod(this, B220CardatronInput.prototype.CIStopBtn_onClick));
|
||
this.$$("ClearBtn").addEventListener("click",
|
||
B220Util.bindMethod(this, B220CardatronInput.prototype.ClearBtn_onClick));
|
||
this.hopperBar.addEventListener("click",
|
||
B220Util.bindMethod(this, B220CardatronInput.prototype.CIHopperBar_onClick));
|
||
|
||
this.window.resizeBy(de.scrollWidth - this.window.innerWidth + 4, // kludge for right-padding/margin
|
||
de.scrollHeight - this.window.innerHeight);
|
||
};
|
||
|
||
/**************************************/
|
||
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);
|
||
this.window.close();
|
||
};
|