From df29455c91004814d4edba4a2e33ac9529a164be Mon Sep 17 00:00:00 2001 From: Paul Kimpel Date: Sat, 29 Apr 2017 13:41:02 -0700 Subject: [PATCH] Commit version 0.00d: 1. Implement console teletype printer and paper tape punch devices. 2. Correct timing mechanism and operation complete processing for asynchronous (I/O) processor mode. 3. Delete extraneous alarm toggles from B220Processor. 4. Correct Reset And Transfer switch so it will work when the Processor is running. 5. Implement Hello World default program for SPO at address 0020. 6. Change keyboard keystroke assignments for B220ConsoleKeyboard. 7. Implement Reset-to-Defaults feature for B220SystemConfig. 8. Implement DOM Element.classList for B220Util class attribute manipulation. 9. Minor typo correction to BALGOL-Generator.bacg. --- emulator/B220Processor.js | 518 +++++++++++++++-------- software/BALGOL/BALGOL-Generator.bacg | 2 +- webUI/B220.html | 9 +- webUI/B220Common.css | 2 +- webUI/B220ConsoleKeyboard.html | 6 +- webUI/B220ConsoleKeyboard.js | 18 +- webUI/B220ConsolePrinter.css | 294 +++++++++++++ webUI/B220ConsolePrinter.html | 81 ++++ webUI/B220ConsolePrinter.js | 566 ++++++++++++++++++++++++++ webUI/B220ControlConsole.js | 140 +++++-- webUI/B220FramePaper.html | 48 +++ webUI/B220Manifest.appcache | 14 +- webUI/B220PanelUtil.js | 10 +- webUI/B220PaperTapePunch.css | 94 +++++ webUI/B220PaperTapePunch.html | 58 +++ webUI/B220PaperTapePunch.js | 324 +++++++++++++++ webUI/B220SystemConfig.css | 65 +-- webUI/B220SystemConfig.html | 481 +++++++++++++++++++++- webUI/B220SystemConfig.js | 160 ++++---- webUI/B220Util.js | 15 +- 20 files changed, 2538 insertions(+), 367 deletions(-) create mode 100644 webUI/B220ConsolePrinter.css create mode 100644 webUI/B220ConsolePrinter.html create mode 100644 webUI/B220ConsolePrinter.js create mode 100644 webUI/B220FramePaper.html create mode 100644 webUI/B220PaperTapePunch.css create mode 100644 webUI/B220PaperTapePunch.html create mode 100644 webUI/B220PaperTapePunch.js diff --git a/emulator/B220Processor.js b/emulator/B220Processor.js index 0c9d358..14beace 100644 --- a/emulator/B220Processor.js +++ b/emulator/B220Processor.js @@ -78,8 +78,8 @@ function B220Processor(config, devices) { this.IB = new B220Processor.Register(11*4, this, true); // memory Input Buffer // Processor throttling control and timing statistics - this.execTime = 0; // estimated internal processor time, ms - this.execLimit = 0; // current time slice limit on this.execTime, ms + this.execClock = 0; // emulated internal processor clock, ms + this.execLimit = 0; // current time slice limit on this.execClock, ms this.opTime = 0; // estimated time for current instruction, ms this.procStart = 0; // Javascript time that the processor started running, ms this.procTime = 0; // total internal running time for processor, ms @@ -106,12 +106,6 @@ function B220Processor(config, devices) { // Control Console Lamps this.digitCheckAlarm = new B220Processor.FlipFlop(this, false); - this.programCheckAlarm = new B220Processor.FlipFlop(this, false); - this.storageAlarm = new B220Processor.FlipFlop(this, false); - this.magneticTapeAlarm = new B220Processor.FlipFlop(this, false); - this.paperTapeAlarm = new B220Processor.FlipFlop(this, false); - this.cardatronAlarm = new B220Processor.FlipFlop(this, false); - this.highSpeedPrinterAlarm =new B220Processor.FlipFlop(this, false); this.systemNotReady = new B220Processor.FlipFlop(this, false); this.computerNotReady = new B220Processor.FlipFlop(this, false); @@ -190,7 +184,7 @@ function B220Processor(config, devices) { // Right-Hand Maintenance Panel Registers & Flip-Flops this.AX = new B220Processor.Register(10, this, false); // A exponent register - this.BI = new B220Processor.Register( 8, this, false); // paper tape decoder + this.BI = new B220Processor.Register( 8, this, false); // paper tape buffer inverters this.DX = new B220Processor.Register( 8, this, false); // D exponent register this.PA = new B220Processor.Register( 8, this, false); // PA register @@ -198,10 +192,10 @@ function B220Processor(config, devices) { this.AST = new B220Processor.FlipFlop(this, false); // asynchronous toggle this.CCT = new B220Processor.FlipFlop(this, false); // ?? toggle this.CRT = new B220Processor.FlipFlop(this, false); // Cardatron alarm toggle - this.DPT = new B220Processor.FlipFlop(this, false); // ?? digit pulse toggle + this.DPT = new B220Processor.FlipFlop(this, false); // decimal point toggle (SPO) this.EWT = new B220Processor.FlipFlop(this, false); // end of word toggle this.EXT = new B220Processor.FlipFlop(this, false); // fetch(0)/execute(1) toggle - this.HAT = new B220Processor.FlipFlop(this, false); // ?? toggle + this.HAT = new B220Processor.FlipFlop(this, false); // high-speed printer alarm toggle this.HCT = new B220Processor.FlipFlop(this, false); // halt control toggle, for SOR, SOH, IOM this.HIT = new B220Processor.FlipFlop(this, false); // high comparison toggle this.MAT = new B220Processor.FlipFlop(this, false); // multiple access toggle @@ -227,8 +221,8 @@ function B220Processor(config, devices) { // Context-bound routines this.boundUpdateLampGlow = B220Processor.bindMethod(this, B220Processor.prototype.updateLampGlow); - this.boundConsoleOutputSignDigit = B220Processor.bindMethod(this, B220Processor.prototype.consoleOutputSignDigit); - this.boundConsoleOutputNumberDigit= B220Processor.bindMethod(this, B220Processor.prototype.consoleOutputNumberDigit); + this.boundConsoleOutputSign = B220Processor.bindMethod(this, B220Processor.prototype.consoleOutputSign); + this.boundConsoleOutputChar = B220Processor.bindMethod(this, B220Processor.prototype.consoleOutputChar); this.boundConsoleOutputFinished = B220Processor.bindMethod(this, B220Processor.prototype.consoleOutputFinished); this.boundConsoleInputDigit = B220Processor.bindMethod(this, B220Processor.prototype.consoleInputDigit); this.boundConsoleReceiveDigit = B220Processor.bindMethod(this, B220Processor.prototype.consoleReceiveDigit); @@ -255,7 +249,7 @@ function B220Processor(config, devices) { * Global Constants * ***********************************************************************/ -B220Processor.version = "0.00c"; +B220Processor.version = "0.00d"; B220Processor.tick = 1000/200000; // milliseconds per clock cycle (200KHz) B220Processor.cyclesPerMilli = 1/B220Processor.tick; @@ -500,11 +494,6 @@ B220Processor.prototype.clear = function clear() { // Control Console Lamps this.digitCheckAlarm.set(0); - this.programCheckAlarm.set(0); - this.storageAlarm.set(0); - this.magneticTapeAlarm.set(0); - this.paperTapeAlarm.set(0); - this.cardatronAlarm.set(0); this.systemNotReady.set(0); this.computerNotReady.set(0); @@ -587,8 +576,13 @@ B220Processor.prototype.updateGlow = function updateGlow(beta) { /* Updates the lamp glow for all registers and flip-flops in the system. Beta is a bias in the range (0,1). For normal update use 0; to freeze the current state in the lamps use 1 */ + var clocking = (this.execClock < 0); var gamma = (this.RUT.value ? beta || 0 : 1); + if (clocking) { + this.clockIn(); + } + // Primary Registers this.A.updateGlow(gamma); this.B.updateGlow(gamma); @@ -602,11 +596,6 @@ B220Processor.prototype.updateGlow = function updateGlow(beta) { // Control Console Lamps this.digitCheckAlarm.updateGlow(gamma); - this.programCheckAlarm.updateGlow(gamma); - this.storageAlarm.updateGlow(gamma); - this.magneticTapeAlarm.updateGlow(gamma); - this.paperTapeAlarm.updateGlow(gamma); - this.cardatronAlarm.updateGlow(gamma); this.systemNotReady.updateGlow(gamma); this.computerNotReady.updateGlow(gamma); @@ -640,35 +629,42 @@ B220Processor.prototype.updateGlow = function updateGlow(beta) { } // Right-Hand Maintenance Panel Registers & Flip-Flops + this.ALT.updateGlow(gamma); + this.MET.updateGlow(gamma); + this.TAT.updateGlow(gamma); + this.PAT.updateGlow(gamma); + this.CRT.updateGlow(gamma); + this.HAT.updateGlow(gamma); + this.EXT.updateGlow(gamma); this.OFT.updateGlow(gamma); this.RPT.updateGlow(gamma); this.RUT.updateGlow(gamma); + if (this.rightPanelOpen) { this.AX.updateGlow(gamma); this.BI.updateGlow(gamma); this.DX.updateGlow(gamma); this.PA.updateGlow(gamma); - this.ALT.updateGlow(gamma); this.AST.updateGlow(gamma); this.CCT.updateGlow(gamma); this.CRT.updateGlow(gamma); this.DPT.updateGlow(gamma); this.EWT.updateGlow(gamma); - this.HAT.updateGlow(gamma); this.HCT.updateGlow(gamma); this.HIT.updateGlow(gamma); this.MAT.updateGlow(gamma); - this.MET.updateGlow(gamma); this.MNT.updateGlow(gamma); - this.PAT.updateGlow(gamma); this.PRT.updateGlow(gamma); this.PZT.updateGlow(gamma); this.SST.updateGlow(gamma); - this.TAT.updateGlow(gamma); this.UET.updateGlow(gamma); } + + if (clocking) { + this.clockOut(); + } }; @@ -682,14 +678,14 @@ B220Processor.Register = function Register(bits, p, invisible) { the timing members. "invisible" should be true if the register does not have a visible presence in the UI -- this will inhibit computing average lamp glow values for the register. - Note that it is important to increment this.execTime in the caller AFTER + Note that it is important to increment this.execClock in the caller AFTER setting new values in registers and flip-flops. This allows the average intensity to be computed based on the amount of time a bit was actually in that state */ this.bits = bits; // number of bits in register this.visible = (invisible ? false : true); - this.lastExecTime = 0; // time register was last set + this.lastExecClock = 0; // time register was last set this.p = p; // processor instance this.value = 0; // binary value of register: read-only externally @@ -723,10 +719,10 @@ B220Processor.Register.prototype.checkFC = function checkFC() { /**************************************/ B220Processor.Register.prototype.updateGlow = function updateGlow(beta) { - /* Updates the lamp glow averages based on this.p.execTime. Note that the + /* Updates the lamp glow averages based on this.p.execClock. Note that the glow is always aged by at least one clock tick. Beta is a bias in the range (0,1). For normal update, use 0; to freeze the current state, use 1 */ - var alpha = Math.min(Math.max(this.p.execTime-this.lastExecTime, B220Processor.tick)/ + var alpha = Math.min(Math.max(this.p.execClock-this.lastExecClock, B220Processor.tick)/ B220Processor.maxGlowTime + beta, 1.0); var alpha1 = 1.0-alpha; var b = 0; @@ -747,7 +743,7 @@ B220Processor.Register.prototype.updateGlow = function updateGlow(beta) { } } - this.lastExecTime = this.p.execTime; + this.lastExecClock = this.p.execClock; }; /**************************************/ @@ -786,7 +782,7 @@ B220Processor.Register.prototype.getBit = function getBit(bitNr) { B220Processor.Register.prototype.setBit = function setBit(bitNr, value) { /* Set a bit on or off in the register. Returns the new register value. Note that the glow is always aged by at least one clock tick */ - var alpha = Math.min(Math.max(this.p.execTime-this.lastExecTime, B220Processor.tick)/ + var alpha = Math.min(Math.max(this.p.execClock-this.lastExecClock, B220Processor.tick)/ B220Processor.maxGlowTime, 1.0); var bit = value%2; @@ -808,7 +804,7 @@ B220Processor.Register.prototype.setBit = function setBit(bitNr, value) { B220Processor.Register.prototype.flipBit = function flipBit(bitNr) { /* Complements a bit in the register. Returns the new register value. Note that the glow is always aged by at least one clock tick */ - var alpha = Math.min(Math.max(this.p.execTime-this.lastExecTime, B220Processor.tick)/ + var alpha = Math.min(Math.max(this.p.execClock-this.lastExecClock, B220Processor.tick)/ B220Processor.maxGlowTime, 1.0); var bit; @@ -847,13 +843,13 @@ B220Processor.FlipFlop = function FlopFlop(p, invisible) { Processor object, used to access the timing members. "invisible" should be true if the FF does not have a visible presence in the UI -- this will inhibit computing the average lamp glow value for it. - Note that it is important to increment this.execTime in the caller AFTER + Note that it is important to increment this.execClock in the caller AFTER setting new values in registers and flip-flops. This allows the average intensity to be computed based on the amount of time a bit was actually in that state */ this.visible = (invisible ? false : true); - this.lastExecTime = 0; // time register was last set + this.lastExecClock = 0; // time register was last set this.p = p; // processor instance this.value = 0; // binary value of register: read-only externally this.glow = 0; // average lamp glow value @@ -865,15 +861,14 @@ B220Processor.FlipFlop.prototype.updateGlow = function updateGlow(beta) { always aged by at least one clock tick. Beta is a bias in the range (0,1). For normal update, use 0; to freeze the current state, use 1. Returns the new average */ - var alpha = Math.min(Math.max(this.p.execTime-this.lastExecTime, B220Processor.tick)/ + var alpha = Math.min(Math.max(this.p.execClock-this.lastExecClock, B220Processor.tick)/ B220Processor.maxGlowTime + beta, 1.0); - var v = (this.value ? 1 : 0); if (this.visible) { - this.glow = this.glow*(1.0-alpha) + v*alpha; + this.glow = this.glow*(1.0-alpha) + this.value*alpha; } - this.lastExecTime = this.p.execTime; + this.lastExecClock = this.p.execClock; return this.glow; }; @@ -882,7 +877,7 @@ B220Processor.FlipFlop.prototype.set = function set(value) { /* Set the value of the FF. Use this rather than setting the value member directly so that average lamp glow can be computed. Returns the new value */ - this.value = value; + this.value = (value ? 1 : 0); if (this.visible) { this.updateGlow(0); } @@ -894,7 +889,7 @@ B220Processor.FlipFlop.prototype.set = function set(value) { B220Processor.FlipFlop.prototype.flip = function flip() { /* Complement the value of the FF. Returns the new value */ - return this.set(this.value ? 0 : 1); + return this.set(1-this.value); }; @@ -902,7 +897,33 @@ B220Processor.FlipFlop.prototype.flip = function flip() { * Timing and Statistics Functions * ***********************************************************************/ -// TBD +/**************************************/ +B220Processor.prototype.clockOut = function clockOut() { + /* Turns off the accumulation of emulated time for the current instruction + during I/O. Once the processor regains control, it should call this.clockIn + to increment this.execClock by the amount of elapsed time the I/O was + running asynchronously */ + var stamp = performance.now(); + + while (this.execClock > 0) { + this.execClock -= stamp; + } + + return stamp; +}; + +/**************************************/ +B220Processor.prototype.clockIn = function clockIn() { + /* Turns on the accumulation of emulated time for the current instruction + after an elapsed period of asynchronous I/O has completed */ + var stamp = performance.now(); + + while (this.execClock < 0) { + this.execClock += stamp; + } + + return stamp; +}; /*********************************************************************** @@ -927,7 +948,6 @@ B220Processor.prototype.setProgramCheck = function setProgramCheck(value) { /* Sets the Program Check alarm */ if (!this.ALARMSW) { - this.programCheckAlarm.set(value); this.ALT.set(value); if (value) { this.RUT.set(0); @@ -940,7 +960,6 @@ B220Processor.prototype.setStorageCheck = function setStorageCheck(value) { /* Sets the Storage Check alarm */ if (!this.ALARMSW) { - this.storageCheckAlarm.set(value); this.MET.set(value); if (value) { this.RUT.set(0); @@ -954,7 +973,6 @@ B220Processor.prototype.setMagneticTapeCheck = function setMagneticTapeCheck(val /* Sets the Magnetic Tape Check alarm */ if (!this.ALARMSW) { - this.magneticTapeAlarm.set(value); this.TAT.set(value); if (value) { this.RUT.set(0); @@ -967,7 +985,6 @@ B220Processor.prototype.setCardatronCheck = function setCardatronCheck(value) { /* Sets the Cardatron Check alarm */ if (!this.ALARMSW) { - this.cardatronAlarm.set(value); this.CRT.set(value); if (value) { this.RUT.set(0); @@ -980,7 +997,6 @@ B220Processor.prototype.setPaperTapeCheck = function setPaperTapeCheck(value) { /* Sets the Paper Tape Check alarm */ if (!this.ALARMSW) { - this.paperTapeAlarm.set(value); this.PAT.set(value); if (value) { this.RUT.set(0); @@ -993,8 +1009,7 @@ B220Processor.prototype.setHighSpeedPrinterCheck = function setHighSpeedPrinterC /* Sets the Cardatron Check alarm */ if (!this.ALARMSW) { - this.highSpeedPrinterAlarm.set(value); - this.CRT.set(value); + this.HAT.set(value); if (value) { this.RUT.set(0); } @@ -1866,63 +1881,116 @@ B220Processor.prototype.keyboardAdd = function keyboardAdd() { }; /**************************************/ -B220Processor.prototype.consoleOutputSignDigit = function consoleOutputSignDigit() { - /* Outputs the sign digit for a PTW (03) command and sets up to output the - first number digit. If the Shift Counter is already at 19, terminates the - output operation */ +B220Processor.prototype.consoleOutputSign = function consoleOutputSign(printSign) { + /* Outputs the sign character for a SPO (09) command and sets up to output the + first number digit */ var d; - var w = this.A.value % 0x10000000000; + var w; - if (this.togPO1) { // if false, we've probably been cleared - d = (this.A.value - w)/0x10000000000; // get the digit - this.A.set(w*0x10 + d); // rotate A+sign left one - if (this.SHIFT == 0x19) { // if the shift counter is already 19, we're done - this.togOK = this.togPO1 = 0; // for display only - this.consoleOutputFinished(); + this.clockIn(); + if (this.AST.value) { // if false, we've probably been cleared + d = this.bcdAdd(this.CCONTROL, 0x990); // decrement word count + this.CCONTROL += d%0x1000 - this.CCONTROL%0x1000; + this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR); + this.E.set(this.CADDR); + this.readMemory(); + if (this.MET.value) { // invalid address + this.ioComplete(); } else { - this.togOK = 1-this.togOK; // for dislay only - this.togPO2 = 1; // for display only - this.togDELAY = 0; // for display only - this.console.writeSignDigit(d, this.boundConsoleOutputNumberDigit); + this.D.set(this.IB.value); + this.opTime += 0.700; // estimate for memory access and rotation + w = this.D.value%0x10000000000; + d = (this.D.value - w)/0x10000000000; // get the sign digit + this.D.set(w*0x10 + d); // rotate D+sign left one + this.DC.set(0x10); + this.DPT.set(this.CCONTROL%0x10 == 1 && this.COP == 0x09); + this.LT1.set(this.LEADINGZEROESSW); // use LT1 for leading-zero suppression (probably not accurate) + this.EWT.set(0); + this.PZT.set(d == 2 && !this.HOLDPZTZEROSW); + this.PA.set(0x80 + d); // translate numerically + this.clockOut(); + printSign(this.PA.value, this.boundConsoleOutputChar); } } }; /**************************************/ -B220Processor.prototype.consoleOutputNumberDigit = function consoleOutputNumberDigit() { - /* Outputs a numeric digit for a PTW (03) command and sets up to output the - next number digit. If the Shift Counter is already at 19, terminates the +B220Processor.prototype.consoleOutputChar = function consoleOutputChar(printChar) { + /* Outputs the next character code for a SPO (09) command and sets up to output the + next number digit. If the Shift Counter is already at 20, terminates the output operation and sends a Finish signal */ var d; - var w = this.A.value % 0x10000000000; + var w; - if (this.togPO1) { // if false, we've probably been cleared - this.togOK = 1-this.togOK; // for dislay only - this.togPO2 = 1-this.togPO2; // for display only - this.togDELAY = 1-this.togDELAY;// for display only - if (this.SHIFT == 0x19) { // if the shift counter is already 19, we're done - d = (this.CADDR - this.CADDR%0x1000)/0x1000; - this.console.writeFinish(d, this.boundConsoleOutputFinished); + this.clockIn(); + if (this.AST.value) { // if false, we've probably been cleared + if (this.EWT.value) { + if (this.CCONTROL%0x1000 < 0x10) { + this.clockOut(); + printChar(0x35, this.boundConsoleOutputFinished); + } else { + this.C.add(1); + this.CADDR = this.C.value%0x10000; + this.clockOut(); + printChar(0x35, this.boundConsoleOutputSign); + } + } else if (this.PZT.value) { + // Output alphabetically + w = this.D.value % 0x1000000000; + d = (this.D.value - w)/0x1000000000; // get next 2 digits + this.D.set(w*0x100 + d); // rotate D+sign left by two + this.opTime += 0.060; // estimate for rotation + this.DC.add(0x02); + this.PA.set(d); + if (this.DC.value >= 0x20) { + this.EWT.set(1); + } + this.clockOut(); + printChar(d, this.boundConsoleOutputChar); } else { - d = (this.A.value - w)/0x10000000000; // get the digit - this.A.set(w*0x10 + d); // rotate A+sign left one - this.SHIFT = this.bcdAdd(this.SHIFT, 1); - this.console.writeNumberDigit(d, this.boundConsoleOutputNumberDigit); + // Output numerically + if (this.DPT.value && !this.LEADINGZEROESSW) { + // decimal point may be needed + d = this.CCONTROL >>> 12; + if (this.DC.value + d > 0x19) { + this.DPT.set(0); + this.LT1.set(0); // stop any zero-suppression + this.PA.set(0x03); // decimal point code + this.clockOut(); + printChar(0x03, this.boundConsoleOutputChar); + return; // early exit + } + } + + do { // suppress leading zeroes if necessary + w = this.D.value % 0x10000000000; + d = (this.D.value - w)/0x10000000000; // get a digit + this.D.value = w*0x10 + d; // rotate D+sign left by one + this.opTime += 0.065; // estimate for rotation + this.DC.add(0x01); + } while (d == 0 && this.LT1.value && this.DC.value < 0x20); + + this.LT1.set(0); + this.D.set(this.D.value); + d += 0x80; // translate numerically + this.PA.set(d); + if (this.DC.value >= 0x20) { + this.EWT.set(1); + } + this.clockOut(); + printChar(d, this.boundConsoleOutputChar); } } }; /**************************************/ B220Processor.prototype.consoleOutputFinished = function consoleOutputFinished() { - /* Handles the final cycle of an I/O operation and restores this.execTime */ + /* Handles the final cycle of console output */ - if (this.togOK || this.togPO1) { // if false, we've probably been cleared - this.togOK = 0; // for display only - this.togPO1 = this.togPO2 = 0; // for display only - this.togDELAY = 0; // for display only - this.stopIdle = 0; // turn IDLE lamp back off now that we're done - this.execTime += performance.now()*B220Processor.wordsPerMilli; - this.schedule(); + this.clockIn(); + if (this.AST.value) { // if false, we've probably been cleared + this.EWT.set(0); + this.ioComplete(); } }; @@ -2485,7 +2553,7 @@ B220Processor.prototype.fetch = function fetch() { this.EXT.set(1); } - this.execTime += 0.090; // fetch uniformly requires 90 us + this.execClock += 0.090; // fetch uniformly requires 90 us }; @@ -2493,18 +2561,6 @@ B220Processor.prototype.fetch = function fetch() { * Execute Module * ***********************************************************************/ -/**************************************/ -B220Processor.prototype.operationComplete = function operationComplete() { - /* Implements Operation Complete for the Execute cycle. If we're not locked - in Execute, switch to Fetch cycle next */ - - if (this.FETCHEXECUTELOCKSW != 1) { - this.EXT.set(0); - } - - this.execTime += this.opTime; -}; - /**************************************/ B220Processor.prototype.execute = function execute() { /* Implements the Execute cycle of the 220 processor. This is initiated @@ -2532,66 +2588,116 @@ B220Processor.prototype.execute = function execute() { case 0x00: //--------------------- HLT Halt this.RUT.set(0); this.opTime = 0.010; + this.operationComplete(); break; case 0x01: //--------------------- NOP No operation // do nothing this.opTime = 0.010; + this.operationComplete(); break; case 0x03: //--------------------- PRD Paper tape read this.setProgramCheck(1); + this.operationComplete(); break; case 0x04: //--------------------- PRB Paper tape read, branch this.setProgramCheck(1); + this.operationComplete(); break; case 0x05: //--------------------- PRI Paper tape read, inverse format this.setProgramCheck(1); + this.operationComplete(); break; case 0x06: //--------------------- PWR Paper tape write - this.setProgramCheck(1); + this.opTime = 0.185; // just a guess... + this.AST.set(1); + d = this.CCONTROL >>> 12; // get unit number + if (d == 0) { + d = 10; // xlate unit 0 to unit 10 + } + + this.clockOut(); + d = this.console.outputUnitSelect(d, this.boundConsoleOutputSign); + if (d < 0) { // no unit available -- set alarm and quit + this.clockIn(); + this.AST.set(0); + this.setPaperTapeCheck(1); + this.operationComplete(); + } + break; + + case 0x07: //--------------------- PWI Paper tape write interrogate, branch + d = this.CCONTROL >>> 12; // get unit number + if (d == 0) { + d = 10; // xlate unit 0 to unit 10 + } + d = this.console.outputUnitSelect(d, B220Processor.emptyFunction); + if (d < 0) { // if not ready, continue in sequence + this.opTime = 0.015; + } else { // if ready, branch to operand address + this.P.set(this.CADDR); + this.opTime = 0.035; + } + this.operationComplete(); break; case 0x08: //--------------------- KAD Keyboard add this.D.set(0); this.setStop(); + this.operationComplete(); break; case 0x09: //--------------------- SPO Supervisory print-out - this.setProgramCheck(1); + this.opTime = 0.185; // just a guess... + this.AST.set(1); + this.clockOut(); + d = this.console.outputUnitSelect(0, this.boundConsoleOutputSign); + if (d < 0) { // no unit available -- set alarm and quit + this.clockIn(); + this.AST.set(0); + this.setPaperTapeCheck(1); + this.operationComplete(); + } break; case 0x10: //--------------------- CAD/CAA Clear add/add absolute this.SUT.set(0); this.A.value = this.IB.value - this.IB.value%0x10000000000; // 0 with sign of IB this.integerAdd(this.CCONTROL % 0x10 == 1); + this.operationComplete(); break; case 0x11: //--------------------- CSU/CSA Clear subtract/subtract absolute this.SUT.set(1); this.A.value = this.IB.value - this.IB.value%0x10000000000; // 0 with sign of IB this.integerAdd(this.CCONTROL % 0x10 == 1); + this.operationComplete(); break; case 0x12: //--------------------- ADD/ADA Add/add absolute this.SUT.set(0); this.integerAdd(this.CCONTROL % 0x10 == 1); + this.operationComplete(); break; case 0x13: //--------------------- SUB/SUA Subtract/subtract absolute this.SUT.set(1); this.integerAdd(this.CCONTROL % 0x10 == 1); + this.operationComplete(); break; case 0x14: //--------------------- MUL Multiply this.integerMultiply(); + this.operationComplete(); break; case 0x15: //--------------------- DIV Divide this.integerDivide(); + this.operationComplete(); break; case 0x16: //--------------------- RND Round @@ -2611,93 +2717,115 @@ B220Processor.prototype.execute = function execute() { this.opTime += 0.060; // account for add cycle } this.R.set(0); // unconditionally clear R + this.operationComplete(); break; case 0x17: //--------------------- EXT Extract this.integerExtract(); + this.operationComplete(); break; case 0x18: //--------------------- CFA/CFR Compare field A/R this.setProgramCheck(1); + this.operationComplete(); break; case 0x19: //--------------------- ADL Add to location this.setProgramCheck(1); + this.operationComplete(); break; case 0x20: //--------------------- IBB Increase B, branch this.setProgramCheck(1); + this.operationComplete(); break; case 0x21: //--------------------- DBB Decrease B, branch this.setProgramCheck(1); + this.operationComplete(); break; case 0x22: //--------------------- FAD/FAA Floating add/add absolute this.SUT.set(0); this.floatingAdd(this.CCONTROL % 0x10 == 1); + this.operationComplete(); break; case 0x23: //--------------------- FSU/FSA Floating subtract/subtract absolute this.SUT.set(1); this.floatingAdd(this.CCONTROL % 0x10 == 1); + this.operationComplete(); break; case 0x24: //--------------------- FMU Floating multiply this.floatingMultiply(); + this.operationComplete(); break; case 0x25: //--------------------- FDV Floating divide this.floatingDivide(0); + this.operationComplete(); break; case 0x26: //--------------------- IFL Increase field location this.setProgramCheck(1); + this.operationComplete(); break; case 0x27: //--------------------- DFL Decrease field location this.setProgramCheck(1); + this.operationComplete(); break; case 0x28: //--------------------- DLB Decrease field location, load B this.setProgramCheck(1); + this.operationComplete(); break; case 0x29: //--------------------- RTF Record transfer this.setProgramCheck(1); + this.operationComplete(); break; case 0x30: //--------------------- BUN Branch, unconditionally this.P.set(this.CADDR); this.opTime = 0.035; + this.operationComplete(); break; case 0x31: //--------------------- BOF Branch, overflow this.setProgramCheck(1); + this.operationComplete(); break; case 0x32: //--------------------- BRP Branch, repeat this.setProgramCheck(1); + this.operationComplete(); break; case 0x33: //--------------------- BSA Branch, sign A this.setProgramCheck(1); + this.operationComplete(); break; case 0x34: //--------------------- BCH/BCL Branch, comparison high/low this.setProgramCheck(1); + this.operationComplete(); break; case 0x35: //--------------------- BCE/BCU Branch, comparison equal/unequal this.setProgramCheck(1); + this.operationComplete(); break; case 0x36: //--------------------- BFA Branch, field A this.setProgramCheck(1); + this.operationComplete(); break; case 0x37: //--------------------- BFR Branch, field R this.setProgramCheck(1); + this.operationComplete(); break; case 0x38: //--------------------- BCS Branch, control switch @@ -2707,128 +2835,155 @@ B220Processor.prototype.execute = function execute() { this.opTime += 0.020; this.P.set(this.CADDR); } + this.operationComplete(); break; case 0x39: //--------------------- SO*/IOM Set overflow remember/halt, Interrogate overflow mode this.setProgramCheck(1); + this.operationComplete(); break; case 0x40: //--------------------- ST* Store A/R/B this.setProgramCheck(1); + this.operationComplete(); break; case 0x41: //--------------------- LDR Load R this.setProgramCheck(1); + this.operationComplete(); break; case 0x42: //--------------------- LDB/LBC Load B/B complement this.setProgramCheck(1); + this.operationComplete(); break; case 0x43: //--------------------- LSA Load sign A this.setProgramCheck(1); + this.operationComplete(); break; case 0x44: //--------------------- STP Store P this.setProgramCheck(1); + this.operationComplete(); break; case 0x45: //--------------------- CL* Clear A/R/B this.setProgramCheck(1); + this.operationComplete(); break; case 0x46: //--------------------- CLL Clear location this.setProgramCheck(1); + this.operationComplete(); break; case 0x48: //--------------------- SR* Shift right A/A and R/A with sign this.setProgramCheck(1); + this.operationComplete(); break; case 0x49: //--------------------- SL* Shift left A/A and R/A with sign this.setProgramCheck(1); + this.operationComplete(); break; case 0x50: //--------------------- MT* Magnetic tape search/field search/lane select/rewind this.setProgramCheck(1); + this.operationComplete(); break; case 0x51: //--------------------- MTC/MFC Magnetic tape scan/field scan this.setProgramCheck(1); + this.operationComplete(); break; case 0x52: //--------------------- MRD Magnetic tape read this.setProgramCheck(1); + this.operationComplete(); break; case 0x53: //--------------------- MRR Magnetic tape read, record this.setProgramCheck(1); + this.operationComplete(); break; case 0x54: //--------------------- MIW Magnetic tape initial write this.setProgramCheck(1); + this.operationComplete(); break; case 0x55: //--------------------- MIR Magnetic tape initial write, record this.setProgramCheck(1); + this.operationComplete(); break; case 0x56: //--------------------- MOW Magnetic tape overwrite this.setProgramCheck(1); + this.operationComplete(); break; case 0x57: //--------------------- MOR Magnetic tape overwrite, record this.setProgramCheck(1); + this.operationComplete(); break; case 0x58: //--------------------- MPF/MPB Magnetic tape position forward/backward/at end this.setProgramCheck(1); + this.operationComplete(); break; case 0x59: //--------------------- MIB/MIE Magnetic tape interrogate, branch/end of tape, branch this.setProgramCheck(1); + this.operationComplete(); break; case 0x60: //--------------------- CRD Card read this.setProgramCheck(1); + this.operationComplete(); break; case 0x61: //--------------------- CWR Card write this.setProgramCheck(1); + this.operationComplete(); break; case 0x62: //--------------------- CRF Card read, format load this.setProgramCheck(1); + this.operationComplete(); break; case 0x63: //--------------------- CWF Card write, format load this.setProgramCheck(1); + this.operationComplete(); break; case 0x64: //--------------------- CRI Card read interrogate, branch this.setProgramCheck(1); + this.operationComplete(); break; case 0x65: //--------------------- CWI Card write interrogate, branch this.setProgramCheck(1); + this.operationComplete(); break; case 0x66: //--------------------- HPW High speed printer write this.setProgramCheck(1); + this.operationComplete(); break; case 0x67: //--------------------- HPI High speed printer interrogate, branch this.setProgramCheck(1); + this.operationComplete(); break; default: //--------------------- Invalid op code -- set Program Check alarm this.setProgramCheck(1); + this.operationComplete(); break; } // switch this.COP - // Operation Complete: - this.operationComplete(); - /*************************************************************************** @@ -3245,6 +3400,51 @@ B220Processor.prototype.execute = function execute() { * Processor Run Control * ***********************************************************************/ +/**************************************/ +B220Processor.prototype.operationComplete = function operationComplete() { + /* Implements Operation Complete for the Execute cycle. If we're not locked + in Execute, switch to Fetch cycle next */ + + this.execClock += this.opTime; + if (this.FETCHEXECUTELOCKSW != 1) { + this.EXT.set(0); // set to FETCH state + } + + if (this.ORDERCOMPLEMENTSW) { + this.C.flipBit(16); // complement low order bit of op code + this.COP ^= 0x01; + } + + if (!this.RUT.value) { // halted + this.stop(); + } else if (this.SST.value) { + this.stop(); // single-stepping + } else if (this.SONSW) { + if (this.STOCSW) { // check for post-execute S-to-C stop + if (this.SUNITSSW) { + if (this.C.value%0x10 == this.S.value%0x10) { + this.stop(); + } + } else if (this.C.value%0x10000 == this.S.value) { + this.stop(); + } + } + } +}; + +/**************************************/ +B220Processor.prototype.ioComplete = function operationComplete() { + /* Implements completion of the Execute cycle for an I/O instruction that + has been executing asynchronously */ + + this.operationComplete(); + this.AST.set(0); + this.procTime += this.execClock; + if (this.RUT.value) { + this.schedule(); + } +}; + /**************************************/ B220Processor.prototype.run = function run() { /* Main execution control loop for the processor. Called from this.schedule() @@ -3260,39 +3460,12 @@ B220Processor.prototype.run = function run() { this.schedule() to restart execution again once memory transfers have completed */ - this.execLimit = this.execTime + B220Processor.timeSlice; + this.execLimit = this.execClock + B220Processor.timeSlice; do { - switch (1) { - case this.AST.value: // doing I/O -- just exit - this.execLimit = 0; - break; - - case this.EXT.value: // enter execute cycle + if (this.EXT.value) { // enter EXECUTE cycle this.execute(); - if (this.ORDERCOMPLEMENTSW) { - this.C.flipBit(16); // complement low order bit of op code - this.COP ^= 0x01; - } - - if (!this.RUT.value) { // halted - this.stop(); - } else if (this.SST.value) { - this.stop(); // single-stepping - } else if (this.SONSW) { - if (this.STOCSW) { // check for post-execute S-to-C stop - if (this.SUNITSSW) { - if (this.C.value%0x10 == this.S.value%0x10) { - this.stop(); - } - } else if (this.C.value%0x10000 == this.S.value) { - this.stop(); - } - } - } - break; - - default: // enter fetch cycle + } else { // enter FETCH cycle if (this.SONSW) { // check for post-fetch S-to-P stop if (this.STOPSW) { // must check before P is incremented in fetch() if (this.SUNITSSW) { @@ -3311,7 +3484,7 @@ B220Processor.prototype.run = function run() { } break; } - } while (this.execTime < this.execLimit); + } while (this.execClock < this.execLimit && !this.AST.value); }; /**************************************/ @@ -3342,19 +3515,14 @@ B220Processor.prototype.schedule = function schedule() { this.procSlackAvg*B220Processor.slackAlpha1; } - // Accumulate internal processor run time if it's been idle due to I/O. - while (this.procTime < 0) { - this.procTime += stamp; - } + this.procTime -= this.execClock; // prepare to accumulate internal processor time // Execute the time slice. - runStamp = stamp; // starting clock time for time slice - this.procTime -= this.execTime; // prepare to accumulate internal processor time + runStamp = stamp; // starting clock time for time slice this.run(); stamp = performance.now(); - this.procTime += this.execTime; // accumulate internal processor time for the slice this.procRunAvg = (stamp - runStamp)*B220Processor.slackAlpha + this.procRunAvg*B220Processor.slackAlpha1; @@ -3362,13 +3530,13 @@ B220Processor.prototype.schedule = function schedule() { if (!this.RUT.value) { // Processor is stopped, just inhibit delay averaging on next call and exit. this.lastDelayStamp = 0; + this.procTime += this.execClock; // accumulate internal processor time for the slice } else if (this.AST.value) { - // Processor is still running, but idle during I/O. + // Processor is still running, but idle during I/O. Do not update this.procTime. this.lastDelayStamp = 0; - while (this.procTime >= 0) { - this.procTime -= stamp; // keep the internal processor timer running - } } else { + this.procTime += this.execClock; // accumulate internal processor time for the slice + // The processor is still running, so schedule next time slice after a // throttling delay. delayTime is the number of milliseconds the // processor is running ahead of real-world time. Web browsers have a @@ -3378,7 +3546,7 @@ B220Processor.prototype.schedule = function schedule() { // hope). If the delay is greater than the minimum, setCallback() will // reschedule us after that delay. - delayTime = this.execTime - stamp; + delayTime = this.execClock - stamp; this.delayRequested = delayTime; this.delayLastStamp = stamp; this.scheduler = setCallback(this.mnemonic, this, delayTime, this.schedule); @@ -3391,12 +3559,13 @@ B220Processor.prototype.start = function start() { var stamp = performance.now(); if (this.poweredOn && !this.RUT.value && !this.AST.value && - !this.programCheckAlarm.value && !this.storageAlarm.value && - !this.magneticTapeAlarm.value && !this.cardatronAlarm.value && - !this.paperTapeAlarm.value && !this.highSpeedPrinterAlarm.value && + !this.digitCheckAlarm.value && + !this.ALT.value && !this.MET.value && + !this.TAT.value && !this.CRT.value && + !this.PAT.value && !this.HAT.value && !this.systemNotReady.value && !this.computerNotReady.value) { this.procStart = stamp; - this.execTime = stamp; + this.execClock = stamp; this.delayLastStamp = 0; this.delayRequested = 0; this.RUT.set(1); @@ -3469,7 +3638,7 @@ B220Processor.prototype.setCycle = function setCycle(cycle) { if (this.poweredOn) { if (!this.RUT.value) { - this.EXT.set(cycle ? 1 : 0); + this.EXT.set(cycle); } } }; @@ -3489,20 +3658,19 @@ B220Processor.prototype.resetRunTimer = function resetRunTimer() { /**************************************/ B220Processor.prototype.resetTransfer = function resetTransfer() { - /* Performs a Reset and Transfer operation, storing P in address 0000/04 - and C in 0000/64. Then branches to address 0001 */ - var w; + /* Initiates a Reset and Transfer operation, storing P in address 0000/04 + and C in 0000/64, then branching to address 0001. Always active, even + when running */ if (this.poweredOn) { + this.E.set(0x0000); + this.readMemory(); + this.IB.add((this.C.value % 0x10000)*0x10000 + (this.P.value % 0x10000) - + this.IB.value % 0x100000000); + this.writeMemory(); + this.P.set(0x0001); + this.EXT.set(0); // set to Fetch cycle if (!this.RUT.value) { - this.E.set(0x0000); - w = this.readMemory(); - w += (this.C.value % 0x10000)*0x10000 + (this.P.value % 0x10000) - - w % 0x100000000; - this.IB.set(w); - this.writeMemory(); - this.P.set(0x0001); - this.EXT.set(0); // set to Fetch cycle this.start(); } } @@ -3584,18 +3752,16 @@ B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() { this.MM[ 10] = 0x0000360200 // BT6 200 this.MM[ 11] = 0x0000370220 // BT7 220 this.MM[ 12] = 0x0000206980 // CU 6980 branch to loop-6 entry point + *************************************************/ // Hello World - this.MM[ 20] = 0x0000070500; // PTWF 0500 line feed - this.MM[ 21] = 0x0000640027; // CAD 27 - this.MM[ 22] = 0x0000030410; // PTW 0410 - this.MM[ 23] = 0x0000070800; // PTWF 0800 space - this.MM[ 24] = 0x0000640028; // CAD 28 - this.MM[ 25] = 0x0000030410; // PTW 0410 - this.MM[ 26] = 0x0000089429; // HALT 9429 - this.MM[ 27] = 0x4845535356; // LIT "HELLO" - this.MM[ 28] = 0x6656595344; // LIT "WORLD" + this.MM[ 20] = 0x0030090022; // SPO 22 + this.MM[ 21] = 0x0000009999; // HLT 9999 + this.MM[ 22] = 0x21648455353; // LIT R'HELL' + this.MM[ 23] = 0x25600665659; // LIT 'O WOR' + this.MM[ 24] = 0x25344000016; // LIT 'LD 'R + /************************************************* // Tom Sawyer's "Square Roots 100" (Babylonian or Newton's method): this.MM[ 100] = 0x640139; // CAD 139 this.MM[ 101] = 0x120138; // ST 138 diff --git a/software/BALGOL/BALGOL-Generator.bacg b/software/BALGOL/BALGOL-Generator.bacg index df4e36a..4174499 100644 --- a/software/BALGOL/BALGOL-Generator.bacg +++ b/software/BALGOL/BALGOL-Generator.bacg @@ -220,7 +220,7 @@ SEQ PLAC ADDR WORD LABEL OPCODE OPERAND ' IS A 4 210 0155 0 4410 40 2449 STA TBL+1/44 211 0156 0 4204 27 2451 DFL TBL+3/42,4 212 0157 0 0000 30 0134 BUN COMMENT - 213 0158 0 0000 10 2449 SETSCAN.1 CRD TBL+1 + 213 0158 0 0000 10 2449 SETSCAN.1 CAD TBL+1 214 0159 1 4410 40 1677 -STA IA/44 215 0160 0 0000 30 0514 BUN RETURN 216 diff --git a/webUI/B220.html b/webUI/B220.html index d849242..8a496d6 100644 --- a/webUI/B220.html +++ b/webUI/B220.html @@ -30,11 +30,7 @@ - - - + + + + + diff --git a/webUI/B220Common.css b/webUI/B220Common.css index 9381422..838fcc6 100644 --- a/webUI/B220Common.css +++ b/webUI/B220Common.css @@ -308,7 +308,7 @@ DIV.blackControlKnobBottomCaption { DIV.panelSurface { position: absolute; - background-color: #E4DDCD; /* was #D8C5BC; putty #EDEAE8; */ + background-color: #D8C5BC; /* was #E4DDCD; putty #EDEAE8; */ color: black} DIV.panelRegister { diff --git a/webUI/B220ConsoleKeyboard.html b/webUI/B220ConsoleKeyboard.html index 0a50dcb..0697a3a 100644 --- a/webUI/B220ConsoleKeyboard.html +++ b/webUI/B220ConsoleKeyboard.html @@ -33,11 +33,11 @@ class=keyboardBtn>C - - - diff --git a/webUI/B220ConsoleKeyboard.js b/webUI/B220ConsoleKeyboard.js index ab30a39..4383ba1 100644 --- a/webUI/B220ConsoleKeyboard.js +++ b/webUI/B220ConsoleKeyboard.js @@ -23,7 +23,7 @@ function B220ConsoleKeyboard(p) { this.window = null; // window object, null if not displayed this.enabled = false; // true if keyboard is active - this.boundKeypress = B220Processor.bindMethod(this, B220ConsoleKeyboard.prototype.keypress); + this.boundKeypress = B220Util.bindMethod(this, B220ConsoleKeyboard.prototype.keypress); this.boundButton_Click = B220Util.bindMethod(this, B220ConsoleKeyboard.prototype.button_Click); this.boundKeyboard_OnLoad = B220Util.bindMethod(this, B220ConsoleKeyboard.prototype.keyboardOnLoad); this.boundKeyboard_Unload = B220Util.bindMethod(this, B220ConsoleKeyboard.prototype.keyboardUnload); @@ -150,14 +150,22 @@ B220ConsoleKeyboard.prototype.keypress = function keypress(ev) { this.animateClick(this.$$("EBtn")); this.p.keyboardAction(-3); break; - case 0x0D: // Enter key = EXAM + case 0x58: case 0x78: // "X", "x" this.animateClick(this.$$("ExamBtn")); this.p.keyboardAction(-4); break; + case 0x0D: // Enter key = ENT + this.animateClick(this.$$("EntBtn")); + this.p.keyboardAction(-5); + break; + case 0x53: case 0x73: // "S", "s" + this.animateClick(this.$$("StepBtn")); + this.p.keyboardAction(-6); + break; case 0: // Firefox reports only graphic charCodes for keypress if (ev.keyCode == 0x0D) { // check keyCode instead - this.animateClick(this.$$("ExamBtn")); - this.p.keyboardAction(-4); + this.animateClick(this.$$("EntBtn")); + this.p.keyboardAction(-5); } break; } // switch c @@ -175,7 +183,7 @@ B220ConsoleKeyboard.prototype.keyboardOpen = function keyboardOpen() { if (!this.window) { this.window = window.open("../webUI/B220ConsoleKeyboard.html", this.mnemonic, - "location=no,scrollbars=no,resizable,width=" + w + ",height=" + h + + "resizable,width=" + w + ",height=" + h + ",left=" + (screen.availWidth - w) + ",top=" + (screen.availHeight - h)); this.window.addEventListener("load", this.boundKeyboard_OnLoad, false); } diff --git a/webUI/B220ConsolePrinter.css b/webUI/B220ConsolePrinter.css new file mode 100644 index 0000000..9dcadd1 --- /dev/null +++ b/webUI/B220ConsolePrinter.css @@ -0,0 +1,294 @@ +/*********************************************************************** +* retro-220/webUI B220PaperTapePunch.css +************************************************************************ +* Copyright (c) 2017, Paul Kimpel. +* Licensed under the MIT License, see +* http://www.opensource.org/licenses/mit-license.php +************************************************************************ +* Burroughs 220 Console Printer Unit. +************************************************************************ +* 2017-03-17 P.Kimpel +* Original version, from retro-205 D205ConsoleOutput.css. +***********************************************************************/ + +#PrinterBody { + height: 100%; + min-height: 100%; + overflow: hidden; + padding: 0} + +#Printer { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: 4px; + border-radius: 16px} + +#LineFeedBtn { + left: 16px; + top: 8px; + box-shadow: 3px 3px 2px #999} +#LineFeedBtnCaption { + left: 48px; + top: 18px} + +#CarriageReturnBtn { + right: 16px; + top: 8px; + box-shadow: 3px 3px 2px #999} +#CarriageReturnBtnCaption { + right: 48px; + top: 18px} + +#OpenPanelBtn { + position: absolute; + top: 8px; + left: calc(50% - 50px); + width: 100px} + +#ClosePanelBtn { + position: absolute; + top: 8px; + left: calc(50% - 50px); + width: 100px} + +#PrinterPlaten { + position: absolute; + left: 16px; + right: 16px; + top: 48px; + bottom: 16px; + min-height: 64px; + min-width: 120px; + overflow-x: hidden; + overflow-y: scroll; + color: black; + background-color: white; + padding: 4px; + border: 1px solid gray} + +#Paper { + margin-left: 0; + margin-right: 0; + margin-top: 2000px; + margin-bottom: 0; + padding: 0} + +#EndOfPaper { + display: block; + margin: 0; + padding: 0; + opacity: 0} +#EndOfPaper.hidden { + display: none} + +#FormatControlsDiv { + position: absolute; + overflow: visible; + display: none; + left: calc(50% - 312px); + top: 48px; + z-index: 10; + height: 216px; + width: 624px; + border-radius: 8px; + border: 2px solid black; + box-shadow: 3px 3px 2px #999; + color: black; + background-color: #E4DDCD} + +#RemoteKnob { + position: absolute; + left: 36px; + top: 60px} +#RemoteKnobRemoteCaption { + left: 36px; + top: 48px; + width: 36px; + text-align: left} +#RemoteKnobLocalCaption { + left: 62px; + top: 48px; + width: 38px; + text-align: right} + +#FormatKnob { + position: absolute; + right: 36px; + top: 60px} +#FormatKnobCaption { + right: 36px; + top: 128px; + width: 64px} +#FormatKnobSpaceCaption { + right: 88px; + top: 48px; + text-align: right} +#FormatKnobTabCaption { + right: 36px; + top: 48px; + width: 64px} +#FormatKnobCarRetCaption { + right: 12px; + top: 48px; + text-align: right} + +#ZeroSuppressSwitch { + position: absolute; + left: 134px; + top: 80px; + width: 24px} +#ZeroSuppressOn { + left: 122px; + top: 66px; + width: 48px} +#ZeroSuppressOff { + left: 122px; + top: 110px; + width: 48px} + +#MapMemorySwitch { + position: absolute; + left: 184px; + top: 80px; + width: 24px} +#MapMemoryOn { + left: 172px; + top: 66px; + width: 48px} +#MapMemoryNormal { + left: 172px; + top: 110px; + width: 48px} + +#Columns { + position: absolute; + right: 356px; + top: 80px; + width: 16px} +#ColumnsCaption { + right: 338px; + top: 66px; + width: 60px} + +#TabStops { + position: absolute; + right: 124px; + top: 80px; + width: 220px} +#TabStopsCaption { + right: 124px; + top: 66px; + width: 220px} + +#UnitSwitch0 { + position: absolute; + left: 134px; + top: 160px; + width: 24px} +#UnitSwitch0Caption { + left: 122px; + top: 146px; + width: 48px} + +#UnitSwitch1 { + position: absolute; + left: 170px; + top: 160px; + width: 24px} +#UnitSwitch1Caption { + left: 158px; + top: 146px; + width: 48px} + +#UnitSwitch2 { + position: absolute; + left: 206px; + top: 160px; + width: 24px} +#UnitSwitch2Caption { + left: 194px; + top: 146px; + width: 48px} + +#UnitSwitch3 { + position: absolute; + left: 242px; + top: 160px; + width: 24px} +#UnitSwitch3Caption { + left: 230px; + top: 146px; + width: 48px} + +#UnitSwitch4 { + position: absolute; + left: 278px; + top: 160px; + width: 24px} +#UnitSwitch4Caption { + left: 266px; + top: 146px; + width: 48px} + +#UnitSwitch5 { + position: absolute; + left: 314px; + top: 160px; + width: 24px} +#UnitSwitch5Caption { + left: 302px; + top: 146px; + width: 48px} + +#UnitSwitch6 { + position: absolute; + left: 350px; + top: 160px; + width: 24px} +#UnitSwitch6Caption { + left: 338px; + top: 146px; + width: 48px} + +#UnitSwitch7 { + position: absolute; + left: 386px; + top: 160px; + width: 24px} +#UnitSwitch7Caption { + left: 374px; + top: 146px; + width: 48px} + +#UnitSwitch8 { + position: absolute; + left: 422px; + top: 160px; + width: 24px} +#UnitSwitch8Caption { + left: 410px; + top: 146px; + width: 48px} + +#UnitSwitch9 { + position: absolute; + left: 458px; + top: 160px; + width: 24px} +#UnitSwitch9Caption { + left: 446px; + top: 146px; + width: 48px} + +#UnitSwitch10 { + position: absolute; + left: 494px; + top: 160px; + width: 24px} +#UnitSwitch10Caption { + left: 482px; + top: 146px; + width: 48px} diff --git a/webUI/B220ConsolePrinter.html b/webUI/B220ConsolePrinter.html new file mode 100644 index 0000000..d63703d --- /dev/null +++ b/webUI/B220ConsolePrinter.html @@ -0,0 +1,81 @@ + + + + +retro-220 Emulator Console Printer + + + + + + + + + + +
+
+
LINE FEED
+ + + +
CAR. RET.
+
+ +
+
 
+
 
+
+ +
+ + +
REMOTE
+
LOCAL
+ +
ZERO
SUPPRESS
+
NORMAL
+ +
MAP
MEMORY
+
NORMAL
+ +
SPACE
+
TAB
+
CAR.RET.
+
FORMAT
+ +
COLUMNS
+ + +
TAB STOPS
+ + +
SPO
+
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
0
+
+
+ + + \ No newline at end of file diff --git a/webUI/B220ConsolePrinter.js b/webUI/B220ConsolePrinter.js new file mode 100644 index 0000000..d5cf81b --- /dev/null +++ b/webUI/B220ConsolePrinter.js @@ -0,0 +1,566 @@ +/*********************************************************************** +* retro-220/webUI B220ConsolePrinter.js +************************************************************************ +* Copyright (c) 2017, Paul Kimpel. +* Licensed under the MIT License, see +* http://www.opensource.org/licenses/mit-license.php +************************************************************************ +* Burroughs 220 Console Printer and +* High-Speed Paper Tape Punch devices. +************************************************************************ +* 2017-03-17 P.Kimpel +* Original version, from retro-205 D205ConsoleOutput.js. +***********************************************************************/ +"use strict"; + +/**************************************/ +function B220ConsolePrinter(mnemonic, unitIndex, config) { + /* Constructor for the Console Printer object */ + var top = unitIndex*32; + var left = unitIndex*32; + + this.config = config; // System configuration object + this.mnemonic = mnemonic; // Unit mnemonic + this.unitIndex = unitIndex; // Unit index into console output units + this.outTimer = 0; // output setCallback() token + + this.columns = 72; // right-margin auto-return position + this.format = 0; // 0=space, 1=tab, 2=carriage-return + this.nextCharTime = 0; // next time a character can be printed + this.mapMemory = 0; // map-memory switch setting + this.unitMask = 0; // unit selection mask + this.unitSwitch = new Array(11); // unit selection switch objects + this.tabStop = []; // 0-relative tab stop positions + this.zeroSuppress = 0; // zero-suppression switch setting + + this.boundButton_Click = B220Util.bindMethod(this, B220ConsolePrinter.prototype.button_Click); + this.boundText_OnChange = B220Util.bindMethod(this, B220ConsolePrinter.prototype.text_OnChange); + this.boundFlipSwitch = B220Util.bindMethod(this, B220ConsolePrinter.prototype.flipSwitch); + this.boundReceiveSign = B220Util.bindMethod(this, B220ConsolePrinter.prototype.receiveSign); + this.boundReceiveChar = B220Util.bindMethod(this, B220ConsolePrinter.prototype.receiveChar); + + this.clear(); + + // Create the printer window and onload event + this.doc = null; + this.paper = null; + this.printerEOP = null; + this.printerLine = 0; + this.printerCol = 0; + this.window = window.open("../webUI/B220ConsolePrinter.html", mnemonic, + "location=no,scrollbars=no,resizable,width=640,height=300," + + "left=" + left + ",top=" + top); + this.window.addEventListener("load", B220Util.bindMethod(this, + B220ConsolePrinter.prototype.printerOnLoad)); +} + +/**************************************/ +B220ConsolePrinter.offSwitchImage = "./resources/ToggleDown.png"; +B220ConsolePrinter.onSwitchImage = "./resources/ToggleUp.png"; + +B220ConsolePrinter.charsPerSecond = 10; // Printer speed +B220ConsolePrinter.charPeriod = 1000/B220ConsolePrinter.charsPerSecond; + // Inter-character period, ms +B220ConsolePrinter.pageSize = 66; // lines/page for form-feed +B220ConsolePrinter.maxScrollLines = 15000; + // Maximum amount of paper scrollback + +B220ConsolePrinter.codeXlate = [ // translate internal B220 code to ANSI + " ", "?", " ", ".", "\u00A4", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 00-0F + "&", "?", "?", "$", "*", "\f", "\n", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 10-1F + "-", "/", "?", ",", "%", "?", "\t", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 20-2F + "?", "?", "?", "#", "@", "!", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 30-3F + "?", "A", "B", "C", "D", "E", "F", "G", "H", "I", "?", "?", "?", "?", "?", "?", // 40-4F + "?", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "?", "?", "?", "?", "?", "?", // 50-5F + "?", "?", "S", "T", "U", "V", "W", "X", "Y", "Z", "?", "?", "?", "?", "?", "?", // 60-6F + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 70-7F + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "?", "?", "?", "?", "?", "?", // 80-8F + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]; // 90-9F + + +/**************************************/ +B220ConsolePrinter.prototype.clear = function clear() { + /* Initializes (and if necessary, creates) the SPO unit state */ + + this.ready = false; // ready status + this.busy = false; // busy status + + this.eowAction = 0; // 1 => End-of-Word action needed + this.suppressLZ = 0; // 1 => currently suppressing leading zeroes +}; + +/**************************************/ +B220ConsolePrinter.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; +}; + +/**************************************/ +B220ConsolePrinter.prototype.$$ = function $$(e) { + return this.doc.getElementById(e); +}; + +/**************************************/ +B220ConsolePrinter.prototype.emptyPaper = function emptyPaper() { + /* Empties the printer output "paper" and initializes it for new output */ + + while (this.paper.firstChild) { + this.paper.removeChild(this.paper.firstChild); + } + this.paper.appendChild(this.doc.createTextNode("")); +}; + +/**************************************/ +B220ConsolePrinter.prototype.emptyLine = function emptyLine(text) { + /* Removes excess lines already output, then appends a new text node to the +
 element within the paper element. Note that "text" is an ANSI string */
+    var paper = this.paper;
+    var line = text || "";
+
+    while (paper.childNodes.length > B220ConsolePrinter.maxScrollLines) {
+        paper.removeChild(paper.firstChild);
+    }
+    paper.lastChild.nodeValue += "\n";     // newline
+    paper.appendChild(this.doc.createTextNode(line));
+    ++this.printerLine;
+    this.printerCol = line.length;
+    this.printerEOP.scrollIntoView();
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.printChar = function printChar(code) {
+    /* Outputs the character "code" to the device */
+    var c = B220ConsolePrinter.codeXlate[code];
+    var line;
+    var len;
+
+    if (c != "?") {                     // some 220 codes just don't print
+        line = this.paper.lastChild.nodeValue;
+        len = line.length;
+        if (len < 1) {
+            this.paper.lastChild.nodeValue = c;
+            this.printerCol = 1;
+        } else if (len < this.columns) {
+            this.paper.lastChild.nodeValue = line + c;
+            ++this.printerCol;
+        } else {
+             this.emptyLine(c);
+        }
+    }
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.printTab = function printTab() {
+    /* Simulates tabulation by outputting an appropriate number of spaces */
+    var tabCol;                         // tabulation column
+    var x;                              // scratch index
+
+    for (x=0; x this.printerCol) {
+            tabCol = this.tabStop[x];
+            break; // out of for loop
+        }
+    } // for x
+
+    if (this.columns < tabCol) {
+        this.emptyLine();                   // tab would overflow right margin
+    } else {
+        while (this.printerCol < tabCol) {
+            this.printChar(0x00);           // output a space
+        }
+    }
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.printFormFeed = function printFormFeed() {
+    /* Simulates a form feed by outputting an appropriate number of blank lines */
+
+    this.printerLine %= B220ConsolePrinter.pageSize;
+    while (this.printerLine < B220ConsolePrinter.pageSize) {
+        this.emptyLine();
+    }
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.resizeWindow = function resizeWindow(ev) {
+    /* Handles the window onresize event by scrolling the "paper" so it remains at the end */
+
+    this.printerEOP.scrollIntoView();
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.copyPaper = function copyPaper(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 text = this.paper.textContent;
+    var title = "B220 " + this.mnemonic + " Text Snapshot";
+    var win = window.open("./B220FramePaper.html", "TTY-Snapshot",
+            "scrollbars,resizable,width=500,height=500");
+
+    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.emptyPaper();
+    this.emptyLine();
+    ev.preventDefault();
+    ev.stopPropagation();
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.button_Click = function button_Click(ev) {
+    /* Handler for button clicks */
+
+    switch (ev.target.id) {
+    case "LineFeedBtn":
+    case "CarriageReturnBtn":
+        if (!this.ready) {
+            this.emptyLine();
+        }
+        break;
+    case "OpenPanelBtn":
+        ev.target.disabled = true;
+        this.$$("FormatControlsDiv").style.display = "block";
+        break;
+    case "ClosePanelBtn":
+        this.$$("OpenPanelBtn").disabled = false;
+        this.$$("FormatControlsDiv").style.display = "none";
+        break;
+    } // switch ev.target.id
+
+    ev.preventDefault();
+    ev.stopPropagation();
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.flipSwitch = function flipSwitch(ev) {
+    /* Handler for switch clicks */
+    var id = ev.target.id;
+    var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
+    var x;
+
+    switch (id) {
+    case "ZeroSuppressSwitch":
+        this.zeroSuppressSwitch.flip();
+        prefs.zeroSuppress = this.zeroSuppress = this.zeroSuppressSwitch.state;
+        break;
+    case "MapMemorySwitch":
+        this.mapMemorySwitch.flip();
+        prefs.mapMemory, this.mapMemory = this.mapMemorySwitch.state;
+        break;
+    case "RemoteKnob":
+        this.remoteKnob.step();
+        prefs.remote = this.remoteKnob.position;
+        this.ready = (this.remoteKnob.position != 0);
+        break;
+    case "FormatKnob":
+        this.formatKnob.step();
+        prefs.format = this.formatKnob.position;
+        this.format = this.formatKnob.position;
+        break;
+    default:
+        x = id.indexOf("UnitSwitch");
+        if (x == 0) {
+            x = parseInt(id.substring(10), 10);
+            if (!isNaN(x)) {
+                this.unitSwitch[x].flip();
+                this.unitMask ^= B220Processor.pow2[x];
+                prefs.unitMask = this.unitMask;
+            }
+        }
+        break;
+    }
+
+    this.config.putNode("ConsoleOutput.units", prefs, this.unitIndex);
+    ev.preventDefault();
+    ev.stopPropagation();
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.text_OnChange = function text_OnChange(ev) {
+    /* Handler for text onchange events */
+    var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
+    var text = ev.target.value;
+    var v;
+
+    switch (ev.target.id) {
+    case "Columns":
+        v = parseInt(text, 10);
+        if (!isNaN(v)) {
+            this.columns = v;
+            ev.target.value = text = v.toFixed();
+            prefs.columns = v;
+        }
+        break;
+    case "TabStops":
+        v = this.parseTabStops(prefs.tabs || "", this.window);
+        if (v !== null) {
+            this.tabStop = v;
+            ev.target.value = text = v.join(",");
+            prefs.tabs = text;
+        }
+        break;
+    } // switch ev.target.id
+
+    this.config.putNode("ConsoleOutput.units", prefs, this.unitIndex);
+    ev.preventDefault();
+    ev.stopPropagation();
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.parseTabStops = function parsetabStops(text, alertWin) {
+    /* Parses a comma-delimited list of 1-relative tab stops. If the list is parsed
+    successfully, returns an array of 0-relative tab stop positions; 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 tabStop = [];
+
+    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("Tab stop #" + (x+1) + " (\"" + cols[x] + "\") is not numeric");
+                    break; // out of for loop
+                } else if (col <= lastCol) {
+                    copacetic = false;
+                    alertWin.alert("Tab stop #" + (x+1) + " (\"" + col + "\") is out of sequence");
+                    break; // out of for loop
+                } else {
+                    lastCol = col;
+                    tabStop.push(col-1);
+                }
+            }
+        } // for x
+    }
+
+    return (copacetic ? tabStop : null);
+};
+
+/**************************************/
+B220ConsolePrinter.prototype.printerOnLoad = function printerOnLoad() {
+    /* Initializes the Teletype printer window and user interface */
+    var body;
+    var id;
+    var mask;
+    var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
+    var tabStop;
+    var x;
+
+    this.doc = this.window.document;
+    this.doc.title = "retro-220 Printer - " + this.mnemonic;
+    this.paper = this.$$("Paper");
+    this.printerEOP = this.$$("EndOfPaper");
+    this.emptyPaper();
+    this.emptyLine();
+
+    body = this.$$("FormatControlsDiv");
+    this.remoteKnob = new BlackControlKnob(body, null, null, "RemoteKnob",
+        prefs.remote, [20, -20]);
+    this.ready = (prefs.remote != 0);
+
+    this.zeroSuppressSwitch = new ToggleSwitch(body, null, null, "ZeroSuppressSwitch",
+            B220ConsolePrinter.offSwitchImage, B220ConsolePrinter.onSwitchImage);
+    this.zeroSuppressSwitch.set(prefs.zeroSuppress);
+    this.zeroSuppress = this.zeroSuppressSwitch.state;
+    this.mapMemorySwitch = new ToggleSwitch(body, null, null, "MapMemorySwitch",
+            B220ConsolePrinter.offSwitchImage, B220ConsolePrinter.onSwitchImage);
+    this.mapMemorySwitch.set(prefs.mapMemory);
+    this.mapMemory = this.mapMemorySwitch.state;
+
+    mask = 0x001;
+    this.unitMask = prefs.unitMask;
+    for (x=0; x
+
+
+
+Burroughs 220 Device Frame Paper Page
+
+
+
+
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/webUI/B220Manifest.appcache b/webUI/B220Manifest.appcache index 9c3e1ca..1fae711 100644 --- a/webUI/B220Manifest.appcache +++ b/webUI/B220Manifest.appcache @@ -1,5 +1,5 @@ CACHE MANIFEST -# retro-220 emulator 0.00c, 2017-03-12 12:15 +# retro-220 emulator 0.00d, 2017-04-29 12:35 CACHE: ../emulator/B220Processor.js B220.css @@ -21,8 +21,9 @@ B220Common.css B220ConsoleKeyboard.css B220ConsoleKeyboard.html B220ConsoleKeyboard.js -#B220ConsoleOutput.css -#B220ConsoleOutput.js +B220ConsolePrinter.css +B220ConsolePrinter.html +B220ConsolePrinter.js B220ControlConsole.css B220ControlConsole.html B220ControlConsole.js @@ -30,8 +31,7 @@ B220ControlConsole.js #B220DataFile.html #B220DataFile.js #B220DiagMonitor.html -#B220Flexowriter.html -#B220FramePaper.html +B220FramePaper.html #B220MagTapeControl.css #B220MagTapeControl.html #B220MagTapeControl.js @@ -40,7 +40,9 @@ B220ControlConsole.js #B220MagTapeDrive.js #B220MagTapeLoadPanel.html B220PanelUtil.js -#B220PaperTapePunch.html +B220PaperTapePunch.css +B220PaperTapePunch.html +B220PaperTapePunch.js #B220PaperTapeReader.html B220SetCallback.js B220SystemConfig.css diff --git a/webUI/B220PanelUtil.js b/webUI/B220PanelUtil.js index a42e198..5b860ce 100644 --- a/webUI/B220PanelUtil.js +++ b/webUI/B220PanelUtil.js @@ -133,6 +133,7 @@ function NeonLampBox(parent, x, y, id, caption) { this.lamp = new NeonLamp(this.element, 3, 3, id + "_Lamp"); this.button = document.createElement("div"); + this.button.id = id + "_LampBtn"; this.button.className = NeonLampBox.lampButtonClass; this.button.textContent = caption; this.element.appendChild(this.button); @@ -582,6 +583,7 @@ function BlackControlKnob(parent, x, y, id, initial, positions) { BlackControlKnob.topCaptionClass = "blackControlKnobTopCaption"; BlackControlKnob.bottomCaptionClass = "blackControlKnobBottomCaption"; BlackControlKnob.className = "blackControlKnob1"; +BlackControlKnob.canvasColor = "transparent"; BlackControlKnob.size = 64; // width/height in pixels /**************************************/ @@ -614,7 +616,7 @@ BlackControlKnob.prototype.set = function set(position) { dc.save(); dc.translate(halfSize+0.5, halfSize+0.5); // move origin to the center - dc.fillStyle = "#246"; // fill in the panel background (aids antialiasing) + dc.fillStyle = BlackControlKnob.canvasColor;// fill in the panel background (aids antialiasing) dc.fillRect(-halfSize, -halfSize, BlackControlKnob.size, BlackControlKnob.size); silverSkirt = dc.createRadialGradient(0, 0, halfSize, 0, 0, quarterSize); @@ -639,7 +641,7 @@ BlackControlKnob.prototype.set = function set(position) { dc.save(); // draw the knob indicator dc.rotate(this.positions[this.position]*degrees); dc.beginPath(); - dc.moveTo(0, -halfSize); + dc.moveTo(0, 1-halfSize); dc.lineTo(-quarterSize/4, -halfSize+quarterSize/2); dc.lineTo(quarterSize/4, -halfSize+quarterSize/2); dc.closePath(); @@ -678,10 +680,10 @@ BlackControlKnob.prototype.setCaption = function setCaption(caption, atBottom) { e = document.createElement("div"); if (atBottom) { this.bottomCaptionDiv = e; - e.className = blackControlKnob.bottomCaptionClass; + e.className = BlackControlKnob.bottomCaptionClass; } else { this.topCaptionDiv = e; - e.className = blackControlKnob.topCaptionClass; + e.className = BlackControlKnob.topCaptionClass; } e.appendChild(document.createTextNode(caption)); this.element.appendChild(e); diff --git a/webUI/B220PaperTapePunch.css b/webUI/B220PaperTapePunch.css new file mode 100644 index 0000000..53b7ec9 --- /dev/null +++ b/webUI/B220PaperTapePunch.css @@ -0,0 +1,94 @@ +/*********************************************************************** +* retro-220/webUI B220PaperTapePunch.css +************************************************************************ +* Copyright (c) 2017, Paul Kimpel. +* Licensed under the MIT License, see +* http://www.opensource.org/licenses/mit-license.php +************************************************************************ +* Burroughs 220 Paper Tape Punch Unit. +************************************************************************ +* 2017-04-28 P.Kimpel +* Original version, from retro-205 D205ConsoleOutput.css. +***********************************************************************/ + +#PunchBody { + height: 100%; + min-height: 100%; + overflow: hidden; + padding: 0} + +#PaperTapePunch { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: 4px; + border-radius: 8px} + +#PunchTape { + position: absolute; + left: 8px; + right: 8px; + top: 60px; + bottom: 8px; + min-height: 32px; + min-width: 120px; + overflow-x: hidden; + overflow-y: scroll; + color: black; + background-color: white; + padding: 4px; + border: 1px solid gray} + +#Paper { + margin-left: 0; + margin-right: 0; + margin-top: 0; + margin-bottom: 0; + padding: 0} + +#EndOfPaper { + display: block; + margin: 0; + padding: 0; + opacity: 0} +#EndOfPaper.hidden { + display: none} + +#RemoteSwitch { + position: absolute; + width: 24px; + left: 18px; + top: 12px} +#RemoteSwitchOn { + width: 36px; + left: 12px; + top: 6px} +#RemoteSwitchOff { + width: 36px; + left: 12px; + top: 42px} + +#ReadyLamp { + position: absolute; + width: 24px; + left: 56px; + top: 14px} +#ReadyLampCaption { + width: 36px; + left: 52px; + top: 6px} + +#UnitDesignateKnob { + position: absolute; + text-align: center; + width: 64px; + right: 8px; + top: 18px; + color: white; + background-color: #333} +#UnitDesignateKnobCaption { + width: 64px; + right: 8px; + top: 6px} diff --git a/webUI/B220PaperTapePunch.html b/webUI/B220PaperTapePunch.html new file mode 100644 index 0000000..d8d0e28 --- /dev/null +++ b/webUI/B220PaperTapePunch.html @@ -0,0 +1,58 @@ + + + + +retro-220 Emulator Paper Tape Punch + + + + + + + + + + +
+
REMOTE
+
LOCAL
+ +
READY
+ +
UNIT DESIGNATE
+ + +
+
 
+
 
+
+
+ + + \ No newline at end of file diff --git a/webUI/B220PaperTapePunch.js b/webUI/B220PaperTapePunch.js new file mode 100644 index 0000000..9da56b1 --- /dev/null +++ b/webUI/B220PaperTapePunch.js @@ -0,0 +1,324 @@ +/*********************************************************************** +* retro-220/webUI B220PaperTapePunch.js +************************************************************************ +* Copyright (c) 2017, Paul Kimpel. +* Licensed under the MIT License, see +* http://www.opensource.org/licenses/mit-license.php +************************************************************************ +* Burroughs 220 High-Speed Paper Tape Punch device. +************************************************************************ +* 2017-04-28 P.Kimpel +* Original version, from retro-205 D205ConsoleOutput.js. +***********************************************************************/ +"use strict"; + +/**************************************/ +function B220PaperTapePunch(mnemonic, unitIndex, config) { + /* Constructor for the Console Paper Tape Punch object */ + var top = unitIndex*32; + var left = unitIndex*32; + + this.config = config; // System configuration object + this.mnemonic = mnemonic; // Unit mnemonic + this.unitIndex = unitIndex; // Unit index into console output units + this.outTimer = 0; // output setCallback() token + + this.nextCharTime = 0; // next time a character can be punched + this.unitMask = 0; // unit selection mask + this.unitSwitch = new Array(11); // unit selection switch objects + this.tabStop = []; // 0-relative tab stop positions + + this.boundFlipSwitch = B220Util.bindMethod(this, B220PaperTapePunch.prototype.flipSwitch); + this.boundReceiveSign = B220Util.bindMethod(this, B220PaperTapePunch.prototype.receiveSign); + this.boundReceiveChar = B220Util.bindMethod(this, B220PaperTapePunch.prototype.receiveChar); + + this.clear(); + + // Create the punch window and onload event + this.doc = null; + this.punchTape = null; + this.punchEOP = null; + this.window = window.open("../webUI/B220PaperTapePunch.html", mnemonic, + "location=no,scrollbars=no,resizable,width=240,height=160," + + "left=" + left + ",top=" + top); + this.window.addEventListener("load", B220Util.bindMethod(this, + B220PaperTapePunch.prototype.punchOnLoad)); +} + +/**************************************/ +B220PaperTapePunch.offSwitchImage = "./resources/ToggleDown.png"; +B220PaperTapePunch.onSwitchImage = "./resources/ToggleUp.png"; + +B220PaperTapePunch.charsPerSecond = 60; // Punch speed, characters/second +B220PaperTapePunch.charPeriod = 1000/B220PaperTapePunch.charsPerSecond; + // Inter-character period, ms +B220PaperTapePunch.maxScrollLines = 45000; + // Maximum amount of punch word scrollback + +B220PaperTapePunch.codeXlate = [ // translate internal B220 code to ANSI + // Note that ANSI new-line sequences are used for end-of-word characters, + // so B220 carriage-return translates to "|". To avoide space-expansion of + // tab characters, they are translated to "~". + " ", "?", " ", ".", "\u00A4", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 00-0F + "&", "?", "?", "$", "*", "\f", "|", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 10-1F + "-", "/", "?", ",", "%", "?", "~", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 20-2F + "?", "?", "?", "#", "@", "!", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 30-3F + "?", "A", "B", "C", "D", "E", "F", "G", "H", "I", "?", "?", "?", "?", "?", "?", // 40-4F + "?", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "?", "?", "?", "?", "?", "?", // 50-5F + "?", "?", "S", "T", "U", "V", "W", "X", "Y", "Z", "?", "?", "?", "?", "?", "?", // 60-6F + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", // 70-7F + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "?", "?", "?", "?", "?", "?", // 80-8F + "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]; // 90-9F + + +/**************************************/ +B220PaperTapePunch.prototype.clear = function clear() { + /* Initializes (and if necessary, creates) the SPO unit state */ + + this.ready = false; // ready status + this.busy = false; // busy status +}; + +/**************************************/ +B220PaperTapePunch.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; +}; + +/**************************************/ +B220PaperTapePunch.prototype.$$ = function $$(e) { + return this.doc.getElementById(e); +}; + +/**************************************/ +B220PaperTapePunch.prototype.punchEmptyPaper = function punchEmptyPaper() { + /* Empties the punch output "paper" and initializes it for new output */ + + while (this.punchTape.firstChild) { + this.punchTape.removeChild(this.punchTape.firstChild); + } + this.punchTape.appendChild(this.doc.createTextNode("")); +}; + +/**************************************/ +B220PaperTapePunch.prototype.punchEmptyLine = function punchEmptyLine(text) { + /* Removes excess lines already output, then appends a new text node to the +
 element within the paper element. Note that "text" is an ANSI string */
+    var paper = this.punchTape;
+    var line = text || "";
+
+    while (paper.childNodes.length > B220PaperTapePunch.maxScrollLines) {
+        paper.removeChild(paper.firstChild);
+    }
+    paper.lastChild.nodeValue += "\n";     // newline
+    paper.appendChild(this.doc.createTextNode(line));
+};
+
+/**************************************/
+B220PaperTapePunch.prototype.punchChar = function punchChar(code) {
+    /* Outputs the character "code" to the device */
+    var c = B220PaperTapePunch.codeXlate[code];
+    var line;
+    var len;
+
+    if (c != "?") {                     // some 220 codes just don't print
+        line = this.punchTape.lastChild.nodeValue;
+        len = line.length;
+        if (len < 1) {
+            this.punchTape.lastChild.nodeValue = c;
+        } else {
+            this.punchTape.lastChild.nodeValue = line + c;
+        }
+    }
+};
+
+/**************************************/
+B220PaperTapePunch.prototype.resizeWindow = function resizeWindow(ev) {
+    /* Handles the window onresize event by scrolling the "tape" so it remains at the end */
+
+    this.punchEOP.scrollIntoView();
+};
+
+/**************************************/
+B220PaperTapePunch.prototype.punchCopyTape = function punchCopyTape(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 text = this.punchTape.textContent;
+    var title = "B220 " + this.mnemonic + " Text Snapshot";
+    var win = window.open("./B220FramePaper.html", "PaperTape-Snapshot",
+            "scrollbars,resizable,width=500,height=500");
+
+    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.punchEmptyPaper();
+    ev.preventDefault();
+    ev.stopPropagation();
+};
+
+/**************************************/
+B220PaperTapePunch.prototype.flipSwitch = function flipSwitch(ev) {
+    /* Handler for switch clicks */
+    var id = ev.target.id;
+    var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
+    var x;
+
+    switch (id) {
+    case "RemoteSwitch":
+        this.remoteSwitch.flip();
+        prefs.remote = this.remoteSwitch.state;
+        this.ready = (this.remoteSwitch.state != 0);
+        this.readyLamp.set(this.remoteSwitch.state);
+        break;
+    case "UnitDesignateKnob":
+        x = this.unitDesignateKnob.selectedIndex;
+        if (x < 0) {
+            x = this.unitDesignateKnob.length-1;
+            this.unitMask = 0;
+        } else {
+            this.unitMask = B220Processor.pow2[x];
+            prefs.unitMask = this.unitMask
+        }
+        break;
+    }
+
+    this.config.putNode("ConsoleOutput.units", prefs, this.unitIndex);
+    ev.preventDefault();
+    ev.stopPropagation();
+};
+
+/**************************************/
+B220PaperTapePunch.prototype.punchOnLoad = function punchOnLoad() {
+    /* Initializes the Paper Tape Punch window and user interface */
+    var body;
+    var id;
+    var mask;
+    var prefs = this.config.getNode("ConsoleOutput.units", this.unitIndex);
+    var x;
+
+    this.doc = this.window.document;
+    this.doc.title = "retro-220 Punch - " + this.mnemonic;
+    this.punchTape = this.$$("Paper");
+    this.punchEOP = this.$$("EndOfPaper");
+    this.punchEmptyPaper();
+
+    body = this.$$("PaperTapePunch")
+    this.remoteSwitch = new ToggleSwitch(body, null, null, "RemoteSwitch",
+            B220PaperTapePunch.offSwitchImage, B220PaperTapePunch.onSwitchImage);
+    this.remoteSwitch.set(prefs.remote);
+    this.ready = (this.remoteSwitch.state != 0);
+
+    this.readyLamp = new ColoredLamp(body, null, null, "ReadyLamp", "blueLamp lampCollar", "blueLit");
+    this.readyLamp.set(this.remoteSwitch.state);
+
+    this.unitDesignateKnob = this.$$("UnitDesignateKnob");
+    mask = 0x001;
+    this.unitMask = prefs.unitMask;
+    if (this.unitMask == 0) {
+        this.unitDesignateKnob.selectedIndex = this.unitDesignateKnob.length-1;
+    } else {
+        for (x=0; x
     
+
+ + + +
System Properties:
- + + Word Memory Size + +
Console Output:
+ +
+ + + + + + + + + + + + +
PositionTypeSPO12345678910FormatColsTabs
0 - - × 1000 Word Memory + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +   +
1 - - - + + + + + + + + + + + + + + + + + + + + + + + + +   + +   + +
2 + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +   + +
3 + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +   + +
4 + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +   + +
5 + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +   + +
6 + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +   + +
7 + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +   + +
8 + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +   + +
9 + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +   + +
10 + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + +  
Cardatron Unit Selection:
@@ -446,11 +910,6 @@ - - -
- - diff --git a/webUI/B220SystemConfig.js b/webUI/B220SystemConfig.js index d5114cd..249abec 100644 --- a/webUI/B220SystemConfig.js +++ b/webUI/B220SystemConfig.js @@ -58,23 +58,35 @@ B220SystemConfig.prototype.createConfigData = function createConfigData() { memorySize: 5000, // 11-digit words ControlConsole: { - hasSPO: true, - poSuppressSwitch: 0, - skipSwitch: 0, - audibleAlarmSwitch: 0, - outputKnob: 2, - breakpointKnob: 0, - inputKnob: 1}, + PCS1SW: 0, // Program Control Switches 1-0 + PCS2SW: 0, + PCS3SW: 0, + PCS4SW: 0, + PCS5SW: 0, + PCW6SW: 0, + PCS7SW: 0, + PCS8SW: 0, + PCS9SW: 0, + PCS0SW: 0, + SONSW: 0, // S-register on + SUNITSSW: 0, // S-register units + STOCSW: 0, // S-to-C stop + STOPSW: 0}, // S-to-P stop - Flexowriter: { - zeroSuppressSwitch: 0, - tabSpaceSwitch: 2, - groupingCountersSwitch: 0, - autoStopSwitch: 0, - powerSwitch: 1, - wordsKnob: 0, - linesKnob: 0, - groupsKnob: 0}, + 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: "NONE"}, + {type: "NONE"}, + {type: "NONE"}, + {type: "NONE"}, + {type: "NONE"}, + {type: "NONE"}, + {type: "NONE"}, + {type: "NONE"}, + {type: "NONE"}, + {type: "NONE"} + ]}, Cardatron: { hasCardatron: true, @@ -108,54 +120,6 @@ B220SystemConfig.prototype.createConfigData = function createConfigData() { }; this.flushHandler(); - - // Convert old Supervisory Panel prefs - s = localStorage.getItem("retro-205-SupervisoryPanel-Prefs"); - if (s) { - try { - prefs = JSON.parse(s); - } finally { - // nothing - } - - for (pref in prefs) { - this.configData.SupervisoryPanel[pref] = prefs[pref]; - } - this.flushHandler(); - localStorage.removeItem("retro-205-SupervisoryPanel-Prefs"); - } - - // Convert old Control Console prefs - s = localStorage.getItem("retro-205-ControlConsole-Prefs"); - if (s) { - try { - prefs = JSON.parse(s); - } finally { - // nothing - } - - for (pref in prefs) { - this.configData.ControlConsole[pref] = prefs[pref]; - } - this.flushHandler(); - localStorage.removeItem("retro-205-ControlConsole-Prefs"); - } - - // Convert old Flexowriter prefs - s = localStorage.getItem("retro-205-Flexowriter-Prefs"); - if (s) { - try { - prefs = JSON.parse(s); - } finally { - // nothing - } - - for (pref in prefs) { - this.configData.Flexowriter[pref] = prefs[pref]; - } - this.flushHandler(); - localStorage.removeItem("retro-205-Flexowriter-Prefs"); - } }; /**************************************/ @@ -306,14 +270,31 @@ B220SystemConfig.prototype.setListValue = function setListValue(id, value) { B220SystemConfig.prototype.loadConfigDialog = function loadConfigDialog() { /* Loads the configuration UI window with the settings from this.configData */ var cd = this.configData; // local configuration reference + var mask; // unit mask bits var prefix; // unit id prefix var unit; // unit configuration object var x; // unit index + var y; // secondary index - this.$$("SystemMemorySize").value = Math.floor(cd.memorySize/1000).toString(); + // System Properties + this.setListValue("SystemMemorySize", cd.memorySize.toString()); - // Console units - this.$$("SPO").checked = cd.ControlConsole.hasSPO; + // Console Output units + for (x=0; x<=10; ++x) { + unit = cd.ConsoleOutput.units[x]; + prefix = "ConsoleOut" + x; + this.setListValue(prefix + "Type", unit.type); + mask = 0x001; + this.$$(prefix + "_SPO").checked = (unit.unitMask & mask ? true : false); + for (y=1; y<=10; ++y) { + mask <<= 1; + this.$$(prefix + "_" + y).checked = (unit.unitMask & mask ? true : false); + } // for y + + this.setListValue(prefix + "Format", unit.format); + this.$$(prefix + "Columns").textContent = (unit.columns ? unit.columns : 72); + this.$$(prefix + "Tabs").textContent = (unit.tabs ? unit.tabs : ""); + } // for x // Cardatron units for (x=1; x<=7; ++x) { @@ -363,9 +344,11 @@ B220SystemConfig.prototype.saveConfigDialog = function saveConfigDialog() { the updated configuration to localStorage */ var cd = this.configData; // local configuration reference var e; // local element reference + var mask; // unit mask var prefix; // unit id prefix var unit; // unit configuration object var x; // unit index + var y; // secondary index function getNumber(id, caption, min, max) { var n; @@ -382,16 +365,36 @@ B220SystemConfig.prototype.saveConfigDialog = function saveConfigDialog() { return n; } - x = getNumber.call(this, "SystemMemorySize", "Memory Size", 1, 10); - if (isNaN(x)) { - return; - } else { - cd.memorySize = x*1000; - } + // System Properties - // Console units + e = this.$$("SystemMemorySize"); + x = parseInt(e.options[e.selectedIndex], 10); + cd.memorySize = (isNaN(x) ? 5000 : x); - cd.ControlConsole.hasSPO = this.$$("SPO").checked; + // Console Output units + + for (x=0; x<=10; ++x) { + unit = cd.ConsoleOutput.units[x]; + prefix = "ConsoleOut" + x; + e = this.$$(prefix + "Type"); + unit.type = (e.selectedIndex < 0 ? "NONE" : e.options[e.selectedIndex].value); + mask = 0x001; + unit.unitMask = 0; + if (this.$$(prefix + "_SPO").checked) { + unit.unitMask |= mask; + } + for (y=1; y<=10; ++y) { + mask <<= 1; + if (this.$$(prefix + "_" + y).checked) { + unit.unitMask |= mask; + } + } // for y + + e = this.$$(prefix + "Format"); + unit.format = (e.selectedIndex < 0 ? "NONE" : e.options[e.selectedIndex].value); + unit.columns = (unit.columns ? unit.columns : 72); + unit.tabs = (unit.tabs ? unit.tabs : "1,9,17,25,33,41,49,57,65,73"); + } // for x // Cardatron units @@ -472,6 +475,11 @@ B220SystemConfig.prototype.openConfigUI = function openConfigUI() { B220Util.bindMethod(this, this.saveConfigDialog)); this.$$("CancelBtn").addEventListener("click", B220Util.bindMethod(this, function(ev) {this.window.close()})); + this.$$("DefaultsBtn").addEventListener("click", + B220Util.bindMethod(this, function(ev) { + this.createConfigData(); + this.loadConfigDialog(); + })); this.window.addEventListener("unload", B220Util.bindMethod(this, this.closeConfigUI), false); this.loadConfigDialog(); @@ -479,7 +487,7 @@ B220SystemConfig.prototype.openConfigUI = function openConfigUI() { this.doc = null; this.window = window.open("../webUI/B220SystemConfig.html", this.configStorageName, - "location=no,scrollbars,resizable,width=640,height=800"); + "location=no,scrollbars,resizable,width=800,height=800"); this.window.moveTo(screen.availWidth-this.window.outerWidth-40, (screen.availHeight-this.window.outerHeight)/2); this.window.focus(); diff --git a/webUI/B220Util.js b/webUI/B220Util.js index 3f92bb1..fada3a6 100644 --- a/webUI/B220Util.js +++ b/webUI/B220Util.js @@ -44,31 +44,22 @@ B220Util.$$ = function $$(e) { /**************************************/ B220Util.hasClass = function hasClass(e, name) { /* returns true if element "e" has class "name" in its class list */ - var classes = e.className; - if (!e) { - return false; - } else if (classes == name) { - return true; - } else { - return (classes.search("\\b" + name + "\\b") >= 0); - } + return e.classList.contains(name); }; /**************************************/ B220Util.addClass = function addClass(e, name) { /* Adds a class "name" to the element "e"s class list */ - if (!B220Util.hasClass(e, name)) { - e.className += (" " + name); - } + e.classList.add(name); }; /**************************************/ B220Util.removeClass = function removeClass(e, name) { /* Removes the class "name" from the element "e"s class list */ - e.className = e.className.replace(new RegExp("\\b" + name + "\\b\\s*", "g"), ""); + e.classList.remove(name); }; /**************************************/