diff --git a/emulator/B220Processor.js b/emulator/B220Processor.js
index ecb2c64..08b064c 100644
--- a/emulator/B220Processor.js
+++ b/emulator/B220Processor.js
@@ -77,6 +77,7 @@ function B220Processor(config, devices) {
// Memory
this.memorySize = config.getNode("memorySize"); // memory size, words
+ this.bcdMemorySize = B220Processor.binaryBCD(this.memorySize);
this.MM = new Float64Array(this.memorySize); // main memory, 11-digit words
this.IB = new B220Processor.Register(11*4, this, true); // memory Input Buffer
@@ -108,6 +109,14 @@ function B220Processor(config, devices) {
this.R = new B220Processor.Register(11*4, this, false);
this.S = new B220Processor.Register( 4*4, this, false);
+ // Register E decrements modulo the system memory size, so override dec().
+ this.E.dec = function decE() {
+ if (this.value == 0) {
+ this.value = this.p.bcdMemorySize;
+ }
+ return this.constructor.prototype.dec.apply(this);
+ };
+
// Control Console Lamps
this.digitCheckAlarm = new B220Processor.FlipFlop(this, false);
@@ -232,10 +241,8 @@ function B220Processor(config, devices) {
this.boundConsoleReceiveDigit = B220Processor.bindMethod(this, B220Processor.prototype.consoleReceiveDigit);
this.boundConsoleReceiveSingleDigit = B220Processor.bindMethod(this, B220Processor.prototype.consoleReceiveSingleDigit);
- this.boundCardatronOutputWordReady = B220Processor.bindMethod(this, B220Processor.prototype.cardatronOutputWordReady);
this.boundCardatronOutputWord= B220Processor.bindMethod(this, B220Processor.prototype.cardatronOutputWord);
this.boundCardatronOutputFinished = B220Processor.bindMethod(this, B220Processor.prototype.cardatronOutputFinished);
- this.boundCardatronInputWord = B220Processor.bindMethod(this, B220Processor.prototype.cardatronInputWord);
this.boundCardatronReceiveWord = B220Processor.bindMethod(this, B220Processor.prototype.cardatronReceiveWord);
this.boundMagTapeReceiveBlock = B220Processor.bindMethod(this, B220Processor.prototype.magTapeReceiveBlock);
@@ -253,7 +260,7 @@ function B220Processor(config, devices) {
* Global Constants *
***********************************************************************/
-B220Processor.version = "0.01";
+B220Processor.version = "0.01a";
B220Processor.tick = 1000/200000; // milliseconds per clock cycle (200KHz)
B220Processor.cyclesPerMilli = 1/B220Processor.tick;
@@ -561,17 +568,17 @@ B220Processor.prototype.clear = function clear() {
this.COP = 0; // copy of C register op code (2 digits)
this.CADDR = 0; // copy of C register operand address (4 digits)
+ // I/O globals
+ this.rDigit = 0; // variant/format digit from control part of instruction
+ this.vDigit = 0; // variant digit from control part of instruction
+ this.selectedUnit = 0; // currently-selected unit number
+
// Kill any pending action that may be in process
if (this.scheduler) {
clearCallback(this.scheduler);
this.scheduler = 0;
}
- // Clear Cardatron Control Unit
- if (this.cardatron) {
- this.cardatron.clear();
- }
-
this.updateGlow(1); // initialize the lamp states
};
@@ -1060,6 +1067,49 @@ B220Processor.prototype.setHighSpeedPrinterCheck = function setHighSpeedPrinterC
};
+/***********************************************************************
+* Memory Access *
+***********************************************************************/
+
+/**************************************/
+B220Processor.prototype.readMemory = function readMemory() {
+ /* Reads the contents of one word of memory into the IB register from the
+ address in the E register. Sets the Storage Check alarm if the address is
+ not valid. Returns the word fetched, or the current value of IB if invalid
+ address */
+ var addr = B220Processor.bcdBinary(this.E.value);
+
+ if (isNaN(addr)) {
+ this.setStorageCheck(1);
+ return this.IB.value;
+ } else if (addr >= this.memorySize) {
+ this.setStorageCheck(1);
+ return this.IB.value;
+ } else if (this.MEMORYLOCKOUTSW) {
+ return this.IB.set(this.D.value);
+ } else {
+ return this.IB.set(this.MM[addr]);
+ }
+};
+
+/**************************************/
+B220Processor.prototype.writeMemory = function writeMemory() {
+ /* Stores one word of memory from the IB register to the address in the E
+ register. Sets the Storage Check alarm if the address is not valid */
+ var addr = B220Processor.bcdBinary(this.E.value);
+
+ if (isNaN(addr)) {
+ this.setStorageCheck(1);
+ } else if (addr >= this.memorySize) {
+ this.setStorageCheck(1);
+ } else if (this.MEMORYLOCKOUTSW) {
+ this.D.set(this.IB.value);
+ } else {
+ this.MM[addr] = this.IB.value;
+ }
+};
+
+
/***********************************************************************
* The 220 Adder and Arithmetic Operations *
***********************************************************************/
@@ -2263,49 +2313,6 @@ B220Processor.prototype.storeRegister = function storeRegister() {
};
-/***********************************************************************
-* Memory Access *
-***********************************************************************/
-
-/**************************************/
-B220Processor.prototype.readMemory = function readMemory() {
- /* Reads the contents of one word of memory into the IB register from the
- address in the E register. Sets the Storage Check alarm if the address is
- not valid. Returns the word fetched, or the current value of IB if invalid
- address */
- var addr = B220Processor.bcdBinary(this.E.value);
-
- if (isNaN(addr)) {
- this.setStorageCheck(1);
- return this.IB.value;
- } else if (addr >= this.memorySize) {
- this.setStorageCheck(1);
- return this.IB.value;
- } else if (this.MEMORYLOCKOUTSW) {
- return this.IB.set(this.D.value);
- } else {
- return this.IB.set(this.MM[addr]);
- }
-};
-
-/**************************************/
-B220Processor.prototype.writeMemory = function writeMemory() {
- /* Stores one word of memory from the IB register to the address in the E
- register. Sets the Storage Check alarm if the address is not valid */
- var addr = B220Processor.bcdBinary(this.E.value);
-
- if (isNaN(addr)) {
- this.setStorageCheck(1);
- } else if (addr >= this.memorySize) {
- this.setStorageCheck(1);
- } else if (this.MEMORYLOCKOUTSW) {
- this.D.set(this.IB.value);
- } else {
- this.MM[addr] = this.IB.value;
- }
-};
-
-
/***********************************************************************
* Console I/O Module *
***********************************************************************/
@@ -2427,7 +2434,7 @@ B220Processor.prototype.consoleOutputSign = function consoleOutputSign(printSign
this.E.set(this.CADDR);
this.readMemory();
if (this.MET.value) { // invalid address
- this.ioComplete();
+ this.ioComplete(true);
} else {
this.D.set(this.IB.value);
this.opTime += 0.070; // estimate for memory access and rotation
@@ -2517,7 +2524,7 @@ B220Processor.prototype.consoleOutputFinished = function consoleOutputFinished()
this.clockIn();
if (this.AST.value) { // if false, we've probably been cleared
this.EWT.set(0);
- this.ioComplete();
+ this.ioComplete(true);
}
};
@@ -2574,8 +2581,8 @@ B220Processor.prototype.consoleReceiveDigit = function consoleReceiveDigit(digit
this.CCONTROL = this.bcdAdd(this.CADDR, 1, 4);
}
- this.CEXTRA = (this.D.value - word%0x1000000)/0x1000000;
- this.kDigit = (this.CEXTRA >>> 8) & 0x0F;
+ this.CCONTROL = (this.D.value - word%0x1000000)/0x1000000;
+ this.rDigit = (this.CCONTROL >>> 8) & 0x0F;
// do not set this.selectedUnit from the word -- keep the same unit
// Shift D5-D10 into C1-C6, modify by B as necessary, and execute
@@ -2654,175 +2661,113 @@ B220Processor.prototype.consoleReceiveSingleDigit = function consoleReceiveSingl
***********************************************************************/
/**************************************/
-B220Processor.prototype.cardatronOutputWordReady = function cardatronOutputWordReady() {
- /* Successor function for readMemory that sets up the next word of output
- and calls the current ioCallback function to output that word */
-
- if (this.tog3IO) { // if false, we've probably been cleared
- this.SHIFT = 0x09; // for display only
- this.A.set(this.D.value); // move D with the memory word to A
- this.togTWA = 1; // for display only
- this.execTime -= performance.now()*B220Processor.wordsPerMilli;
- this.ioCallback(this.A.value, this.boundCardatronOutputWord, this.boundCardatronOutputFinished);
- }
-};
-
-/**************************************/
-B220Processor.prototype.cardatronOutputWord = function cardatronOutputWord(receiver) {
+B220Processor.prototype.cardatronOutputWord = function cardatronOutputWord() {
/* Initiates a read of the next word from memory for output to the
- Cardatron Control Unit */
+ Cardatron Control Unit. Returns a negative number to stop transfer */
+ var word;
- this.execTime += performance.now()*B220Processor.wordsPerMilli;
- if (this.tog3IO) { // if false, we've probably been cleared
- // Increment the source address (except on the first word)
- this.SHIFTCONTROL = 0x01; // for display only
- this.SHIFT = 0x15; // for display only
- if (this.togCOUNT) {
- this.CADDR = this.bcdAdd(this.CADDR, 1, 4);
+ this.clockIn();
+ if (!this.AST.value) { // we've probably been cleared
+ word = -1;
+ } else if (this.MET.value) { // previous memory access error
+ word = 0;
+ } else {
+ word = this.readMemory(); // address in E was previously set
+ if (this.MET.value) {
+ word = 0;
} else {
- this.togCOUNT = 1;
+ this.E.dec(); // step down to next memory address
}
- this.C.set((this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL);
- this.ioCallback = receiver;
- this.readMemory(this.boundCardatronOutputWordReady);
}
+
+ return word;
};
/**************************************/
B220Processor.prototype.cardatronOutputFinished = function cardatronOutputFinished() {
/* Handles the final cycle of an I/O operation and restores this.execTime */
- if (this.tog3IO) { // if false, we've probably been cleared
- this.tog3IO = 0;
- this.togTWA = 0; // for display only
- this.execTime += performance.now()*B220Processor.wordsPerMilli;
- this.schedule();
+ if (this.AST.value) { // if false, we've probably been cleared
+ this.ioComplete(true);
}
};
-/**************************************/
-B220Processor.prototype.cardatronInputWord = function cardatronInputWord() {
- // Solicits the next input word from the Cardatron Control Unit */
-
- this.togTF = 0; // for display only, reset finish pulse
- this.togTWA = 1; // for display only
- this.execTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O
- this.cardatron.inputWord(this.selectedUnit, this.boundCardatronReceiveWord);
-};
-
/**************************************/
B220Processor.prototype.cardatronReceiveWord = function cardatronReceiveWord(word) {
/* Handles a word coming from the Cardatron input unit. Negative values for
- the word indicate the last word was previously sent and the I/O is finished.
- The word is stored into the D register and is handled according to the sign
- digit in the D register. Any partial word received at the end of the
- I/O is abandoned */
+ the word indicates this is the last word and the I/O is finished. The word
+ is stored into the D register and is handled according to the sign digit in
+ the D register. The last word received (typically a "pusher" word of zeroes)
+ is abandoned and not acted upon. Returns -1 if further data transfer is to
+ be terminated, 0 otherwise */
+ var returnCode = 0; // default is to continue receiving
var sign; // D-register sign digit
- this.execTime += performance.now()*B220Processor.wordsPerMilli; // restore time after I/O
- if (word < 0) {
+ this.clockIn();
+ if (!this.AST.value) { // we've probably been cleared
+ returnCode = -1;
+ } else if (word < 0) {
// Last word received -- finished with the I/O
- this.tog3IO = 0;
- this.togSTART = 0;
- this.togTF = 0; // for display only
- this.togTWA = 0; // for display only
- this.D.set(word-900000000000); // remove the finished signal; for display only, not stored
- this.schedule();
+ this.D.set(word-0x900000000000);// remove the finished signal; for display only, not stored
+ this.ioComplete(true);
+ returnCode = -1;
+ } else if (this.MET.value) {
+ // Memory error has occurred: just ignore further data from Cardatron
} else {
// Full word accumulated -- process it and initialize for the next word
- this.SHIFT = 0x19; // for display only
- this.togTF = 1; // for display only
this.D.set(word);
word %= 0x10000000000; // strip the sign digit
sign = (this.D.value - word)/0x10000000000; // get D-sign
- if (sign & 0x04) {
- // D-sign is 4, 5, 6, 7: execute the word as an instruction
- this.execTime += 2;
- this.togTF = 0; // for display only
- this.togSTART = 1-((sign >>> 1)%2); // whether to continue in input mode
- this.setTimingToggle(0); // Execute mode
- this.togCOUNT = 0;
- this.togBTOAIN = 0;
- this.togADDAB = 1; // for display only
- this.togADDER = 1; // for display only
- this.togDPCTR = 1; // for display only
- this.togCLEAR = ((this.kDigit & 0x08) ? 1 : 1-(sign%2));
- this.togSIGN = ((this.A.value - this.A.value%0x10000000000)/0x10000000000)%2; // display only
-
- this.CEXTRA = (this.D - word%0x1000000)/0x1000000;
- this.kDigit = (this.CEXTRA >>> 8) & 0x0F;
- // do not set this.selectedUnit from the word -- keep the same unit
-
- // Shift D5-D10 into C1-C6, modify by B as necessary, and execute
- if (this.togCLEAR) {
- word = this.bcdAdd(word%0x1000000, 0, 11);
- } else {
- word = this.bcdAdd(word%0x1000000, this.B.value, 11) % 0x1000000;
- }
- this.C.set(word*0x10000 + this.CCONTROL); // put C back together
- this.CADDR = word % 0x10000;
- this.COP = (word - this.CADDR)/0x10000;
- if (sign & 0x02) { // sign-6 or -7
- this.tog3IO = 0;
- this.togTF = 0; // for display only
- this.cardatron.inputStop(this.selectedUnit);
- this.execute();
- } else { // sign-4 or -5
- /* It's not exactly clear what should happen at this point. The
- documentation states that a sign-4 or -5 word coming from a Cardatron
- input unit can only contain a CDR (44) instruction, which is sensible,
- since sign-4/5 words are generally used to change the destination memory
- address for the data transfer, and the Cardatron presumably still had
- words to transfer. What it doesn't say is what happened if the sign-4/5
- word contained something else. My guess is that either the Processor
- ignored any other op code and proceeded as if it had been a CDR, or more
- likely, things went to hell in a handbasket. The latter is a little
- difficult to emulate, especially since we don't know which hell or
- handbasket might be involved, so we'll assume the former, and just
- continue requesting words from the Cardatron */
- this.SHIFT = 0x09; // reset shift counter for next word
- this.D.set(0); // clear D to prepare for next word
- this.cardatronInputWord(); // request the next word
- }
- } else {
- // D-sign is 0, 1, 2, 3, 8, 9: store word, possibly modified by B
- this.execTime += 3;
- this.setTimingToggle(1); // Fetch mode
- this.togCOUNT = this.togBTOAIN;
- this.togBTOAIN = 1;
- this.togADDAB = 1; // for display only
- this.togADDER = 1; // for display only
- this.togDPCTR = 1; // for display only
- this.togSIGN = sign%2;
- if (this.kDigit & 0x08) {
- this.togCLEAR = 1;
- } else {
- this.togCLEAR = 1-((sign >>> 1)%2);
- sign &= 0x0D;
+ switch (sign) {
+ case 0: // sign is 0-5: store word normally
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ this.IB.set(this.D.value);
+ this.writeMemory();
+ if (!this.MET.value) {
+ this.E.dec(); // decrement memory address for next word
}
+ break;
- // Increment the destination address (except on the first word)
- this.SHIFTCONTROL = 0x01; // for display only
- this.SHIFT = 0x15; // for display only
- if (this.togCOUNT) {
- this.CADDR = this.bcdAdd(this.CADDR, 1, 4);
+ case 6: // sign is 6, 7: execute control word
+ case 7:
+ if (this.vDigit & 0x01) { // input control words are inhibited
+ this.IB.set(this.D.value);
+ this.writeMemory(); // just store the word with its sign
+ if (!this.MET.value) {
+ this.E.dec(); // decrement memory address for next word
+ }
+ } else { // input control words are executed
+ this.IB.set(this.D.value); // move word to IB for use by fetch()
+ this.ioComplete(false); // terminate I/O but do not restart Processor yet
+ this.fetch(true); // set up to execute control word
+ returnCode = -1; // stop further input from Cardatron
+ // Schedule the Processor to give Cardatron a chance to finish its operation.
+ setCallback(this.mnemonic, this, 0, this.schedule);
}
- this.C.set((this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL);
+ break;
- // Modify the word by B as necessary and store it
- if (this.togCLEAR) {
- word = this.bcdAdd(word, 0, 11);
- } else {
- word = this.bcdAdd(word, this.B.value, 11);
+ default: // sign is 8, 9: store word with optional B mod
+ if (!(this.rDigit & 0x08)) { // no B-register modification
+ this.IB.set(this.D.value);
+ } else { // add B to low-order five digits of word
+ word = word - word%0x100000 + this.bcdAdd(word, this.B.value, 5);
+ this.C10.set(0); // reset carry toggle
+ this.IB.set((sign%2)*0x10000000000 + word);
}
-
- this.A.set(sign*0x10000000000 + word%0x10000000000);
- this.SHIFT = 0x09; // reset shift counter for next word
- this.D.set(0); // clear D and request the next word after storing this one
- this.writeMemory(this.boundCardatronInputWord, false);
- }
+ this.writeMemory();
+ if (!this.MET.value) {
+ this.E.dec(); // decrement memory address for next word
+ }
+ break;
+ } // switch sign
}
+
+ return returnCode;
};
/***********************************************************************
@@ -3047,18 +2992,18 @@ B220Processor.prototype.fetchWordToC = function fetchWordToC(word) {
};
/**************************************/
-B220Processor.prototype.fetch = function fetch() {
+B220Processor.prototype.fetch = function fetch(entryP) {
/* Implements the Fetch cycle of the 220 processor. This is initiated either
by pressing START on the Console with EXT=0 (Fetch), pressing STEP on the
Console when the computer is stopped and EXT=0, during I/O when a control
- word (sign>3) is received from a peripheral device, or by the prior
- Operation Complete if the processor is in continuous mode. Note that the
- time for fetch is built into the individual instruction times, and is
- accumulated here only if the Execute cycle is skipped */
+ word (sign 6,7) is received from a peripheral device, or by the prior
+ Operation Complete if the processor is in continuous mode. The "entryP"
+ parameter indicates whether the instruction word is already in IB (true)
+ or must be fetched from the address in P first (false) */
var dSign; // sign bit of IB register
var word; // instruction word
- if (this.AST.value) { // if doing I/O
+ if (entryP) { // if instruction already loaded
word = this.IB.value;
} else { // if doing normal fetch
this.E.set(this.P.value);
@@ -3070,7 +3015,7 @@ B220Processor.prototype.fetch = function fetch() {
this.fetchWordToC(word);
this.D.set(word); // D contains a copy of memory word
- if (!this.AST.value && !this.PCOUNTSW) {
+ if (!entryP && !this.PCOUNTSW) {
this.P.inc(); // if not doing I/O, bump the program counter
}
}
@@ -3150,7 +3095,7 @@ B220Processor.prototype.execute = function execute() {
d = this.console.outputUnitSelect(d, this.boundConsoleOutputSign);
if (d < 0) { // no unit available -- set alarm and quit
this.setPaperTapeCheck(1);
- this.ioComplete();
+ this.ioComplete(true);
}
break;
@@ -3182,7 +3127,7 @@ B220Processor.prototype.execute = function execute() {
d = this.console.outputUnitSelect(0, this.boundConsoleOutputSign);
if (d < 0) { // no unit available -- set alarm and quit
this.setPaperTapeCheck(1);
- this.ioComplete();
+ this.ioComplete(true);
}
break;
@@ -3677,8 +3622,23 @@ B220Processor.prototype.execute = function execute() {
break;
case 0x60: //--------------------- CRD Card read
- this.setProgramCheck(1);
- this.operationComplete();
+ this.opTime = 1.600; // rough minimum estimage
+ this.E.set(this.CADDR);
+ this.D.set(0);
+ if (!this.cardatron) {
+ this.setCardatronCheck(1);
+ this.operationComplete();
+ } else {
+ this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
+ this.rDigit = this.CCONTROL & 0x0F;
+ this.vDigit = (this.CCONTROL >>> 4) & 0x0F;
+ this.ioInitiate();
+ d = this.cardatron.inputInitiate(this.selectedUnit, this.rDigit, this.boundCardatronReceiveWord);
+ if (d < 0) { // invalid unit
+ this.setCardatronCheck(1);
+ this.ioComplete(true);
+ }
+ }
break;
case 0x61: //--------------------- CWR Card write
@@ -3688,6 +3648,23 @@ B220Processor.prototype.execute = function execute() {
case 0x62: //--------------------- CRF Card read, format load
this.setProgramCheck(1);
+ this.opTime = 2.940; // rough minimum estimage
+ this.E.set(this.CADDR);
+ this.D.set(0);
+ if (!this.cardatron) {
+ this.setCardatronCheck(1);
+ this.operationComplete();
+ } else {
+ this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
+ this.rDigit = this.CCONTROL & 0x0F;
+ this.ioInitiate();
+ d = this.cardatron.inputFormatInitiate(this.selectedUnit, this.rDigit,
+ this.boundCardatronOutputWord, this.boundCardatronOutputFinished);
+ if (d < 0) { // invalid unit
+ this.setCardatronCheck(1);
+ this.ioComplete(true);
+ }
+ }
this.operationComplete();
break;
@@ -3697,7 +3674,22 @@ B220Processor.prototype.execute = function execute() {
break;
case 0x64: //--------------------- CRI Card read interrogate, branch
- this.setProgramCheck(1);
+ this.opTime = 0.265; // average
+ this.E.set(this.CADDR);
+ this.D.set(0);
+ if (!this.cardatron) {
+ this.setCardatronCheck(1);
+ } else {
+ this.E.set(this.CADDR);
+ this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
+ d = this.cardatron.inputReadyInterrogate(this.selectedUnit);
+ if (d < 0) { // invalid unit
+ this.setCardatronCheck(1);
+ } else if (d > 0) {
+ this.opTime += 0.020;
+ this.P.set(this.CADDR);
+ }
+ }
this.operationComplete();
break;
@@ -3736,8 +3728,8 @@ B220Processor.prototype.execute = function execute() {
if (!this.magTape) {
//this.schedule();
} else {
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x0F;
- d = (this.CEXTRA >>> 8) & 0xFF; // number of blocks
+ this.selectedUnit = (this.CCONTROL >>> 4) & 0x0F;
+ d = (this.CCONTROL >>> 8) & 0xFF; // number of blocks
this.togMT3P = 1;
this.togMT1BV4 = d%2; // select initial loop buffer
this.togMT1BV5 = 1-this.togMT1BV4;
@@ -3752,8 +3744,8 @@ B220Processor.prototype.execute = function execute() {
case 0x42: //---------------- MTS Magnetic Tape Search
if (this.magTape) {
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x0F;
- d = (this.CEXTRA >>> 8) & 0xFF; // lane number
+ this.selectedUnit = (this.CCONTROL >>> 4) & 0x0F;
+ d = (this.CCONTROL >>> 8) & 0xFF; // lane number
if (this.magTape.search(this.selectedUnit, d, this.CADDR)) {
this.OFT.set(1); // control or tape unit busy/not-ready
}
@@ -3761,52 +3753,12 @@ B220Processor.prototype.execute = function execute() {
//this.schedule();
break;
- case 0x44: //---------------- CDR Card Read (Cardatron)
- this.D.set(0);
- if (!this.cardatron) {
- //this.schedule();
- } else {
- this.tog3IO = 1;
- this.kDigit = (this.CEXTRA >>> 8) & 0x0F;
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x07;
- this.SHIFT = 0x08; // prepare to receive 11 digits
- this.execTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O
- this.cardatron.inputInitiate(this.selectedUnit, this.kDigit, this.boundCardatronReceiveWord);
- }
- break;
-
- case 0x45: //---------------- CDRI Card Read Interrogate
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x07;
- if (this.cardatron && this.cardatron.inputReadyInterrogate(this.selectedUnit)) {
- this.R.set(this.CCONTROL*0x1000000);
- this.setTimingToggle(0); // stay in Execute
- this.OFT.set(1); // set overflow
- this.COP = 0x28; // make into a CC
- this.C.set((this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL);
- }
- //this.schedule();
- break;
-
- case 0x48: //---------------- CDRF Card Read Format
- if (!this.cardatron) {
- //this.schedule();
- } else {
- this.tog3IO = 1;
- this.kDigit = (this.CEXTRA >>> 8) & 0x0F;
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x07;
- this.SHIFT = 0x19; // start at beginning of a word
- this.execTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O
- this.cardatron.inputFormatInitiate(this.selectedUnit, this.kDigit,
- this.boundCardatronOutputWord, this.boundCardatronOutputFinished);
- }
- break;
-
case 0x50: //---------------- MTW Magnetic Tape Write
if (!this.magTape) {
//this.schedule();
} else {
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x0F;
- d = (this.CEXTRA >>> 8) & 0xFF; // number of blocks
+ this.selectedUnit = (this.CCONTROL >>> 4) & 0x0F;
+ d = (this.CCONTROL >>> 8) & 0xFF; // number of blocks
this.togMT3P = 1;
this.togMT1BV4 = d%2; // select initial loop buffer
this.togMT1BV5 = 1-this.togMT1BV4;
@@ -3821,7 +3773,7 @@ B220Processor.prototype.execute = function execute() {
case 0x52: //---------------- MTRW Magnetic Tape Rewind
if (this.magTape) {
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x0F;
+ this.selectedUnit = (this.CCONTROL >>> 4) & 0x0F;
if (this.magTape.rewind(this.selectedUnit)) {
this.OFT.set(1); // control or tape unit busy/not-ready
}
@@ -3833,17 +3785,17 @@ B220Processor.prototype.execute = function execute() {
//this.schedule();
} else {
this.tog3IO = 1;
- this.kDigit = (this.CEXTRA >>> 8) & 0x0F;
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x07;
+ this.kDigit = (this.CCONTROL >>> 8) & 0x0F;
+ this.selectedUnit = (this.CCONTROL >>> 4) & 0x07;
this.SHIFT = 0x19; // start at beginning of a word
this.execTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O
- this.cardatron.outputInitiate(this.selectedUnit, this.kDigit, (this.CEXTRA >>> 12) & 0x0F,
+ this.cardatron.outputInitiate(this.selectedUnit, this.kDigit, (this.CCONTROL >>> 12) & 0x0F,
this.boundCardatronOutputWord, this.boundCardatronOutputFinished);
}
break;
case 0x55: //---------------- CDWI Card Write Interrogate
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x07;
+ this.selectedUnit = (this.CCONTROL >>> 4) & 0x07;
if (this.cardatron && this.cardatron.outputReadyInterrogate(this.selectedUnit)) {
this.R.set(this.CCONTROL*0x1000000);
this.setTimingToggle(0); // stay in Execute
@@ -3858,8 +3810,8 @@ B220Processor.prototype.execute = function execute() {
//this.schedule();
} else {
this.tog3IO = 1;
- this.kDigit = (this.CEXTRA >>> 8) & 0x0F;
- this.selectedUnit = (this.CEXTRA >>> 4) & 0x07;
+ this.kDigit = (this.CCONTROL >>> 8) & 0x0F;
+ this.selectedUnit = (this.CCONTROL >>> 4) & 0x07;
this.SHIFT = 0x19; // start at beginning of a word
this.execTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O
this.cardatron.outputFormatInitiate(this.selectedUnit, this.kDigit,
@@ -3911,9 +3863,10 @@ B220Processor.prototype.operationComplete = function operationComplete() {
};
/**************************************/
-B220Processor.prototype.ioComplete = function ioComplete() {
+B220Processor.prototype.ioComplete = function ioComplete(restart) {
/* Implements completion of the Execute cycle for an I/O instruction that
- has been executing asynchronously */
+ has been executing asynchronously. If "restart" is true, the Processor will
+ resume automatic operation */
this.AST.set(0);
this.clockIn();
@@ -3922,7 +3875,7 @@ B220Processor.prototype.ioComplete = function ioComplete() {
}
this.operationComplete();
- if (this.RUT.value) {
+ if (restart && this.RUT.value) {
this.schedule();
}
};
@@ -3968,7 +3921,7 @@ B220Processor.prototype.run = function run() {
}
}
- this.fetch();
+ this.fetch(false);
if (this.SST.value) {
this.stop(); // single-stepping
}
@@ -4199,20 +4152,6 @@ B220Processor.prototype.tcuClear = function tcuClear() {
}
};
-/**************************************/
-B220Processor.prototype.inputSetup = function inputSetup(unitNr) {
- /* Called from the Cardatron Control Unit. If the Processor is stopped,
- loads a CDR (60) instruction into C for unit "unitNr" and sets Execute cycle */
-
- if (this.poweredOn && !this.RUT.value) {
- this.CONTROL = unitNr*0x1000; // recognize control words, no lockout
- this.COP = 0x60; // CDR instruction
- this.CADDR = 0;
- this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR);
- this.EXT.set(1);
- }
-};
-
/**************************************/
B220Processor.prototype.powerUp = function powerUp() {
/* Powers up the system */
@@ -4343,7 +4282,7 @@ B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() {
this.MM[ 229] = 0;
this.MM[ 230] = 0;
this.MM[ 231] = 0;
- this.MM[ 232] = 0x20000000016; // carriage return
+ this.MM[ 232] = 0x20202020216; // carriage return
this.MM[ 233] = 0x05110000000; // 1.0 literal: argument increment
this.MM[ 234] = 0x05120000000; // 2.0 literal
this.MM[ 235] = 0x05099999990; // 0.99999990 literal: target precision
@@ -4351,4 +4290,92 @@ B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() {
this.MM[ 237] = 0; // current sqrt result
this.MM[ 238] = 0; // current upper bound on result
this.MM[ 239] = 0x05120000000; // 2.0 sqrt argument
+
+ // Print first 800 digits of Pi; adapted from C program by Dik Winter of CWI, Amsterdam
+ this.MM[ 300]= 0x00000100371; // CAD FLIM
+ this.MM[ 301]= 0x00000400365; // STA C C=FLIM
+ this.MM[ 302]= 0x00000100363; // CAD A
+ this.MM[ 303]= 0x00001480010; // SRT 10
+ this.MM[ 304]= 0x00000150375; // DIV FIVE A DIV 5
+ this.MM[ 305]= 0x00000420365; // LDB C FOR (B=C; B>=0; --B)
+ this.MM[ 306]= 0x10000401000; // STA - F F[B]=A DIV 5
+ this.MM[ 307]= 0x00001210306; // DBB *-1,1
+
+ this.MM[ 308]= 0x00000100365; // L1 CAD C START OF OUTER LOOP
+ this.MM[ 309]= 0x00000140374; // MUL TWO
+ this.MM[ 310]= 0x00001400368; // STR G G=C*2
+ this.MM[ 311]= 0x00000370362; // BFR ENDL1,00,00 IF G EQL 0, BRANCH OUT OF LOOP
+ this.MM[ 312]= 0x00000460366; // CLL D D=0
+ this.MM[ 313]= 0x00000100365; // CAD C
+ this.MM[ 314]= 0x00000400364; // STA B B=C
+ this.MM[ 315]= 0x00000420364; // LDB B
+
+ this.MM[ 316]= 0x10000101000; // DO CAD - F START OF INNER LOOP
+ this.MM[ 317]= 0x00000140363; // MUL A F[B]*A
+ this.MM[ 318]= 0x00001490010; // SLT 10 SHIFT PRODUCT TO RA
+ this.MM[ 319]= 0x00000120366; // ADD D
+ this.MM[ 320]= 0x00000400366; // STA D D+=F[B]*A
+ this.MM[ 321]= 0x00001480010; // SRT 10 SAVE NEW D IN RR
+ this.MM[ 322]= 0x00001270368; // DFL G,00,1 G-=1
+ this.MM[ 323]= 0x00000150368; // DIV G D DIV G
+ this.MM[ 324]= 0x10001401000; // STR - F F[B]=D MOD G
+ this.MM[ 325]= 0x00000400366; // STA D D=D DIV G
+ this.MM[ 326]= 0x00001270368; // DFL G,00,1 G-=1
+ this.MM[ 327]= 0x00000100364; // CAD B
+ this.MM[ 328]= 0x00000130373; // SUB ONE
+ this.MM[ 329]= 0x00000400364; // STA B B-=1
+ this.MM[ 330]= 0x00000360334; // BFA ENDDO,00,00 IF B EQL 0, BRANCH OUT OF INNER LOOP
+ this.MM[ 331]= 0x00000140366; // MUL D
+ this.MM[ 332]= 0x00001400366; // STR D D*=B
+ this.MM[ 333]= 0x00001210316; // DBB DO,1 DECREMENT RB, REPEAT INNER LOOP IF >= 0
+
+ this.MM[ 334]= 0x00014270365; // ENDDO DFL C,00,14 C-=14
+ this.MM[ 335]= 0x00000100366; // CAD D
+ this.MM[ 336]= 0x00001480010; // SRT 10
+ this.MM[ 337]= 0x00000150363; // DIV A D DIV A
+ this.MM[ 338]= 0x00000120367; // ADD E RA=E+D DIV A
+ this.MM[ 339]= 0x00001400367; // STR E E=D MOD A
+
+ // FORMAT 4 DIGITS FOR SPO OUTPUT
+ this.MM[ 340]= 0x00001480003; // SRT 3 ISOLATE HIGH-ORDER DIGIT IN A
+ this.MM[ 341]= 0x00000120376; // ADD N80 CONVERT 1ST DIGIT TO ALPHA
+ this.MM[ 342]= 0x00000490001; // SLA 1
+ this.MM[ 343]= 0x00001490001; // SLT 1
+ this.MM[ 344]= 0x00000120376; // ADD N80 CONVERT 2ND DIGIT TO ALPHA
+ this.MM[ 345]= 0x00000490001; // SLA 1
+ this.MM[ 346]= 0x00001490001; // SLT 1
+ this.MM[ 347]= 0x00000120376; // ADD N80 CONVERT 3RD DIGIT TO ALPHA
+ this.MM[ 348]= 0x00000490001; // SLA 1
+ this.MM[ 349]= 0x00001490001; // SLT 1
+ this.MM[ 350]= 0x00000120376; // ADD N80 CONVERT 4TH DIGIT TO ALPHA
+ this.MM[ 351]= 0x00000490002; // SLA 2 INSERT TRAILING SPACE
+ this.MM[ 352]= 0x00002430000; // LSA 2 SET SIGN TO TWO FOR ALPHA WORD
+ this.MM[ 353]= 0x00000400364; // STA B STORE IN WORD BUFFER
+ this.MM[ 354]= 0x00010090364; // SPO B,1
+ this.MM[ 355]= 0x00405260369; // IFL COL,04,1 CHECK FOR FULL LINE ON SPO
+ this.MM[ 356]= 0x00000100369; // CAD COL
+ this.MM[ 357]= 0x00000180370; // CFA ECOL
+ this.MM[ 358]= 0x00001340308; // BCL L1 IF COL < ECOL, BRANCH
+ this.MM[ 359]= 0x00010090377; // SPO CR,1 OUTPUT NEWLINES
+ this.MM[ 360]= 0x00000460369; // CLL COL CLEAR COLUMN COUNTER
+ this.MM[ 361]= 0x00000300308; // BUN L1
+ this.MM[ 362]= 0x00000007557; // ENDL1 HLT 7557
+
+ this.MM[ 363]= 0x00000010000; // A CNST 10000
+ this.MM[ 364]= 0x00000000000; // B CNST 0
+ this.MM[ 365]= 0x00000000000; // C CNST 0
+ this.MM[ 366]= 0x00000000000; // D CNST 0
+ this.MM[ 367]= 0x00000000000; // E CNST 0
+ this.MM[ 368]= 0x00000000000; // G CNST 0
+ this.MM[ 369]= 0x00000000000; // COL CNST 0
+ this.MM[ 370]= 0x00000000050; // ECOL CNST 50
+ this.MM[ 371]= 0x00000002800; // FLIM CNST 2800
+ this.MM[ 372]= 0x00000000000; // ZERO CNST 0
+ this.MM[ 373]= 0x00000000001; // ONE CNST 1
+ this.MM[ 374]= 0x00000000002; // TWO CNST 2
+ this.MM[ 375]= 0x00000000005; // FIVE CNST 5
+ this.MM[ 376]= 0x00000000080; // N80 CNST 80
+ this.MM[ 377]= 0x20202021616; // CR CNST 20202021616 NEWLINES
+
+ this.MM[1000]= 0x00000000000; // F DEFN * ARRAY F[2800]
};
\ No newline at end of file
diff --git a/software/examples/WINTER.PI.Load.card b/software/examples/WINTER.PI.Load.card
new file mode 100644
index 0000000..cb020ec
--- /dev/null
+++ b/software/examples/WINTER.PI.Load.card
@@ -0,0 +1,17 @@
+777 PRINT FIRST 800 DIGITS OF PI
+777 ADAPTED BY P.KIMPEL FROM C PROGRAM BY DIK WINTER OF CWI, AMSTERDAM
+666 61000600505
+66661000600511000001005710000040056500000100563000014800100000015057500000420565
+66661000600517100004010000000121050600000100565000001405740000140056800000370562
+66661000600523000004605660000010056500000400564000004205641000010100000000140563
+66661000600529000014900100000012056600000400566000014800100000127056800000150568
+66661000600535100014010000000040056600001270568000001005640000013057300000400564
+66661000600541000003605340000014056600001400566000012105160001427056500000100566
+66661000600547000014800100000015056300000120567000014005670000148000300000120576
+66661000600553000004900010000149000100000120576000004900010000149000100000120576
+66661000600559000004900010000149000100000120576000004900020000243000000000400564
+66661000600562000100905640040526056900000100569000001805700000134050800010090577
+666 61000600568000004605690000030050800000007557
+66661000600574000000100000000000000000000000000000000000000000000000000000000000
+66661000600577000000000000000000005000000002800000000000000000000000100000000002
+666 60000300500000000000050000000008020202021616
diff --git a/software/examples/WINTER.PI.card b/software/examples/WINTER.PI.card
new file mode 100644
index 0000000..091df71
--- /dev/null
+++ b/software/examples/WINTER.PI.card
@@ -0,0 +1,93 @@
+1 REM PRINT FIRST 800 DIGITS OF PI
+1 REM ADAPTED FROM C PROGRAM B DIK WINTER OF CWI, AMSTERDAM
+ LOCN 0300
+ CAD FLIM
+ STA C C=FLIM
+ CAD A
+ SRT 10
+ DIV FIVE A DIV 5
+ LDB C FOR (B=C; B>=0; --B)
+ STA - F F[B]=A DIV 5
+ DBB *-1,1
+ REM
+ L1 CAD C START OF OUTER LOOP
+ MUL TWO
+ STR G G=C*2
+ BFR ENDL1,00,00 IF G EQL 0, BRANCH OUT OF LOOP
+ CLL D D=0
+ CAD C
+ STA B B=C
+ LDB B
+ REM
+ DO CAD - F START OF INNER LOOP
+ MUL A F[B]*A
+ SLT 10 SHIFT PRODUCT TO RA
+ ADD D
+ STA D D+=F[B]*A
+ SRT 10 SAVE NEW D IN RR
+ DFL G,00,1 G-=1
+ DIV G D DIV G
+ STR - F F[B]=D MOD G
+ STA D D=D DIV G
+ DFL G,00,1 G-=1
+ CAD B
+ SUB ONE
+ STA B B-=1
+ BFA ENDDO,00,00 IF B EQL 0, BRANCH OUT OF INNER LOOP
+ MUL D
+ STR D D*=B
+ DBB DO,1 DECREMENT RB, REPEAT INNER LOOP IF >= 0
+ REM
+ ENDDO DFL C,00,14 C-=14
+ CAD D
+ SRT 10
+ DIV A D DIV A
+ ADD E RA=E+D DIV A
+ STR E E=D MOD A
+ REM
+ REM FORMAT 4 DIGITS FOR SPO OUTPUT
+ SRT 3 ISOLATE HIGH-ORDER DIGIT IN A
+ ADD N80 CONVERT 1ST DIGIT TO ALPHA
+ SLA 1
+ SLT 1
+ ADD N80 CONVERT 2ND DIGIT TO ALPHA
+ SLA 1
+ SLT 1
+ ADD N80 CONVERT 3RD DIGIT TO ALPHA
+ SLA 1
+ SLT 1
+ ADD N80 CONVERT 4TH DIGIT TO ALPHA
+ SLA 2 INSERT TRAILING SPACE
+ LSA 2 SET SIGN TO TWO FOR ALPHA WORD
+ STA B STORE IN WORD BUFFER
+ SPO B,1
+ IFL COL,04,5 CHECK FOR FULL LINE ON SPO
+ CAD COL
+ CFA ECOL
+ BCL L1 IF COL < ECOL, BRANCH
+ SPO CR,1 OUTPUT NEWLINES
+ CLL COL CLEAR COLUMN COUNTER
+ BUN L1
+ ENDL1 HLT 7557
+ REM
+ REM SCALARS
+ A CNST 10000
+ B CNST 0
+ C CNST 0
+ D CNST 0
+ E CNST 0
+ G CNST 0
+ COL CNST 0
+ ECOL CNST 50
+ FLIM CNST 2800
+ ZERO CNST 0
+ ONE CNST 1
+ TWO CNST 2
+ FIVE CNST 5
+ N80 CNST 80
+ CR CNST 20202021616 NEWLINES
+ REM
+ LOCN 1000
+ F DEFN *
+ LOCN *+2800
+ FINI 300
diff --git a/software/examples/WINTER.PI.lst b/software/examples/WINTER.PI.lst
new file mode 100644
index 0000000..4f47b0e
--- /dev/null
+++ b/software/examples/WINTER.PI.lst
@@ -0,0 +1,153 @@
+Assembler for the Burroughs 220 BALGOL Compiler & Intrinsics
+Pass 1 Listing Pass 2 Listing
+
+
+END OF PASS 1, ERRORS = 0
+
+SYMBOL TABLE
+
+ 363 A 364 B 1 BMOD 365 C 369 COL
+ 377 CR 366 D 316 DO 367 E 370 ECOL
+ 334 ENDDO 362 ENDL1 1000 F 375 FIVE 371 FLIM
+ 368 G 308 L1 376 N80 373 ONE 1 RLO
+ 374 TWO 372 ZERO
+
+ PRINT FIRST 800 DIGITS OF PI
+ ADAPTED FROM C PROGRAM B DIK WINTER OF CWI, AMSTERDAM
+ 3 0000 LOCN 0300
+ 4 0300 0 0000 10 0371 CAD FLIM
+ 5 0301 0 0000 40 0365 STA C C=FLIM
+ 6 0302 0 0000 10 0363 CAD A
+ 7 0303 0 0001 48 0010 SRT 10
+ 8 0304 0 0000 15 0375 DIV FIVE A DIV 5
+ 9 0305 0 0000 42 0365 LDB C FOR (B=C; B>=0; --B)
+ 10 0306 1 0000 40 1000 STA - F F[B]=A DIV 5
+ 11 0307 0 0001 21 0306 DBB *-1,1
+
+ 13 0308 0 0000 10 0365 L1 CAD C START OF OUTER LOOP
+ 14 0309 0 0000 14 0374 MUL TWO
+ 15 0310 0 0001 40 0368 STR G G=C*2
+ 16 0311 0 0000 37 0362 BFR ENDL1,00,00 IF G EQL 0, BRANCH OUT OF LOOP
+ 17 0312 0 0000 46 0366 CLL D D=0
+ 18 0313 0 0000 10 0365 CAD C
+ 19 0314 0 0000 40 0364 STA B B=C
+ 20 0315 0 0000 42 0364 LDB B
+
+ 22 0316 1 0000 10 1000 DO CAD - F START OF INNER LOOP
+ 23 0317 0 0000 14 0363 MUL A F[B]*A
+ 24 0318 0 0001 49 0010 SLT 10 SHIFT PRODUCT TO RA
+ 25 0319 0 0000 12 0366 ADD D
+ 26 0320 0 0000 40 0366 STA D D+=F[B]*A
+ 27 0321 0 0001 48 0010 SRT 10 SAVE NEW D IN RR
+ 28 0322 0 0001 27 0368 DFL G,00,1 G-=1
+ 29 0323 0 0000 15 0368 DIV G D DIV G
+ 30 0324 1 0001 40 1000 STR - F F[B]=D MOD G
+ 31 0325 0 0000 40 0366 STA D D=D DIV G
+ 32 0326 0 0001 27 0368 DFL G,00,1 G-=1
+ 33 0327 0 0000 10 0364 CAD B
+ 34 0328 0 0000 13 0373 SUB ONE
+ 35 0329 0 0000 40 0364 STA B B-=1
+ 36 0330 0 0000 36 0334 BFA ENDDO,00,00 IF B EQL 0, BRANCH OUT OF INNER LOOP
+ 37 0331 0 0000 14 0366 MUL D
+ 38 0332 0 0001 40 0366 STR D D*=B
+ 39 0333 0 0001 21 0316 DBB DO,1 DECREMENT RB, REPEAT INNER LOOP IF >= 0
+
+ 41 0334 0 0014 27 0365 ENDDO DFL C,00,14 C-=14
+ 42 0335 0 0000 10 0366 CAD D
+ 43 0336 0 0001 48 0010 SRT 10
+ 44 0337 0 0000 15 0363 DIV A D DIV A
+ 45 0338 0 0000 12 0367 ADD E RA=E+D DIV A
+ 46 0339 0 0001 40 0367 STR E E=D MOD A
+
+ FORMAT 4 DIGITS FOR SPO OUTPUT
+ 49 0340 0 0001 48 0003 SRT 3 ISOLATE HIGH-ORDER DIGIT IN A
+ 50 0341 0 0000 12 0376 ADD N80 CONVERT 1ST DIGIT TO ALPHA
+ 51 0342 0 0000 49 0001 SLA 1
+ 52 0343 0 0001 49 0001 SLT 1
+ 53 0344 0 0000 12 0376 ADD N80 CONVERT 2ND DIGIT TO ALPHA
+ 54 0345 0 0000 49 0001 SLA 1
+ 55 0346 0 0001 49 0001 SLT 1
+ 56 0347 0 0000 12 0376 ADD N80 CONVERT 3RD DIGIT TO ALPHA
+ 57 0348 0 0000 49 0001 SLA 1
+ 58 0349 0 0001 49 0001 SLT 1
+ 59 0350 0 0000 12 0376 ADD N80 CONVERT 4TH DIGIT TO ALPHA
+ 60 0351 0 0000 49 0002 SLA 2 INSERT TRAILING SPACE
+ 61 0352 0 0002 43 0000 LSA 2 SET SIGN TO TWO FOR ALPHA WORD
+ 62 0353 0 0000 40 0364 STA B STORE IN WORD BUFFER
+ 63 0354 0 0010 09 0364 SPO B,1
+ 64 0355 0 0405 26 0369 IFL COL,04,5 CHECK FOR FULL LINE ON SPO
+ 65 0356 0 0000 10 0369 CAD COL
+ 66 0357 0 0000 18 0370 CFA ECOL
+ 67 0358 0 0001 34 0308 BCL L1 IF COL < ECOL, BRANCH
+ 68 0359 0 0010 09 0377 SPO CR,1 OUTPUT NEWLINES
+ 69 0360 0 0000 46 0369 CLL COL CLEAR COLUMN COUNTER
+ 70 0361 0 0000 30 0308 BUN L1
+ 71 0362 0 0000 00 7557 ENDL1 HLT 7557
+
+ SCALARS
+ 74 0363 0 0000 01 0000 A CNST 10000
+ 75 0364 0 0000 00 0000 B CNST 0
+ 76 0365 0 0000 00 0000 C CNST 0
+ 77 0366 0 0000 00 0000 D CNST 0
+ 78 0367 0 0000 00 0000 E CNST 0
+ 79 0368 0 0000 00 0000 G CNST 0
+ 80 0369 0 0000 00 0000 COL CNST 0
+ 81 0370 0 0000 00 0050 ECOL CNST 50
+ 82 0371 0 0000 00 2800 FLIM CNST 2800
+ 83 0372 0 0000 00 0000 ZERO CNST 0
+ 84 0373 0 0000 00 0001 ONE CNST 1
+ 85 0374 0 0000 00 0002 TWO CNST 2
+ 86 0375 0 0000 00 0005 FIVE CNST 5
+ 87 0376 0 0000 00 0080 N80 CNST 80
+ 88 0377 2 0202 02 1616 CR CNST 20202021616 NEWLINES
+
+ 90 0378 LOCN 1000
+ 91 1000 F DEFN *
+ 92 1000 LOCN *+2800
+ 93 3800 FINI 300
+
+
+END OF PASS 2, ERRORS = 0
+
+
+
+
+3141 5926 5358 9793 2384 6264 3383 2795 0288 4197
+
+1693 9937 5105 8209 7494 4592 3078 1640 6286 2089
+
+9862 8034 8253 4211 7067 9821 4808 6513 2823 0664
+
+7093 8446 0955 0582 2317 2535 9408 1284 8111 7450
+
+2841 0270 1938 5211 0555 9644 6229 4895 4930 3819
+
+6442 8810 9756 6593 3446 1284 7564 8233 7867 8316
+
+5271 2019 0914 5648 5669 2346 0348 6104 5432 6648
+
+2133 9360 7260 2491 4127 3724 5870 0660 6315 5881
+
+7488 1520 9209 6282 9254 0917 1536 4367 8925 9036
+
+0011 3305 3054 8820 4665 2138 4146 9519 4151 1609
+
+4330 5727 0365 7595 9195 3092 1861 1738 1932 6117
+
+9310 5118 5480 7446 2379 9627 4956 7351 8857 5272
+
+4891 2279 3818 3011 9491 2983 3673 3624 4065 6643
+
+0860 2139 4946 3952 2473 7190 7021 7986 0943 7027
+
+7053 9217 1762 9317 6752 3846 7481 8467 6694 0513
+
+2000 5681 2714 5263 5608 2778 5771 3427 5778 9609
+
+1736 3717 8721 4684 4090 1224 9534 3014 6549 5853
+
+7105 0792 2796 8925 8923 5420 1995 6112 1290 2196
+
+0864 0344 1815 9813 6297 7477 1309 9605 1870 7211
+
+3499 9999 8372 9780 4995 1059 7317 3281 6096 3185
diff --git a/software/tools/BAC-Assembler.html b/software/tools/BAC-Assembler.html
index 48a54d2..496a8d7 100644
--- a/software/tools/BAC-Assembler.html
+++ b/software/tools/BAC-Assembler.html
@@ -1705,11 +1705,11 @@ window.addEventListener("load", function() {
switch (opCode) {
case "ASMBL":
printLine(padRight("", 8+5+4+3+8+4+6) +
- cardData.text.substring(opCodeIndex, operandIndex+operandLength).trim();
+ cardData.text.substring(opCodeIndex, operandIndex+operandLength).trim());
break;
case "REORD":
printLine(padRight("", 8+5+4+3+8+4+6) +
- cardData.text.substring(opCodeIndex, operandIndex+operandLength).trim();
+ cardData.text.substring(opCodeIndex, operandIndex+operandLength).trim());
break;
default:
done = true;
diff --git a/webUI/B220.html b/webUI/B220.html
index 8a496d6..a278847 100644
--- a/webUI/B220.html
+++ b/webUI/B220.html
@@ -30,16 +30,16 @@
-
diff --git a/webUI/B220.js b/webUI/B220.js
index 7bea62c..7558892 100644
--- a/webUI/B220.js
+++ b/webUI/B220.js
@@ -54,13 +54,13 @@ window.addEventListener("load", function() {
processor = new B220Processor(config, devices);
- /********************
if (config.getNode("Cardatron.hasCardatron")) {
devices.CardatronControl = new B220CardatronControl(processor);
} else {
devices.CardatronControl = null;
}
+ /********************
if (config.getNode("MagTape.hasMagTape")) {
devices.MagTapeControl = new B220MagTapeControl(processor);
} else {
diff --git a/webUI/B220CardatronControl.css b/webUI/B220CardatronControl.css
new file mode 100644
index 0000000..3d9b443
--- /dev/null
+++ b/webUI/B220CardatronControl.css
@@ -0,0 +1,77 @@
+/***********************************************************************
+* retro-220/webUI B220CardatronControl.css
+************************************************************************
+* Copyright (c) 2017, Paul Kimpel.
+* Licensed under the MIT License, see
+* http://www.opensource.org/licenses/mit-license.php
+************************************************************************
+* Burroughs 220 emulator Cardatron Control style sheet.
+************************************************************************
+* 2017-05-19 P.Kimpel
+* Original version, from retro-205 D205CardatronControl.css
+***********************************************************************/
+
+#cardatronControlBody {
+ height: 100%;
+ min-height: 100%;
+ overflow: hidden;
+ padding: 0}
+
+#PanelSurface {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0}
+
+#ControlPanel {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ margin: 4px}
+
+#ClearBtn {
+ bottom: 16px;
+ right: 4px}
+#ClearBtnCaption {
+ bottom: 4px;
+ right: 4px;
+ width: 40px}
+
+#OutputUnitLamp {
+ top: 8px;
+ left: 8px}
+#UnitDesignate4Lamp {
+ top: 8px;
+ left: 32px}
+#UnitDesignate2Lamp {
+ top: 8px;
+ left: 56px}
+#UnitDesignate1Lamp {
+ top: 8px;
+ left: 80px}
+
+#RelayDesignate8Lamp {
+ top: 40px;
+ left: 8px}
+#RelayDesignate4Lamp {
+ top: 40px;
+ left: 32px}
+#RelayDesignate2Lamp {
+ top: 40px;
+ left: 56px}
+#RelayDesignate1Lamp {
+ top: 40px;
+ left: 80px}
+
+#BufferReadLamp {
+ top: 72px;
+ left: 8px}
+#BufferWriteLamp {
+ top: 72px;
+ left: 32px}
+#FormatLoadLamp {
+ top: 72px;
+ left: 56px}
diff --git a/webUI/B220CardatronControl.html b/webUI/B220CardatronControl.html
new file mode 100644
index 0000000..ab64de9
--- /dev/null
+++ b/webUI/B220CardatronControl.html
@@ -0,0 +1,35 @@
+
+
+
+
+retro-220 Cardatron Control Unit
+
+
+
+
+
+
+
+
+
+
+
+
+
CLEAR
+
+
+
+
\ No newline at end of file
diff --git a/webUI/B220CardatronControl.js b/webUI/B220CardatronControl.js
new file mode 100644
index 0000000..8e3faa3
--- /dev/null
+++ b/webUI/B220CardatronControl.js
@@ -0,0 +1,301 @@
+/***********************************************************************
+* retro-220/webUI B220CardatronControl.js
+************************************************************************
+* Copyright (c) 2017, Paul Kimpel.
+* Licensed under the MIT License, see
+* http://www.opensource.org/licenses/mit-license.php
+************************************************************************
+* Burroughs 220 Cardatron Control object.
+************************************************************************
+* 2017-05-19 P.Kimpel
+* Original version, from retro-205 D205CardatronControl.js.
+***********************************************************************/
+"use strict";
+
+/**************************************/
+function B220CardatronControl(p) {
+ /* Constructor for the CardatronControl object */
+ var left = 660; // left window margin
+ var u; // unit config object
+ var x; // unit index
+
+ this.config = p.config; // System configuration object
+ this.mnemonic = "CCU";
+ this.p = p; // B220Processor object
+
+ // Do not call this.clear() here -- call from onLoad instead
+
+ this.doc = null;
+ this.window = window.open("../webUI/B220CardatronControl.html", this.mnemonic,
+ "location=no,scrollbars=no,resizable,width=140,height=140,top=0,left=" + left);
+ this.window.addEventListener("load",
+ B220Util.bindMethod(this, B220CardatronControl.prototype.cardatronOnLoad));
+
+ // Set up the I/O devices from the system configuration
+ this.inputUnit = [
+ null, // no input unit 0
+ null, // input unit 1
+ null, // input unit 2
+ null, // input unit 3
+ null, // input unit 4
+ null, // input unit 5
+ null, // input unit 6
+ null]; // input unit 7
+ this.outputUnit = [
+ null, // no output unit 0
+ null, // output unit 1
+ null, // output unit 2
+ null, // output unit 3
+ null, // output unit 4
+ null, // output unit 5
+ null, // output unit 6
+ null]; // output unit 7
+
+ for (x=7; x>0; --x) {
+ u = this.config.getNode("Cardatron.units", x);
+ switch (u.type.substring(0, 2)) {
+ case "CR":
+ this.inputUnit[x] = new B220CardatronInput(u.type, x, this.config);
+ this.outputUnit[8-x] = null;
+ break;
+ case "CP":
+ case "LP":
+ this.inputUnit[x] = null;
+ this.outputUnit[8-x] = new B220CardatronOutput(u.type, x, this.config);
+ break;
+ default:
+ this.inputUnit[x] = null;
+ this.outputUnit[8-x] = null;
+ break;
+ } // switch u.type
+ } // for x
+}
+
+/**************************************/
+B220CardatronControl.prototype.$$ = function $$(e) {
+ return this.doc.getElementById(e);
+};
+
+/**************************************/
+B220CardatronControl.prototype.clear = function clear() {
+ /* Initializes the panel state */
+
+ this.bufferReadLamp.set(0);
+ this.bufferWriteLamp.set(0);
+ this.formatLoadLamp.set(0);
+ this.outputUnitLamp.set(0);
+ this.setUnitDesignateLamps(0);
+ this.setRelayDesignateLamps(0);
+};
+
+/**************************************/
+B220CardatronControl.prototype.setUnitDesignateLamps = function setUnitDesignateLamps(unit) {
+ /* Sets the UD lamps on the panel from the low-order three bits of "unit" */
+
+ this.unitDesignate1Lamp.set(unit & 0x01);
+ this.unitDesignate2Lamp.set((unit >>> 1) & 0x01);
+ this.unitDesignate4Lamp.set((unit >>> 2) & 0x01);
+};
+
+/**************************************/
+B220CardatronControl.prototype.setRelayDesignateLamps = function setRelayDesignateLamps(mask) {
+ /* Sets the RD lamps on the panel from the low-order four bits of "mask" */
+
+ this.relayDesignate1Lamp.set(mask & 0x01);
+ this.relayDesignate2Lamp.set((mask >>> 1) & 0x01);
+ this.relayDesignate4Lamp.set((mask >>> 2) & 0x01);
+ this.relayDesignate8Lamp.set((mask >>> 3) & 0x01);
+};
+
+/**************************************/
+B220CardatronControl.prototype.ClearBtn_onClick = function ClearBtn_onClick(ev) {
+ /* Handle the click event for the CLEAR button, which will clear the
+ Cardatron Control state */
+
+ this.clear();
+};
+
+/**************************************/
+B220CardatronControl.prototype.beforeUnload = function beforeUnload(ev) {
+ var msg = "Closing this window will make the panel unusable.\n" +
+ "Suggest you stay on the page and minimize this window instead";
+
+ ev.preventDefault();
+ ev.returnValue = msg;
+ return msg;
+};
+
+/**************************************/
+B220CardatronControl.prototype.cardatronOnLoad = function cardatronOnLoad() {
+ /* Initializes the Cardatron Control window and user interface */
+ var body;
+ var box;
+ var e;
+ var x;
+
+ this.doc = this.window.document;
+ body = this.$$("PanelSurface");
+
+ this.bufferReadLamp = new NeonLampBox(body, null, null, "BufferReadLamp");
+ this.bufferReadLamp.setCaption("BR", true);
+ this.bufferWriteLamp = new NeonLampBox(body, null, null, "BufferWriteLamp");
+ this.bufferWriteLamp.setCaption("BW", true);
+ this.formatLoadLamp = new NeonLampBox(body, null, null, "FormatLoadLamp");
+ this.formatLoadLamp.setCaption("FL", true);
+ this.outputUnitLamp = new NeonLampBox(body, null, null, "OutputUnitLamp");
+ this.outputUnitLamp.setCaption("OU", true);
+
+ this.unitDesignate4Lamp = new NeonLampBox(body, null, null, "UnitDesignate4Lamp");
+ this.unitDesignate4Lamp.setCaption("UD4", true);
+ this.unitDesignate2Lamp = new NeonLampBox(body, null, null, "UnitDesignate2Lamp");
+ this.unitDesignate2Lamp.setCaption("UD2", true);
+ this.unitDesignate1Lamp = new NeonLampBox(body, null, null, "UnitDesignate1Lamp");
+ this.unitDesignate1Lamp.setCaption("UD1", true);
+
+ this.relayDesignate8Lamp = new NeonLampBox(body, null, null, "RelayDesignate8Lamp");
+ this.relayDesignate8Lamp.setCaption("RD8", true);
+ this.relayDesignate4Lamp = new NeonLampBox(body, null, null, "RelayDesignate4Lamp");
+ this.relayDesignate4Lamp.setCaption("RD4", true);
+ this.relayDesignate2Lamp = new NeonLampBox(body, null, null, "RelayDesignate2Lamp");
+ this.relayDesignate2Lamp.setCaption("RD2", true);
+ this.relayDesignate1Lamp = new NeonLampBox(body, null, null, "RelayDesignate1Lamp");
+ this.relayDesignate1Lamp.setCaption("RD1", true);
+
+ // Events
+
+ this.window.addEventListener("beforeunload", B220CardatronControl.prototype.beforeUnload);
+ this.$$("ClearBtn").addEventListener("click",
+ B220Util.bindMethod(this, B220CardatronControl.prototype.ClearBtn_onClick));
+
+ this.clear();
+};
+
+/**************************************/
+B220CardatronControl.prototype.inputInitiate = function inputInitiate(unitNr, kDigit, wordSender) {
+ /* Initiates the read from one of the Cardatron input devices.
+ Returns 0 if device exists and the I/O was initiated; returns -1 if device
+ not present */
+
+ this.bufferReadLamp.set(1);
+ this.bufferWriteLamp.set(0);
+ this.formatLoadLamp.set(0);
+ this.outputUnitLamp.set(0);
+ this.setRelayDesignateLamps(0);
+ this.setUnitDesignateLamps(unitNr);
+ if (!this.inputUnit[unitNr]) {
+ return -1; // just terminate the I/O
+ } else {
+ this.inputUnit[unitNr].inputInitiate(kDigit, wordSender);
+ return 0;
+ }
+};
+
+/**************************************/
+B220CardatronControl.prototype.inputReadyInterrogate = function inputReadyInterrogate(unitNr) {
+ /* Interrogates the ready status of a Cardatron input device.
+ Returns -1 if device not present, 0 if device not ready, 1 if device is ready */
+
+ if (!this.inputUnit[unitNr]) {
+ return -1;
+ } else {
+ return (this.inputUnit[unitNr].inputReadyInterrogate() ? 1 : 0);
+ }
+};
+
+/**************************************/
+B220CardatronControl.prototype.inputFormatInitiate = function inputFormatInitiate(
+ unitNr, kDigit, requestNextWord, signalFinished) {
+ /* Initiates loading a format band for one of the Cardatron input devices.
+ Returns 0 if device exists and the I/O was initiated; returns -1 if device
+ not present */
+
+ this.bufferReadLamp.set(0);
+ this.bufferWriteLamp.set(0);
+ this.formatLoadLamp.set(1);
+ this.outputUnitLamp.set(0);
+ this.setRelayDesignateLamps(0);
+ this.setUnitDesignateLamps(unitNr);
+ if (!this.inputUnit[unitNr]) {
+ return -1; // just terminate the I/O
+ } else {
+ this.inputUnit[unitNr].inputFormatInitiate(kDigit, requestNextWord, signalFinished);
+ return 0;
+ }
+};
+
+/**************************************/
+B220CardatronControl.prototype.outputInitiate = function outputInitiate(
+ unitNr, kDigit, tDigit, requestNextWord, signalFinished) {
+ /* Initiates writing to one of the Cardatron output devices */
+
+ this.bufferReadLamp.set(0);
+ this.bufferWriteLamp.set(1);
+ this.formatLoadLamp.set(0);
+ this.outputUnitLamp.set(1);
+ this.setRelayDesignateLamps(tDigit);
+ if (!this.outputUnit[unitNr]) {
+ // ?? what happens if the unitNr is invalid? Halt?
+ signalFinished(); // just terminate the I/O
+ } else {
+ this.setUnitDesignateLamps(unitNr);
+ this.outputUnit[unitNr].outputInitiate(kDigit, tDigit, requestNextWord, signalFinished);
+ }
+};
+
+/**************************************/
+B220CardatronControl.prototype.outputReadyInterrogate = function outputReadyInterrogate(unitNr) {
+ /* Interrogates the ready status of a Cardatron output device */
+
+ if (!this.outputUnit[unitNr]) {
+ // ?? what happens if the unitNr is invalid? Halt?
+ return false;
+ } else {
+ return this.outputUnit[unitNr].outputReadyInterrogate();
+ }
+};
+
+/**************************************/
+B220CardatronControl.prototype.outputFormatInitiate = function outputFormatInitiate(
+ unitNr, kDigit, requestNextWord, signalFinished) {
+ /* Initiates loading a format band for one of the Cardatron output devices */
+
+ this.bufferReadLamp.set(0);
+ this.bufferWriteLamp.set(0);
+ this.formatLoadLamp.set(1);
+ this.outputUnitLamp.set(1);
+ this.setRelayDesignateLamps(0);
+ if (!this.outputUnit[unitNr]) {
+ // ?? what happens if the unitNr is invalid? Halt?
+ signalFinished(); // just terminate the I/O
+ } else {
+ this.setUnitDesignateLamps(unitNr);
+ this.outputUnit[unitNr].outputFormatInitiate(kDigit, requestNextWord, signalFinished);
+ }
+};
+
+/**************************************/
+B220CardatronControl.prototype.shutDown = function shutDown() {
+ /* Shuts down the panel */
+ var x;
+
+ if (this.inputUnit) {
+ for (x=this.inputUnit.length-1; x>=0; --x) {
+ if (this.inputUnit[x]) {
+ this.inputUnit[x].shutDown();
+ this.inputUnit[x] = null;
+ }
+ }
+ }
+
+ if (this.outputUnit) {
+ for (x=this.outputUnit.length-1; x>=0; --x) {
+ if (this.outputUnit[x]) {
+ this.outputUnit[x].shutDown();
+ this.outputUnit[x] = null;
+ }
+ }
+ }
+
+ this.window.removeEventListener("beforeunload", B220CardatronControl.prototype.beforeUnload);
+ this.window.close();
+};
\ No newline at end of file
diff --git a/webUI/B220CardatronInput.css b/webUI/B220CardatronInput.css
new file mode 100644
index 0000000..ebcd2e0
--- /dev/null
+++ b/webUI/B220CardatronInput.css
@@ -0,0 +1,116 @@
+/***********************************************************************
+* retro-220/webUI B220CardatronInput.css
+************************************************************************
+* Copyright (c) 2017, Paul Kimpel.
+* Licensed under the MIT License,
+* see http://www.opensource.org/licenses/mit-license.php
+************************************************************************
+* Burroughs 220 emulator Cardatron Input Unit web interface style sheet.
+************************************************************************
+* 2017-05-19 P.Kimpel
+* Original version, from retro-205 D205CardatronInput.css.
+***********************************************************************/
+
+#readerBody {
+ height: 100%;
+ min-height: 100%;
+ overflow: hidden;
+ padding: 0}
+
+#CIDiv {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ margin: 4px}
+
+#CIStopBtn {
+ position: absolute;
+ top: 8px;
+ left: 8px}
+
+#CIStartBtn {
+ position: absolute;
+ top: 8px;
+ left: 76px}
+
+#NoFormatLamp {
+ top: 16px;
+ left: 160px}
+
+#StartMachineLamp {
+ top: 0;
+ left: 208px}
+#ReloadLockoutLamp {
+ top: 0;
+ left: 240px}
+#FormatLockoutLamp {
+ top: 0;
+ left: 272px}
+
+#FormatSelect4Lamp {
+ top: 28px;
+ left: 208px}
+#FormatSelect2Lamp {
+ top: 28px;
+ left: 240px}
+#FormatSelect1Lamp {
+ top: 28px;
+ left: 272px}
+
+#FormatSelectDiv {
+ position: absolute;
+ text-align: center;
+ font-size: 5pt;
+ font-weight: bold;
+ top: 16px;
+ left: 304px}
+#FormatColumnDiv {
+ position: absolute;
+ text-align: center;
+ font-size: 5pt;
+ font-weight: bold;
+ top: 16px;
+ left: 388px}
+
+#ClearBtn {
+ top: 8px;
+ right: 8px}
+#ClearBtnCaption {
+ top: 44px;
+ right: 8px;
+ width: 32px}
+
+#CIStackerDiv {
+ position: absolute;
+ top: 58px;
+ left: 8px;
+ right: 8px;
+ bottom: auto}
+
+#CIFileSelector {
+ width: 100%;
+ color: white;
+ border: 1px solid white}
+
+#CIHopperBar {
+ margin-top: 4px;
+ width: 100%;
+ height: 16px;
+ border: 1px solid white}
+
+#CIHopperCaption {
+ position: absolute;
+ right: 4px;
+ top: 30px;
+ z-index: 1;
+ height: 12px;
+ color: black;
+ font-weight: bold}
+
+#CIOutHopperFrame {
+ margin-top: 4px;
+ width: 100%;
+ height: 35px;
+ border: 1px solid white}
diff --git a/webUI/B220CardatronInput.html b/webUI/B220CardatronInput.html
new file mode 100644
index 0000000..d7ee40d
--- /dev/null
+++ b/webUI/B220CardatronInput.html
@@ -0,0 +1,147 @@
+
+
+
+
+Burroughs 220 Emulator Cardatron Input Unit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ FORMAT SELECT
+
+
+
+
+ FORMAT COL
+
+
+
+
CLEAR
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/webUI/B220CardatronInput.js b/webUI/B220CardatronInput.js
new file mode 100644
index 0000000..0badc92
--- /dev/null
+++ b/webUI/B220CardatronInput.js
@@ -0,0 +1,911 @@
+/***********************************************************************
+* 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.boundFinishCardRead = B220Util.bindMethod(this, B220CardatronInput.prototype.finishCardRead);
+ this.boundInputFormatWord = B220Util.bindMethod(this, B220CardatronInput.prototype.inputFormatWord);
+
+ 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=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);
+ this.setFormatSelectLamps(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 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.formatColumnList.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>> 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);
+ }
+ }
+};
+
+/**************************************/
+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.boundFinishCardRead);
+ }
+};
+
+/**************************************/
+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.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,
+ this.inputTransfer, wordReceiver);
+ }
+};
+
+/**************************************/
+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.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 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 >= this.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 = this.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 = this.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 = this.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.inputReadyInterrogate = function inputReadyInterrogate() {
+ /* Returns the current ready status of the input unit */
+
+ return this.bufferReady;
+};
+
+/**************************************/
+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 digit.
+ 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.infoIndex = 0; // start at the beginning of the format band
+ this.digitCount = 0;
+ this.togNumeric = true;
+ this.lastNumericDigit = 0;
+ this.pendingCall = signalFinished; // stash the call-back function
+ this.setFormatSelectLamps(this.selectedFormat);
+ setCallback(this.mnemonic, this, B220CardatronInput.drumTransferTime*1.67,
+ this.inputFormatTransfer, requestNextWord);
+ }
+
+};
+
+/**************************************/
+B220CardatronInput.prototype.inputFormatTransfer = function inputFormatTransfer(requestNextWord) {
+ /* Driver for sending words assembled from the info band to the Processor */
+
+ while (this.infoIndex < B220CardatronInput.trackSize) {
+ this.inputFormatWord(requestNextWord);
+ }
+
+ this.pendingCall(); // call signalFinished();
+ this.inputStop();
+};
+
+/**************************************/
+B220CardatronInput.prototype.inputFormatWord = function inputFormatWord(requestNextWord) {
+ /* Receives the next input format band word from the Processor and
+ stores the digits from the word into the next 11 format band digits */
+ var band = this.formatBand[this.selectedFormat];
+ var d; // current format digit
+ var ix = this.infoIndex; // current format band digit index
+ var word = requestNextWord(); // band word from Processor
+ var x; // word-digit index
+
+ 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.infoIndex = ix;
+};
+
+/**************************************/
+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();
+};
diff --git a/webUI/B220CardatronOutput.css b/webUI/B220CardatronOutput.css
new file mode 100644
index 0000000..dbd2556
--- /dev/null
+++ b/webUI/B220CardatronOutput.css
@@ -0,0 +1,150 @@
+/***********************************************************************
+* retro-220/webUI B220CardatronOutput.css
+************************************************************************
+* Copyright (c) 2017, Paul Kimpel.
+* Licensed under the MIT License,
+* see http://www.opensource.org/licenses/mit-license.php
+************************************************************************
+* Burroughs 220 emulator Cardatron Output Unit web interface style sheet.
+************************************************************************
+* 2017-01-19 P.Kimpel
+* Original version, from retro-205 D205CardatronOutput.css.
+***********************************************************************/
+
+#writerBody {
+ height: 100%;
+ min-height: 100%;
+ overflow: hidden;
+ padding: 0}
+
+#CODiv {
+ position: absolute;
+ top: 0px;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;}
+
+#COStopBtn {
+ position: absolute;
+ top: 8px;
+ left: 8px}
+
+#COStartBtn {
+ position: absolute;
+ top: 8px;
+ left: 76px}
+
+#COEndOfSupplyBtn {
+ position: absolute;
+ top: 8px;
+ left: 144px}
+
+#CORunoutSupplyBtn {
+ position: absolute;
+ line-height: 100%;
+ top: 8px;
+ left: 212px}
+
+#ClearBtn {
+ top: 8px;
+ left: 288px}
+#ClearBtnCaption {
+ top: 44px;
+ left: 288px;
+ width: 32px}
+
+#COSetZSBtn {
+ top: 8px;
+ left: 336px}
+#COSetZSBtnCaption {
+ top: 44px;
+ left: 336px;
+ width: 32px}
+
+#StartMachineLamp {
+ top: 4px;
+ left: 384px}
+#FormatSelect4Lamp {
+ top: 4px;
+ left: 416px}
+#FormatSelect2Lamp {
+ top: 4px;
+ left: 448px}
+#FormatSelect1Lamp {
+ top: 4px;
+ left: 480px}
+
+#COOptionsDiv {
+ position: absolute;
+ top: 4px;
+ right: 8px;
+ width: auto}
+
+#COGreenbarSpan {
+ display: none}
+
+#COSupplyMeter {
+ position: absolute;
+ top: 32px;
+ left: 384px;
+ right: 0;
+ width: calc(100% - 346px);
+ height: 18px;
+ border: 1px solid white}
+
+#COSupplyMeterCaption {
+ position: absolute;
+ top: 35px;
+ right: 18px;
+ z-index: 1;
+ height: 12px;
+ color: black;
+ font-weight: bold}
+
+#COSupplyFrame {
+ position: absolute;
+ top: 56px;
+ bottom: 8px;
+ left: 8px;
+ right: 8px;
+ width: calc(100% - 18px);
+ height: calc(100% - 66px);
+ border: 1px solid black}
+
+/* Zero-Suppress Panel */
+
+#COZSGroup {
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ right: 8px}
+
+#COZSColumnList {
+ border: 1px solid white;
+ color: black;
+ width: 100%}
+
+#COZSNotes {
+ position: absolute;
+ top: 40px;
+ width: 300px;
+ left: 0}
+
+#COZSButtonGroup {
+ position: absolute;
+ top: 40px;
+ right: 0}
+
+#COZSCancelBtn {
+ background-image: none;
+ background-color: #900;
+ height: 20px;
+ bottom: 0;
+ right: 64px}
+
+#COZSOKBtn {
+ background-image: none;
+ background-color: #060;
+ height: 20px;
+ bottom: 0;
+ right: 0}
diff --git a/webUI/B220CardatronOutput.html b/webUI/B220CardatronOutput.html
new file mode 100644
index 0000000..6d07001
--- /dev/null
+++ b/webUI/B220CardatronOutput.html
@@ -0,0 +1,63 @@
+
+
+
+
+Burroughs 220 Emulator Cardatron Output Unit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
CLEAR
+
+
+
SET ZS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/webUI/B220CardatronOutput.js b/webUI/B220CardatronOutput.js
new file mode 100644
index 0000000..5bf1352
--- /dev/null
+++ b/webUI/B220CardatronOutput.js
@@ -0,0 +1,1035 @@
+/***********************************************************************
+* retro-220/webUI B220CardatronOutput.js
+************************************************************************
+* Copyright (c) 2017, Paul Kimpel.
+* Licensed under the MIT License, see
+* http://www.opensource.org/licenses/mit-license.php
+************************************************************************
+* Burroughs 220 Cardatron Output Unit module.
+*
+* Defines a line-printer/card-punch peripheral unit type.
+*
+************************************************************************
+* 2017-05-19 P.Kimpel
+* Original version, from retro-205 D105CardatronOutput.js.
+***********************************************************************/
+"use strict";
+
+/**************************************/
+function B220CardatronOutput(mnemonic, unitIndex, config) {
+ /* Constructor for the Cardatron Output object */
+ var h = screen.availHeight*0.25; // window height
+ var tks = B220CardatronOutput.trackSize;
+ var w; // window width
+ var x;
+
+ this.config = config;
+ this.isPrinter = (mnemonic.indexOf("LP") == 0);
+ // Whether printer (true) or punch (false)
+ this.lineWidth = (this.isPrinter ? 120 : 80);
+ this.linesPerMinute = (this.isPrinter ? 150 : 100);
+ // IBM 407=150 LPM, IBM 523=100 CPM
+ this.mnemonic = mnemonic; // Unit mnemonic
+ this.unitIndex = unitIndex; // Output unit number
+
+ this.timer = 0; // setCallback() token
+ this.useAlgolGlyphs = false; // output using special Algol chars
+ this.useGreenbar = false; // format "greenbar" shading on the paper (printer only)
+ this.lpi = 6; // lines/inch (actually, lines per greenbar group, should be even)
+
+ this.boundOutputFormatWord = B220Util.bindMethod(this, B220CardatronOutput.prototype.outputFormatWord);
+ this.boundOutputWord = B220Util.bindMethod(this, B220CardatronOutput.prototype.outputWord);
+
+ this.clear();
+
+ // Line buffer for assembling the print/punch line
+ this.lineBuffer = new Uint8Array(B220CardatronOutput.trackSize + 4);
+
+ // Buffer drum: information band is [0], format bands are [1]-[5]
+ this.bufferDrum = new ArrayBuffer(tks*6);
+ 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
+
+ // Zero-Suppress controls
+ this.zsWindow = null; // handle for the tape loader window
+ this.zsCol = []; // list of 1-relative zero-suppress column numbers
+
+ // Device window
+ this.doc = null;
+ this.barGroup = null; // current greenbar line group
+ this.supplyDoc = null; // the content document for the supply frame
+ this.supply = null; // the "paper" or "cards" we print/punch on
+ this.endOfSupply = null; // dummy element used to control scrolling
+ this.supplyMeter = null; // element showing amount of paper/card supply remaining
+ w = (this.isPrinter ? 780 : 608);
+ this.window = window.open("../webUI/B220CardatronOutput.html", mnemonic,
+ "location=no,scrollbars,resizable,width=" + w + ",height=" + h +
+ ",left=" + (screen.availWidth - w) +
+ ",top=" + (screen.availHeight - h - (unitIndex-1)*24));
+ this.window.addEventListener("load",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.deviceOnLoad), false);
+}
+
+/**************************************/
+
+B220CardatronOutput.prototype.maxSupplyLines = 150000; // maximum output scrollback (about a box of paper)
+B220CardatronOutput.prototype.rtrimRex = /\s+$/; // regular expression for right-trimming lines
+B220CardatronOutput.prototype.theColorGreen = "#CFC"; // for greenbar shading
+
+B220CardatronOutput.trackSize = 316; // digits
+B220CardatronOutput.digitTime = 60/21600/B220CardatronOutput.trackSize;
+ // one digit time, about 8.8 µs at 21600rpm
+B220CardatronOutput.digitsPerMilli = 0.001/B220CardatronOutput.digitTime;
+ // digit times per millisecond: 113.8
+
+// Translate info band zone & numeric digits to ASCII character codes.
+// See U.S. Patent 3,072,328, January 8, 1963, L.L. Bewley et al, Figure 2;
+// and ElectroData Technical Newsletter #5 of February 14, 1958.
+B220CardatronOutput.prototype.outputXlate = [
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12
+ [0x20,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x20,0x23,0x40], // zone digit 0
+ [0x26,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x20,0x2E,0xA4], // zone digit 1
+ [0x2D,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x20,0x24,0x2A], // zone digit 2
+ [0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20], // zone digit 3
+ [0x30,0x2F,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x20,0x2C,0x25], // zone digit 4
+ [0x40,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20], // zone digit 5
+ [0x2D,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20], // zone digit 6
+ [0x20,0x20,0x39,0x2E,0xA4,0xA4,0x2E,0xA4,0x20,0x20,0x20,0x2E,0xA4], // zone digit 7
+ [0x20,0x4A,0x49,0x24,0x2A,0x2A,0x24,0x2A,0x20,0x20,0x20,0x24,0x2A], // zone digit 8
+ [0x20,0x20,0x52,0x27,0x25,0x25,0x2C,0x25,0x20,0x20,0x20,0x27,0x25], // zone digit 9
+ [0x2D,0x20,0x52,0x20,0x20,0x40,0x23,0x40,0x20,0x20,0x20,0x20,0x20], // zone digit 10
+ [0x20,0x26,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20], // zone digit 11
+ [0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20]]; // zone digit 12
+
+// 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,072,328, January 8, 1963, L.L. Bewley et al, Figure 12;
+// and ElectroData Technical Newsletter #5 of February 14, 1958.
+B220CardatronOutput.prototype.zoneXlate = [ // numeric digit:0 1 2 3 4 5 6 7 8 9
+ //0 1 2 3 4 5 6 7 8 9 // = = = = = = = = = =
+ [ 0, 1, 7, 1, 7, 7, 7, 7, 0, 1], // zone digit 0: A 9 . ¤ ¤ . ¤ 8 I
+ [ 1, 8, 8, 8, 8, 8, 8, 8, 1, 2], // zone digit 1: & J I $ * * $ * H R
+ [ 2, 4, 9, 4, 4, 9, 9, 9, 2, 4], // zone digit 2: - / R , % % , % Q Z
+ [10, 0, 10, 0, 0, 10, 10, 10, 2, 0], // zone digit 3: - 1 R # @ @ # @ Q 9
+ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], // zone digit 4: & A B C D E F G H I
+ [ 6, 2, 2, 2, 2, 2, 2, 2, 2, 2], // zone digit 5: - J K L M N O P Q R
+ [ 4, 11, 4, 4, 4, 4, 4, 4, 4, 4], // zone digit 6: 0 & S T U V W X Y Z
+ [ 6, 2, 2, 2, 2, 2, 2, 2, 2, 2], // zone digit 7: - J K L M N O P Q R
+ [ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0], // zone digit 8: 0 1 2 3 4 5 6 7 8 9
+ [ 1, 2, 1, 2, 2, 2, 2, 2, 1, 2]]; // zone digit 9: & J B L M N O P H R
+
+
+// Truthset for leading-zero suppression in output lines. A 1 indicates the
+// character is to be replaced by a space; a 0 indicates a non-suppressable
+// code and the end of leading-zero suppression.
+B220CardatronOutput.prototype.zeroSuppressSet = [
+ //0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00-0F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10-1F
+ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, // 20-2F
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 30-3F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40-4F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50-5F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60-6F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70-7F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-8F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90-9F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0-AF
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0-BF
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0-CF
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0-DF
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0-EF
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // F0-FF
+
+
+/**************************************/
+B220CardatronOutput.prototype.clear = function clear() {
+ /* Initializes (and if necessary, creates) the reader unit state */
+
+ this.ready = false; // ready status
+ this.bufferReady = true; // buffer drum info band is ready to accept data from Processor
+ this.writeRequested = false; // Processor has initiated a write, waiting for buffer
+ this.togNumeric = false; // current digit came from zone (false) or numeric (true) punches
+
+ this.supplyLeft = this.maxSupplyLines; // lines/cards remaining in output supply
+ this.runoutSupplyCount = 0; // counter for triple-formfeed => rip paper/empty hopper
+ this.groupLinesLeft = 0; // lines remaining in current greenbar group
+ this.topOfForm = false; // start new page flag
+ this.pendingSpaceBefore = -1; // pending carriage control (eat the initial space-before)
+
+ this.pendingCall = null; // stashed pending function reference
+ this.pendingParams = null; // stashed pending function parameters
+ this.kDigit = 0; // stashed reload-format band number
+ this.tDigit = 0; // stashed Tab Select relay digit
+ this.lastNumericDigit = 0; // last numeric digit encountered
+ this.infoIndex = 0; // 0-relative offset into info band on drum
+ this.selectedFormat = 0; // currently-selected format band
+};
+
+/**************************************/
+B220CardatronOutput.prototype.$$ = function $$(e) {
+ return this.doc.getElementById(e);
+};
+
+/**************************************/
+B220CardatronOutput.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);
+};
+
+/**************************************/
+B220CardatronOutput.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;
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.ClearBtn_onClick = function ClearBtn_onClick(ev) {
+ /* Handle the click event for the CLEAR button */
+
+ this.clearUnit();
+};
+
+/**************************************/
+B220CardatronOutput.prototype.setDeviceReady = function setDeviceReady(ready) {
+ /* Controls the ready-state of the printer/punch */
+
+ this.runoutSupplyCount = 0;
+ if (ready && !this.ready) {
+ B220Util.addClass(this.$$("COStartBtn"), "greenLit")
+ B220Util.removeClass(this.$$("COStopBtn"), "redLit");
+ this.ready = true;
+ } else if (!ready && this.ready) {
+ B220Util.removeClass(this.$$("COStartBtn"), "greenLit")
+ B220Util.addClass(this.$$("COStopBtn"), "redLit");
+ this.ready = false;
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.runoutSupply = function runoutSupply(ev) {
+ /* Handles an event to clear the supply from the printer/punch */
+
+ this.runoutSupplyCount = 0;
+ B220Util.removeClass(this.$$("COEndOfSupplyBtn"), "redLit");
+ this.supplyMeter.value = this.supplyLeft = this.maxSupplyLines;
+ this.groupLinesLeft = 0;
+ while (this.supply.firstChild) {
+ this.supply.removeChild(this.supply.firstChild);
+ }
+ if (!this.isPrinter) {
+ this.barGroup = this.doc.createElement("pre");
+ this.barGroup.className = "paper";
+ this.supply.appendChild(this.barGroup);
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.copySupply = function copySupply(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 barGroup = this.supply.firstChild;
+ var text = "";
+ var title = "B220 " + this.mnemonic + " Text Snapshot";
+ var win = window.open("./B220FramePaper.html", this.mnemonic + "-Snapshot",
+ "scrollbars,resizable,width=500,height=500");
+
+ while (barGroup) {
+ text += barGroup.textContent + "\n";
+ barGroup = barGroup.nextSibling;
+ }
+
+ win.moveTo((screen.availWidth-win.outerWidth)/2, (screen.availHeight-win.outerHeight)/2);
+ win.addEventListener("load", function() {
+ var doc;
+
+ doc = win.document;
+ doc.title = title;
+ doc.getElementById("Paper").textContent = text;
+ });
+
+ this.runoutSupply();
+ ev.preventDefault();
+ ev.stopPropagation();
+};
+
+/**************************************/
+B220CardatronOutput.prototype.appendLine = function appendLine(text) {
+ /* Appends one line, with a trailing new-line character, to the current
+ greenbar group, this.barGroup. This handles top-of-form and greenbar
+ highlighting */
+ var feed = "\n";
+ var skip = "";
+
+ if (this.isPrinter) {
+ if (this.groupLinesLeft <= 0) {
+ // Start the green half of a greenbar group
+ this.barGroup = this.doc.createElement("div");
+ this.supply.appendChild(this.barGroup);
+ this.groupLinesLeft = this.lpi;
+ if (!this.atTopOfForm) {
+ this.barGroup.className = "paper greenBar";
+ } else {
+ skip = "\f"; // prepend a form-feed to the line
+ this.atTopOfForm = false;
+ this.barGroup.className = "paper greenBar topOfForm";
+ }
+ } else if (this.groupLinesLeft*2 == this.lpi) {
+ // Start the white half of a greenbar group
+ this.barGroup = this.doc.createElement("div");
+ this.supply.appendChild(this.barGroup);
+ this.barGroup.className = "paper whiteBar";
+ } else if (this.groupLinesLeft == 1) {
+ feed = ""; // no linefeed at end of a bar group
+ } else if ((this.groupLinesLeft-1)*2 == this.lpi) {
+ feed = ""; // ditto
+ }
+ }
+
+ this.barGroup.appendChild(this.doc.createTextNode(skip + text + feed));
+ --this.groupLinesLeft;
+};
+
+/**************************************/
+B220CardatronOutput.prototype.printLine = function printLine(text, spaceBefore) {
+ /* Prints one line to the output, handling carriage control and greenbar
+ group completion. For now, SPACE 0 (overprinting) is treated as single-spacing */
+ var lines = 0;
+
+ if (spaceBefore < 0) { // skip to channel 1
+ while(this.groupLinesLeft > 0) {
+ ++lines;
+ this.appendLine("\xA0");
+ }
+ this.atTopOfForm = true;
+ } else { // space before print
+ lines = spaceBefore;
+ while (lines > 1) {
+ --lines;
+ --this.supplyLeft;
+ this.appendLine("\xA0");
+ }
+ }
+
+ this.appendLine(text || "\xA0");
+ if (this.supplyLeft > 0) {
+ this.supplyMeter.value = this.supplyLeft -= lines;
+ } else {
+ this.setDeviceReady(false);
+ B220Util.addClass(this.$$("COEndOfSupplyBtn"), "redLit");
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.finishWrite = function finishWrite() {
+ /* Callback function activated after a line is printed/punched to set the
+ buffer ready and reinitiate any pending write */
+
+ this.bufferReady = true;
+ this.startMachineLamp.set(0);
+ this.setFormatSelectLamps(0);
+ if (this.writeRequested) {
+ this.writeRequested = false;
+ this.pendingCall.apply(this, this.pendingParams);
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.initiateWrite = function initiateWrite() {
+ /* Initiate formatting of the output line/card from the buffer drum and
+ writing it to the output device. If zero-suppression has been configured,
+ step through the list of zero-suppression starting columns and replace any
+ suppressable leading characters in each field */
+ var band = this.formatBand[this.selectedFormat];
+ var fmax = band.length; // max info/format band index
+ var info = this.info; // local reference to info band
+ var lastNumeric = 0; // last numeric digit
+ var line; // ASCII line image
+ var lx = this.lineBuffer.length; // line image character index: start at end
+ var nu = true; // numeric toggle: start as numeric
+ var x = 0; // info/format band digit index
+ var z; // zero-suppress list index
+ var zLen = this.zsCol.length; // length of zsCol[]
+ var zNext; // 0-relative column index of next zero-suppress field
+
+ if (this.ready) {
+ this.startMachineLamp.set(1);
+
+ // Map buffer drum digits to ASCII character codes
+ for (x=0; x=0; --x) {
+ sheet = ss[x];
+ if (sheet.ownerNode.id == "PaperFrameStyles") {
+ rules = sheet.cssRules;
+ // Next, search through the rules for the one that controls greenbar shading.
+ for (x=rules.length-1; x>=0; --x) {
+ rule = rules[x];
+ if (rule.selectorText.toLowerCase() == "div.greenbar") {
+ // Found it: now flip the background color.
+ rule.style.backgroundColor = (useGreen ? this.theColorGreen : "white");
+ }
+ }
+ break; // out of for loop
+ }
+ }
+ this.$$("COGreenbarCheck").checked = useGreen;
+ this.useGreenbar = useGreen;
+};
+
+/**************************************/
+B220CardatronOutput.prototype.COStartBtn_onClick = function COStartBtn_onClick(ev) {
+ /* Handle the click event for the START button */
+
+ if (!this.ready && this.supplyLeft > 0) {
+ this.runoutSupplyCount = 0;
+ this.setDeviceReady(true);
+ if (!this.bufferReady) {
+ this.initiateWrite(); // have a full buffer, so output it
+ }
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.COStopBtn_onClick = function COStopBtn_onClick(ev) {
+ /* Handle the click event for the STOP button */
+
+ if (this.ready) {
+ this.runoutSupplyCount = 0;
+ this.setDeviceReady(false);
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.CORunoutSupplyBtn_onClick = function CORunoutSupplyBtn_onClick(ev) {
+ /* Handle the click event for the Skip To Heading button */
+
+ if (!this.ready) {
+ this.printLine("", -1);
+ this.endOfSupply.scrollIntoView();
+ if (++this.runoutSupplyCount >= 3) {
+ if (this.window.confirm("Do you want to clear the output from the device?")) {
+ this.runoutSupply();
+ }
+ }
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.COEndOfSupplyBtn_onClick = function COEndOfSupplyBtn_onClick(ev) {
+ /* Handle the click event for the End Of Supply button. If the printer/punch
+ is in and end-of-supply condition, this will make the printer/punch ready,
+ but it will still be in an EOS condition. The next time a print/punch line
+ is received, the EOS condition will force it not-ready again. You can print/
+ punch only one line at a time (presumably to the end of the current page).
+ The EOS condition can be cleared by clicking Supply Feed three times to "rip"
+ the paper or empty the punch hopper */
+
+ if (this.supplyLeft <= 0 && !this.ready) {
+ this.runoutSupplyCount = 0;
+ B220Util.removeClass(this.$$("COEndOfSupplyBtn"), "redLit");
+ this.setDeviceReady(true);
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.COAlgolGlyphsCheck_onClick = function COAlgolGlyphsCheck_onClick(ev) {
+ /* Handle the click event for the Algol Glyphs checkbox */
+ var prefs = this.config.getNode("Cardatron.units", this.unitIndex);
+
+ this.setAlgolGlyphs(ev.target.checked);
+ prefs.algolGlyphs = this.useAlgolGlyphs;
+ this.config.putNode("Cardatron.units", prefs, this.unitIndex);
+};
+
+/**************************************/
+B220CardatronOutput.prototype.COGreenbarCheck_onClick = function COGreenbarCheck_onClick(ev) {
+ /* Handle the click event for the Greenbar checkbox */
+ var prefs = this.config.getNode("Cardatron.units", this.unitIndex);
+
+ this.setGreenbar(ev.target.checked);
+ prefs.greenBar = this.useGreenbar;
+ this.config.putNode("Cardatron.units", prefs, this.unitIndex);
+};
+
+/**************************************/
+B220CardatronOutput.prototype.parseZeroSuppressList = function parseZeroSuppressList(text, alertWin) {
+ /* Parses a comma-delimited list of zero-suppression starting columns. If the list is
+ parsed successfully, returns an array of integers; otherwise returns null. An alert
+ is displayed on the window for the first parsing or out-of-sequence error */
+ var col;
+ var cols;
+ var copacetic = true;
+ var lastCol = 0;
+ var raw;
+ var x;
+ var zsCol = [];
+
+ if (text.search(/\S/) >= 0) {
+ cols = text.split(",");
+ for (x=0; x 0) { // ignore empty fields
+ col = parseInt(raw, 10);
+ if (isNaN(col)) {
+ copacetic = false;
+ alertWin.alert("Item #" + (x+1) + " (\"" + cols[x] + "\") is not numeric");
+ break; // out of for loop
+ } else if (col <= lastCol) {
+ copacetic = false;
+ alertWin.alert("Item #" + (x+1) + " (\"" + col + "\") is out of sequence");
+ break; // out of for loop
+ } else {
+ lastCol = col;
+ zsCol.push(col);
+ }
+ }
+ } // for x
+ }
+
+ return (copacetic ? zsCol : null);
+};
+
+/**************************************/
+B220CardatronOutput.prototype.COSetZSBtn_onClick = function COSetZSBtn_onClick(ev) {
+ /* Displays the Zero Suppress Panel window to capture a list of column numbers */
+ var $$$ = null; // getElementById shortcut for loader window
+ var doc = null; // loader window.document
+ var tron = this; // this B220CardatronOutput device instance
+ var win = this.window.open("B220CardatronZeroSuppressPanel.html", this.mnemonic + "ZS",
+ "location=no,scrollbars=no,resizable,width=508,height=120,left=" +
+ (this.window.screenX+16) +",top=" + (this.window.screenY+16));
+
+ function zsOK(ev) {
+ /* Handler for the OK button. Parses the list of column numbers; if successful,
+ sets tron.zsCol to the resulting array, stores the list in the system
+ configuration object, and closes the window */
+ var prefs;
+ var text = $$$("COZSColumnList").value;
+ var zsCol;
+
+ zsCol = tron.parseZeroSuppressList(text, win);
+ if (zsCol !== null) {
+ tron.zsCol = zsCol;
+ B220Util.removeClass(tron.$$("COSetZSBtn"), (zsCol.length > 0 ? "blackButton1" : "greenButton1"));
+ B220Util.addClass(tron.$$("COSetZSBtn"), (zsCol.length > 0 ? "greenButton1" : "blackButton1"));
+
+ // Store the new list in the system configuration object
+ text = zsCol.join(",");
+ prefs = tron.config.getNode("Cardatron.units", tron.unitIndex);
+ prefs.zeroSuppressCols = text;
+ tron.config.putNode("Cardatron.units", prefs, tron.unitIndex);
+ win.close();
+ }
+ }
+
+ function zsOnload (ev) {
+ /* Driver for the tape loader window */
+ var de;
+
+ doc = win.document;
+ doc.title = "retro-220 " + tron.mnemonic + " Zero-Suppress Panel";
+ de = doc.documentElement;
+ $$$ = function $$$(id) {
+ return doc.getElementById(id);
+ };
+
+ $$$("COZSColumnList").value = tron.zsCol.join(",");
+ $$$("COZSOKBtn").addEventListener("click", zsOK, false);
+ $$$("COZSCancelBtn").addEventListener("click", function zsCancelBtn(ev) {
+ win.close();
+ }, false);
+
+ win.resizeBy(de.scrollWidth - win.innerWidth,
+ de.scrollHeight - win.innerHeight);
+ $$$("COZSColumnList").focus();
+ }
+
+ // Outer block of COSetZSBtn_onClick
+ if (this.zsWindow && !this.zsWindow.closed) {
+ this.zsWindow.close();
+ }
+ this.zsWindow = win;
+ win.addEventListener("load", zsOnload, false);
+ win.addEventListener("unload", function zsUnload(ev) {
+ this.zsWindow = null;
+ }, false);
+};
+
+/**************************************/
+B220CardatronOutput.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;
+};
+
+/**************************************/
+B220CardatronOutput.prototype.deviceOnLoad = function deviceOnLoad() {
+ /* Initializes the printer/punch window and user interface */
+ var body;
+ var de;
+ var newChild;
+ var prefs = this.config.getNode("Cardatron.units", this.unitIndex);
+ var zsCol;
+
+ this.doc = this.window.document;
+ de = this.doc.documentElement;
+ this.doc.title = "retro-220 Cardatron " +
+ (this.isPrinter ? "Printer " : "Punch ") + this.mnemonic;
+
+ body = this.$$("CODiv");
+ this.supplyDoc = this.$$("COSupplyFrame").contentDocument;
+ this.supply = this.supplyDoc.getElementById("Paper");
+ this.endOfSupply = this.supplyDoc.getElementById("EndOfPaper");
+ this.supplyMeter = this.$$("COSupplyMeter");
+
+ newChild = this.supplyDoc.createElement("div");
+ newChild.id = this.supply.id;
+ this.supply.parentNode.replaceChild(newChild, this.supply);
+ this.supply = newChild;
+ if (!this.isPrinter) {
+ this.barGroup = this.doc.createElement("pre");
+ this.barGroup.className = "paper";
+ this.supply.appendChild(this.barGroup);
+ }
+
+ this.startMachineLamp = new NeonLamp(body, null, null, "StartMachineLamp");
+ this.startMachineLamp.setCaption("SM", 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.setAlgolGlyphs(prefs.algolGlyphs);
+ this.setGreenbar(prefs.greenBar);
+ zsCol = this.parseZeroSuppressList(prefs.zeroSuppressCols || "", this.window);
+ if (zsCol !== null) {
+ this.zsCol = zsCol;
+ if (zsCol.length > 0) {
+ B220Util.removeClass(this.$$("COSetZSBtn"), (zsCol.length > 0 ? "blackButton1" : "greenButton1"));
+ B220Util.addClass(this.$$("COSetZSBtn"), (zsCol.length > 0 ? "greenButton1" : "blackButton1"));
+ }
+ }
+
+ this.supplyMeter.max = this.maxSupplyLines;
+ this.supplyMeter.low = this.maxSupplyLines*0.1;
+ this.supplyMeter.value = this.supplyLeft = this.maxSupplyLines;
+ this.setDeviceReady(true);
+
+ this.window.addEventListener("beforeunload",
+ B220CardatronOutput.prototype.beforeUnload, false);
+ this.supply.addEventListener("dblclick",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.copySupply));
+ this.$$("COStopBtn").addEventListener("click",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.COStopBtn_onClick), false);
+ this.$$("COStartBtn").addEventListener("click",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.COStartBtn_onClick), false);
+ this.$$("COEndOfSupplyBtn").addEventListener("click",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.COEndOfSupplyBtn_onClick), false);
+ this.$$("CORunoutSupplyBtn").addEventListener("click",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.CORunoutSupplyBtn_onClick), false);
+ this.$$("COAlgolGlyphsCheck").addEventListener("click",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.COAlgolGlyphsCheck_onClick), false);
+ this.$$("COSetZSBtn").addEventListener("click",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.COSetZSBtn_onClick), false);
+ this.$$("ClearBtn").addEventListener("click",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.ClearBtn_onClick));
+
+ if (!this.isPrinter) {
+ this.$$("COEndOfSupplyBtn").innerHTML = "OUT OF CARDS";
+ this.$$("CORunoutSupplyBtn").innerHTML = "RUNOUT CARDS";
+ this.$$("COGreenbarSpan").style.display = "none";
+ this.$$("COGreenbarCheck").disabled = true;
+ } else {
+ this.$$("COEndOfSupplyBtn").innerHTML = "OUT OF PAPER";
+ this.$$("CORunoutSupplyBtn").innerHTML = "FORM FEED";
+ this.$$("COGreenbarSpan").style.display = "inline";
+ this.$$("COGreenbarCheck").addEventListener("click",
+ B220Util.bindMethod(this, B220CardatronOutput.prototype.COGreenbarCheck_onClick), false);
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.outputWord = function outputWord(
+ word, requestNextWord, signalFinished) {
+ /* Receives the next info band word from the Processor and stores its digits
+ under control of the selected format band. requestNextWord is the callback function
+ to request the next word from the Processor; signalFinished is the callback
+ function to tell the Processor we are done with data transfer */
+ var band; // local copy of format band
+ var d; // current digit
+ var eod; // done with current digit
+ var eow = false; // done with current word
+ var drumAddr; // buffer drum address
+ var info = this.info; // local reference to info band
+ var ix = this.infoIndex; // current info/format band index
+ var lastNumeric = this.lastNumericDigit;
+ var latency = 0; // drum latency for first digit of a word
+ var nu = this.togNumeric; // numeric vs. zone digit toggle
+ var x = 0; // word-digit index
+
+ band = this.formatBand[this.selectedFormat];
+ // For the first digit of a word, note the current buffer drum digit address
+ drumAddr = (performance.now()*B220CardatronOutput.digitsPerMilli) % B220CardatronOutput.trackSize;
+ latency = (ix - drumAddr + B220CardatronOutput.trackSize) % B220CardatronOutput.trackSize;
+
+ // Loop through the digits in the word, processing each one
+ do {
+ eod = false;
+ d = word % 0x10;
+ word = (word-d)/0x10;
+
+ // Loop through the format band digits until the current word-digit is consumed
+ do {
+ if (ix >= info.length) {
+ eow = eod = true;
+ } else {
+ // Translate the current digit
+ switch (band[ix]) {
+ case 0: // insert 0 digit
+ nu = !nu;
+ info[ix] = lastNumeric = 0;
+ ++ix;
+ // we are not done with the current word-digit yet...
+ break;
+ case 1: // translate zone/numeric digit
+ if (nu) {
+ // Numeric digit: straight translation except for 3-8 and 4-8 punches
+ nu = false; // next is a zone digit
+ info[ix] = lastNumeric = d;
+ } else {
+ // Zone digit: requires special handling in the sign-digit position
+ // and if the corresponding numeric digit came from a 3-8 or 4-8 punch
+ nu = true; // next is a numeric digit
+ if (x > 9) {
+ // For a zone digit in the sign position, store a 5 (+) or 6 (-)
+ // so that the sign will be printed/punched as a zone 11/12.
+ info[ix] = (d & 0x01) + 5;
+ } else if (d > 3) {
+ info[ix] = this.zoneXlate[d][lastNumeric];
+ } else {
+ // If the prior numeric digit was 3 or 4 AND this zone is 0-3,
+ // store an 11 or 12 for the prior digit to indicate a 3-8 or 4-8 punch.
+ if (ix > 0 && (lastNumeric == 3 || lastNumeric == 4)) {
+ info[ix-1] = lastNumeric+8;
+ }
+ info[ix] = this.zoneXlate[d][lastNumeric];
+ }
+ }
+ ++ix;
+ eod = true;
+ break;
+ case 2: // translate numerically
+ nu = true; // next is forced to be another numeric digit
+ info[ix] = lastNumeric = d;
+ ++ix;
+ eod = true;
+ break;
+ default: // (3) delete the digit -- store nothing
+ ++ix;
+ eod = true;
+ break;
+ } // switch band[ix]
+ }
+ } while (!eod);
+
+ if (x < 10) {
+ ++x;
+ } else {
+ eow = true;
+ }
+ } while (!eow);
+
+ this.lastNumericDigit = lastNumeric;
+ this.togNumeric = nu;
+ this.infoIndex = ix;
+ if (ix < info.length) {
+ // Delay requesting the next word for the amount of time until buffer drum was in position
+ setCallback(this.mnemonic, this, latency/B220CardatronOutput.digitsPerMilli,
+ requestNextWord, this.boundOutputWord);
+ // requestNextWord(this.boundOutputWord); // until we get the timing fixed
+ } else {
+ // At end of info band -- finish the data transfer and start the I/O to the device
+ this.initiateWrite();
+ signalFinished();
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.outputInitiate = function outputInitiate(
+ kDigit, tDigit, requestNextWord, signalFinished) {
+ /* Initiates a write to the buffer drum on this unit. kDigit is the
+ second numeric digit from the instruction word containing the format number.
+ tDigit is the first numeric digit from the instruction word and sets the
+ Tab Select relays for the IBM device. We use it for carriage control as
+ implemented by the standard Burroughs 205/220 plugboard for the 407:
+ 0 = No relays (single space before printing)
+ 1 = Relay 1 (eject page after printing)
+ 2 = Relay 2 (single space before and after printing)
+ 3 = Relay 3 (eject page before printing)
+ 4 = Relay 4 (double space before printing)
+ 5 = Relay 5 (skip to channel 2 before printing)
+ 6 = Relay 2+4 (double space before and single space after printing)
+ 7 = Relay 3+5 (skip to channel 3 before printing)
+ 8 = same as 0
+ 9 = same as 1
+ Carriage control is ignored for punch devices and always set to single spacing.
+ requestNextWord is the callback function that will request the next word from the
+ processor. signalFinished is the callback function that tells the Processor
+ we're done. If the buffer is not ready, simply sets the writeRequested flag
+ and exits after stashing kDigit, tDigit, and the callbacks. Note that if the
+ device is not ready, the buffer can still be loaded */
+
+ if (!this.bufferReady) {
+ this.writeRequested = true; // wait for the buffer to be emptied
+ this.pendingCall = outputInitiate;
+ this.pendingParams = [kDigit, tDigit, requestNextWord, signalFinished];
+ } else if (kDigit > 9) {
+ signalFinished();
+ } else {
+ this.kDigit = kDigit;
+ this.tDigit = (this.isPrinter ? tDigit : 0);
+ this.selectedFormat = ((kDigit >>> 1) & 0x07) + 1;
+ this.setFormatSelectLamps(this.selectedFormat);
+ this.togNumeric = true;
+ this.lastNumericDigit = 0;
+ this.bufferReady = false;
+ this.clearInfoBand();
+ setCallback(this.mnemonic, this,
+ B220CardatronOutput.trackSize/B220CardatronOutput.digitsPerMilli*2.5,
+ requestNextWord, this.boundOutputWord); // request the first data word
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.outputReadyInterrogate = function outputReadyInterrogate() {
+ /* Returns the current ready status of the output unit */
+
+ return this.bufferReady;
+};
+
+/**************************************/
+B220CardatronOutput.prototype.outputFormatInitiate = function outputFormatInitiate(
+ kDigit, requestNextWord, signalFinished) {
+ /* Initiates the loading of a format band on this unit. kDigit is the
+ second numeric digit from the instruction word, the low-order bit is ignored
+ and the remaining three bits indicate 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 (kDigit > 9) {
+ signalFinished();
+ } else {
+ this.kDigit = kDigit;
+ this.selectedFormat = ((kDigit >>> 1) & 0x07) + 1;
+ this.infoIndex = 0; // start at the beginning of the format band
+ this.togNumeric = true;
+ this.lastNumericDigit = 0;
+ this.setFormatSelectLamps(this.selectedFormat);
+ setCallback(this.mnemonic, this,
+ B220CardatronOutput.trackSize/B220CardatronOutput.digitsPerMilli*2.5,
+ requestNextWord, this.boundOutputFormatWord); // request the first format word
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.outputFormatWord = function outputFormatWord(
+ word, requestNextWord, signalFinished) {
+ /* Receives the next output format band word from the Processor and
+ stores the digits from the word into the next 11 format band digits */
+ var band = this.formatBand[this.selectedFormat];
+ var d; // current format digit
+ var ix = this.infoIndex; // current format band digit index
+ var x; // word-digit index
+
+ for (x=0; x<11; ++x) {
+ d = word % 0x10;
+ word = (word-d)/0x10;
+ if (ix < B220CardatronOutput.trackSize) {
+ band[ix++] = d % 4;
+ } else {
+ break; // out of for loop
+ }
+ } // for x
+
+ this.infoIndex = ix;
+ if (ix < B220CardatronOutput.trackSize) {
+ requestNextWord(this.boundOutputFormatWord);
+ } else {
+ this.setFormatSelectLamps(0);
+ signalFinished();
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.clearUnit = function clearUnit() {
+ /* Clears the output unit and resets all internal state */
+
+ this.$$("CRFileSelector").value = null; // reset the control so the same file can be reloaded
+ this.bufferReady = true;
+ this.setDeviceReady(true);
+ this.startMachineLamp.set(0);
+ this.setFormatSelectLamps(0);
+
+ this.clear();
+ if (this.timer) {
+ clearCallback(this.timer);
+ this.timer = 0;
+ }
+};
+
+/**************************************/
+B220CardatronOutput.prototype.shutDown = function shutDown() {
+ /* Shuts down the device */
+
+ if (this.timer) {
+ clearCallback(this.timer);
+ }
+ this.window.removeEventListener("beforeunload", B220CardatronOutput.prototype.beforeUnload);
+ this.window.close();
+ if (this.zsWindow && !this.zsWindow.closed) {
+ this.zsWindow.close();
+ }
+};
diff --git a/webUI/B220CardatronZeroSuppressPanel.html b/webUI/B220CardatronZeroSuppressPanel.html
new file mode 100644
index 0000000..16138aa
--- /dev/null
+++ b/webUI/B220CardatronZeroSuppressPanel.html
@@ -0,0 +1,50 @@
+
+
+
+
+Burroughs 220 Emulator Cardatron Zero-Suppress Panel
+
+
+
+
+
+
+
+
+
+
+
+ Begin zero-suppression in these columns:
+
+
+
+ Enter a comma-delimited list of 1-relative column numbers in ascending
+ order; click OK to save and apply to the device.
+