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. +
+ +
+ +   + +
+
+ + + \ No newline at end of file diff --git a/webUI/B220Common.css b/webUI/B220Common.css index 838fcc6..7c8f08e 100644 --- a/webUI/B220Common.css +++ b/webUI/B220Common.css @@ -58,7 +58,7 @@ BODY.deviceBody { overflow: hidden; margin: 0; padding: 0; - background-color: #246; + background-color: #678; /* IBM 1950s blue-gray equipment color */ color: white} H1 { diff --git a/webUI/B220ConsolePrinter.js b/webUI/B220ConsolePrinter.js index c8ce95f..cb61868 100644 --- a/webUI/B220ConsolePrinter.js +++ b/webUI/B220ConsolePrinter.js @@ -509,6 +509,9 @@ B220ConsolePrinter.prototype.receiveChar = function receiveChar(char, successor) this.printChar(this.suppressLZ ? 0x00 : char); break; + case 0x02: // blank (non-print) + break; + case 0x15: // form-feed delay *= 4; this.suppressLZ = 0; diff --git a/webUI/B220Manifest.appcache b/webUI/B220Manifest.appcache index 43dd083..2b8a827 100644 --- a/webUI/B220Manifest.appcache +++ b/webUI/B220Manifest.appcache @@ -1,20 +1,20 @@ CACHE MANIFEST -# retro-220 emulator 0.01, 2017-05-13 18:30 +# retro-220 emulator 0.01a, 2017-05-20 18:30 CACHE: ../emulator/B220Processor.js B220.css B220.html B220.js -#B220CardatronControl.css -#B220CardatronControl.html -#B220CardatronControl.js -#B220CardatronInput.css -#B220CardatronInput.html -#B220CardatronInput.js -#B220CardatronOutput.css -#B220CardatronOutput.html -#B220CardatronOutput.js -#B220CardatronZeroSuppressPanel.html +B220CardatronControl.css +B220CardatronControl.html +B220CardatronControl.js +B220CardatronInput.css +B220CardatronInput.html +B220CardatronInput.js +B220CardatronOutput.css +B220CardatronOutput.html +B220CardatronOutput.js +B220CardatronZeroSuppressPanel.html B220Common.css #B220ConsoleInput.css #B220ConsoleInput.js diff --git a/webUI/B220SystemConfig.js b/webUI/B220SystemConfig.js index c811537..fec8f74 100644 --- a/webUI/B220SystemConfig.js +++ b/webUI/B220SystemConfig.js @@ -75,7 +75,8 @@ B220SystemConfig.prototype.createConfigData = function createConfigData() { ConsoleOutput: { units: [ - {type: "TTYA", zeroSuppress: 0, mapMemory: 0, unitMask: 0x001, remote: 1, format: 0, columns: 72, tabs: "9,17,25,33,41,49,57,65,73,81"}, + {type: "TTYA", zeroSuppress: 0, mapMemory: 0, unitMask: 0x001, remote: 1, format: 0, + columns: 72, tabs: "9,17,25,33,41,49,57,65,73,81"}, {type: "NONE"}, {type: "NONE"}, {type: "NONE"}, @@ -96,8 +97,8 @@ B220SystemConfig.prototype.createConfigData = function createConfigData() { {type: "NONE"}, {type: "NONE"}, {type: "NONE"}, - {type: "LP3", algolGlyphs: true, greenBar: true, zeroSuppressCols: ""}, - {type: "CP2", algolGlyphs: true, greenBar: false, zeroSuppressCols: ""}, + {type: "NONE"}, + {type: "LP2", algolGlyphs: true, greenBar: true, zeroSuppressCols: ""}, {type: "CP1", algolGlyphs: true, greenBar: false, zeroSuppressCols: ""} ]},